mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
USB/PHY patches for 5.2-rc1
Here is the big set of USB and PHY driver patches for 5.2-rc1 There is the usual set of: - USB gadget updates - PHY driver updates and additions - USB serial driver updates and fixes - typec updates and new chips supported - mtu3 driver updates - xhci driver updates - other tiny driver updates Nothing really interesting, just constant forward progress. All of these have been in linux-next for a while with no reported issues. The usb-gadget and usb-serial trees were merged a bit "late", but both of them had been in linux-next before they got merged here last Friday. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXNKuwg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymRUgCfa8Ri7KrCaBR5NHQcLhbdrX90ToQAmgNw7vpo fqt0XpNM0CSa9O/gOr79 =8HFh -----END PGP SIGNATURE----- Merge tag 'usb-5.2-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 5.2-rc1 There is the usual set of: - USB gadget updates - PHY driver updates and additions - USB serial driver updates and fixes - typec updates and new chips supported - mtu3 driver updates - xhci driver updates - other tiny driver updates Nothing really interesting, just constant forward progress. All of these have been in linux-next for a while with no reported issues. The usb-gadget and usb-serial trees were merged a bit "late", but both of them had been in linux-next before they got merged here last Friday" * tag 'usb-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (206 commits) USB: serial: f81232: implement break control USB: serial: f81232: add high baud rate support USB: serial: f81232: clear overrun flag USB: serial: f81232: fix interrupt worker not stop usb: dwc3: Rename DWC3_DCTL_LPM_ERRATA usb: dwc3: Fix default lpm_nyet_threshold value usb: dwc3: debug: Print GET_STATUS(device) tracepoint usb: dwc3: Do core validation early on probe usb: dwc3: gadget: Set lpm_capable usb: gadget: atmel: tie wake lock to running clock usb: gadget: atmel: support USB suspend usb: gadget: atmel_usba_udc: simplify setting of interrupt-enabled mask dwc2: gadget: Fix completed transfer size calculation in DDMA usb: dwc2: Set lpm mode parameters depend on HW configuration usb: dwc2: Fix channel disable flow usb: dwc2: Set actual frame number for completed ISOC transfer usb: gadget: do not use __constant_cpu_to_le16 usb: dwc2: gadget: Increase descriptors count for ISOC's usb: introduce usb_ep_type_string() function usb: dwc3: move synchronize_irq() out of the spinlock protected block ...
This commit is contained in:
commit
132d68d37d
6
Documentation/ABI/testing/sysfs-driver-ucsi-ccg
Normal file
6
Documentation/ABI/testing/sysfs-driver-ucsi-ccg
Normal file
@ -0,0 +1,6 @@
|
||||
What: /sys/bus/i2c/drivers/ucsi_ccg/.../do_flash
|
||||
Date: May 2019
|
||||
Contact: Ajay Gupta <ajayg@nvidia.com>
|
||||
Description:
|
||||
Tell the driver for Cypress CCGx Type-C controller to attempt
|
||||
firmware upgrade by writing [Yy1] to the file.
|
27
Documentation/ABI/testing/usb-uevent
Normal file
27
Documentation/ABI/testing/usb-uevent
Normal file
@ -0,0 +1,27 @@
|
||||
What: Raise a uevent when a USB Host Controller has died
|
||||
Date: 2019-04-17
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-usb@vger.kernel.org
|
||||
Description: When the USB Host Controller has entered a state where it is no
|
||||
longer functional a uevent will be raised. The uevent will
|
||||
contain ACTION=offline and ERROR=DEAD.
|
||||
|
||||
Here is an example taken using udevadm monitor -p:
|
||||
|
||||
KERNEL[130.428945] offline /devices/pci0000:00/0000:00:10.0/usb2 (usb)
|
||||
ACTION=offline
|
||||
BUSNUM=002
|
||||
DEVNAME=/dev/bus/usb/002/001
|
||||
DEVNUM=001
|
||||
DEVPATH=/devices/pci0000:00/0000:00:10.0/usb2
|
||||
DEVTYPE=usb_device
|
||||
DRIVER=usb
|
||||
ERROR=DEAD
|
||||
MAJOR=189
|
||||
MINOR=128
|
||||
PRODUCT=1d6b/2/414
|
||||
SEQNUM=2168
|
||||
SUBSYSTEM=usb
|
||||
TYPE=9/0/1
|
||||
|
||||
Users: chromium-os-dev@chromium.org
|
@ -0,0 +1,32 @@
|
||||
Broadcom Stingray USB PHY
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of the listed compatibles
|
||||
- "brcm,sr-usb-combo-phy" is combo PHY has two PHYs, one SS and one HS.
|
||||
- "brcm,sr-usb-hs-phy" is a single HS PHY.
|
||||
- reg: offset and length of the PHY blocks registers
|
||||
- #phy-cells:
|
||||
- Must be 1 for brcm,sr-usb-combo-phy as it expects one argument to indicate
|
||||
the PHY number of two PHYs. 0 for HS PHY and 1 for SS PHY.
|
||||
- Must be 0 for brcm,sr-usb-hs-phy.
|
||||
|
||||
Refer to phy/phy-bindings.txt for the generic PHY binding properties
|
||||
|
||||
Example:
|
||||
usbphy0: usb-phy@0 {
|
||||
compatible = "brcm,sr-usb-combo-phy";
|
||||
reg = <0x00000000 0x100>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
|
||||
usbphy1: usb-phy@10000 {
|
||||
compatible = "brcm,sr-usb-combo-phy";
|
||||
reg = <0x00010000 0x100>,
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
|
||||
usbphy2: usb-phy@20000 {
|
||||
compatible = "brcm,sr-usb-hs-phy";
|
||||
reg = <0x00020000 0x100>,
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -7,6 +7,9 @@ Required properties:
|
||||
- clocks: phandles to the clocks for each clock listed in clock-names
|
||||
- clock-names: must contain "phy"
|
||||
|
||||
Optional properties:
|
||||
- vbus-supply: A phandle to the regulator for USB VBUS.
|
||||
|
||||
Example:
|
||||
usb3_phy0: phy@381f0040 {
|
||||
compatible = "fsl,imx8mq-usb-phy";
|
||||
|
@ -0,0 +1,22 @@
|
||||
* Amlogic G12A USB2 PHY binding
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "amlogic,meson-g12a-usb2-phy"
|
||||
- reg: The base address and length of the registers
|
||||
- #phys-cells: must be 0 (see phy-bindings.txt in this directory)
|
||||
- clocks: a phandle to the clock of this PHY
|
||||
- clock-names: must be "xtal"
|
||||
- resets: a phandle to the reset line of this PHY
|
||||
- reset-names: must be "phy"
|
||||
- phy-supply: see phy-bindings.txt in this directory
|
||||
|
||||
Example:
|
||||
usb2_phy0: phy@36000 {
|
||||
compatible = "amlogic,g12a-usb2-phy";
|
||||
reg = <0x0 0x36000 0x0 0x2000>;
|
||||
clocks = <&xtal>;
|
||||
clock-names = "xtal";
|
||||
resets = <&reset RESET_USB_PHY21>;
|
||||
reset-names = "phy";
|
||||
#phy-cells = <0>;
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
* Amlogic G12A USB3 + PCIE Combo PHY binding
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "amlogic,meson-g12a-usb3-pcie-phy"
|
||||
- #phys-cells: must be 1. The cell number is used to select the phy mode
|
||||
as defined in <dt-bindings/phy/phy.h> between PHY_TYPE_USB3 and PHY_TYPE_PCIE
|
||||
- reg: The base address and length of the registers
|
||||
- clocks: a phandle to the 100MHz reference clock of this PHY
|
||||
- clock-names: must be "ref_clk"
|
||||
- resets: phandle to the reset lines for the PHY control
|
||||
- reset-names: must be "phy"
|
||||
|
||||
Example:
|
||||
usb3_pcie_phy: phy@46000 {
|
||||
compatible = "amlogic,g12a-usb3-pcie-phy";
|
||||
reg = <0x0 0x46000 0x0 0x2000>;
|
||||
clocks = <&clkc CLKID_PCIE_PLL>;
|
||||
clock-names = "ref_clk";
|
||||
resets = <&reset RESET_PCIE_PHY>;
|
||||
reset-names = "phy";
|
||||
#phy-cells = <1>;
|
||||
};
|
@ -36,11 +36,20 @@ Required properties:
|
||||
- Tegra124: "nvidia,tegra124-xusb-padctl"
|
||||
- Tegra132: "nvidia,tegra132-xusb-padctl", "nvidia,tegra124-xusb-padctl"
|
||||
- Tegra210: "nvidia,tegra210-xusb-padctl"
|
||||
- Tegra186: "nvidia,tegra186-xusb-padctl"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- resets: Must contain an entry for each entry in reset-names.
|
||||
- reset-names: Must include the following entries:
|
||||
- "padctl"
|
||||
|
||||
For Tegra186:
|
||||
- avdd-pll-erefeut-supply: UPHY brick and reference clock as well as UTMI PHY
|
||||
power supply. Must supply 1.8 V.
|
||||
- avdd-usb-supply: USB I/Os, VBUS, ID, REXT, D+/D- power supply. Must supply
|
||||
3.3 V.
|
||||
- vclamp-usb-supply: Bias rail for USB pad. Must supply 1.8 V.
|
||||
- vddio-hsic-supply: HSIC PHY power supply. Must supply 1.2 V.
|
||||
|
||||
|
||||
Pad nodes:
|
||||
==========
|
||||
|
26
Documentation/devicetree/bindings/phy/phy-hi3660-usb3.txt
Normal file
26
Documentation/devicetree/bindings/phy/phy-hi3660-usb3.txt
Normal file
@ -0,0 +1,26 @@
|
||||
Hisilicon hi3660 USB PHY
|
||||
-----------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "hisilicon,hi3660-usb-phy"
|
||||
- #phy-cells: must be 0
|
||||
- hisilicon,pericrg-syscon: phandle of syscon used to control phy.
|
||||
- hisilicon,pctrl-syscon: phandle of syscon used to control phy.
|
||||
- hisilicon,eye-diagram-param: parameter set for phy
|
||||
Refer to phy/phy-bindings.txt for the generic PHY binding properties
|
||||
|
||||
This is a subnode of usb3_otg_bc register node.
|
||||
|
||||
Example:
|
||||
usb3_otg_bc: usb3_otg_bc@ff200000 {
|
||||
compatible = "syscon", "simple-mfd";
|
||||
reg = <0x0 0xff200000 0x0 0x1000>;
|
||||
|
||||
usb-phy {
|
||||
compatible = "hisilicon,hi3660-usb-phy";
|
||||
#phy-cells = <0>;
|
||||
hisilicon,pericrg-syscon = <&crg_ctrl>;
|
||||
hisilicon,pctrl-syscon = <&pctrl>;
|
||||
hisilicon,eye-diagram-param = <0x22466e4>;
|
||||
};
|
||||
};
|
38
Documentation/devicetree/bindings/phy/phy-mtk-ufs.txt
Normal file
38
Documentation/devicetree/bindings/phy/phy-mtk-ufs.txt
Normal file
@ -0,0 +1,38 @@
|
||||
MediaTek Universal Flash Storage (UFS) M-PHY binding
|
||||
--------------------------------------------------------
|
||||
|
||||
UFS M-PHY nodes are defined to describe on-chip UFS M-PHY hardware macro.
|
||||
Each UFS M-PHY node should have its own node.
|
||||
|
||||
To bind UFS M-PHY with UFS host controller, the controller node should
|
||||
contain a phandle reference to UFS M-PHY node.
|
||||
|
||||
Required properties for UFS M-PHY nodes:
|
||||
- compatible : Compatible list, contains the following controller:
|
||||
"mediatek,mt8183-ufsphy" for ufs phy
|
||||
persent on MT81xx chipsets.
|
||||
- reg : Address and length of the UFS M-PHY register set.
|
||||
- #phy-cells : This property shall be set to 0.
|
||||
- clocks : List of phandle and clock specifier pairs.
|
||||
- clock-names : List of clock input name strings sorted in the same
|
||||
order as the clocks property. Following clocks are
|
||||
mandatory.
|
||||
"unipro": Unipro core control clock.
|
||||
"mp": M-PHY core control clock.
|
||||
|
||||
Example:
|
||||
|
||||
ufsphy: phy@11fa0000 {
|
||||
compatible = "mediatek,mt8183-ufsphy";
|
||||
reg = <0 0x11fa0000 0 0xc000>;
|
||||
#phy-cells = <0>;
|
||||
|
||||
clocks = <&infracfg_ao INFRACFG_AO_UNIPRO_SCK_CG>,
|
||||
<&infracfg_ao INFRACFG_AO_UFS_MP_SAP_BCLK_CG>;
|
||||
clock-names = "unipro", "mp";
|
||||
};
|
||||
|
||||
ufshci@11270000 {
|
||||
...
|
||||
phys = <&ufsphy>;
|
||||
};
|
@ -11,6 +11,7 @@ Required properties:
|
||||
"qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996,
|
||||
"qcom,msm8998-qmp-usb3-phy" for USB3 QMP V3 phy on msm8998,
|
||||
"qcom,msm8998-qmp-ufs-phy" for UFS QMP phy on msm8998,
|
||||
"qcom,msm8998-qmp-pcie-phy" for PCIe QMP phy on msm8998,
|
||||
"qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845,
|
||||
"qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845,
|
||||
"qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845.
|
||||
@ -48,6 +49,8 @@ Required properties:
|
||||
"aux", "cfg_ahb", "ref".
|
||||
For "qcom,msm8998-qmp-ufs-phy" must contain:
|
||||
"ref", "ref_aux".
|
||||
For "qcom,msm8998-qmp-pcie-phy" must contain:
|
||||
"aux", "cfg_ahb", "ref".
|
||||
For "qcom,sdm845-qmp-usb3-phy" must contain:
|
||||
"aux", "cfg_ahb", "ref", "com_aux".
|
||||
For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
|
||||
@ -59,7 +62,8 @@ Required properties:
|
||||
one for each entry in reset-names.
|
||||
- reset-names: "phy" for reset of phy block,
|
||||
"common" for phy common block reset,
|
||||
"cfg" for phy's ahb cfg block reset.
|
||||
"cfg" for phy's ahb cfg block reset,
|
||||
"ufsphy" for the PHY reset in the UFS controller.
|
||||
|
||||
For "qcom,ipq8074-qmp-pcie-phy" must contain:
|
||||
"phy", "common".
|
||||
@ -69,12 +73,16 @@ Required properties:
|
||||
"phy", "common".
|
||||
For "qcom,msm8998-qmp-usb3-phy" must contain
|
||||
"phy", "common".
|
||||
For "qcom,msm8998-qmp-ufs-phy": no resets are listed.
|
||||
For "qcom,msm8998-qmp-ufs-phy": must contain:
|
||||
"ufsphy".
|
||||
For "qcom,msm8998-qmp-pcie-phy" must contain:
|
||||
"phy", "common".
|
||||
For "qcom,sdm845-qmp-usb3-phy" must contain:
|
||||
"phy", "common".
|
||||
For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
|
||||
"phy", "common".
|
||||
For "qcom,sdm845-qmp-ufs-phy": no resets are listed.
|
||||
For "qcom,sdm845-qmp-ufs-phy": must contain:
|
||||
"ufsphy".
|
||||
|
||||
- vdda-phy-supply: Phandle to a regulator supply to PHY core block.
|
||||
- vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
|
||||
|
@ -7,6 +7,7 @@ Required properties:
|
||||
- compatible: "renesas,usb-phy-r8a7743" if the device is a part of R8A7743 SoC.
|
||||
"renesas,usb-phy-r8a7744" if the device is a part of R8A7744 SoC.
|
||||
"renesas,usb-phy-r8a7745" if the device is a part of R8A7745 SoC.
|
||||
"renesas,usb-phy-r8a77470" if the device is a part of R8A77470 SoC.
|
||||
"renesas,usb-phy-r8a7790" if the device is a part of R8A7790 SoC.
|
||||
"renesas,usb-phy-r8a7791" if the device is a part of R8A7791 SoC.
|
||||
"renesas,usb-phy-r8a7794" if the device is a part of R8A7794 SoC.
|
||||
@ -30,7 +31,7 @@ channels. These subnodes must contain the following properties:
|
||||
- #phy-cells: see phy-bindings.txt in the same directory, must be <1>.
|
||||
|
||||
The phandle's argument in the PHY specifier is the USB controller selector for
|
||||
the USB channel; see the selector meanings below:
|
||||
the USB channel other than r8a77470 SoC; see the selector meanings below:
|
||||
|
||||
+-----------+---------------+---------------+
|
||||
|\ Selector | | |
|
||||
@ -41,6 +42,16 @@ the USB channel; see the selector meanings below:
|
||||
| 2 | PCI EHCI/OHCI | xHCI |
|
||||
+-----------+---------------+---------------+
|
||||
|
||||
For r8a77470 SoC;see the selector meaning below:
|
||||
|
||||
+-----------+---------------+---------------+
|
||||
|\ Selector | | |
|
||||
+ --------- + 0 | 1 |
|
||||
| Channel \| | |
|
||||
+-----------+---------------+---------------+
|
||||
| 0 | EHCI/OHCI | HS-USB |
|
||||
+-----------+---------------+---------------+
|
||||
|
||||
Example (Lager board):
|
||||
|
||||
usb-phy@e6590100 {
|
||||
@ -48,15 +59,53 @@ Example (Lager board):
|
||||
reg = <0 0xe6590100 0 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&mstp7_clks R8A7790_CLK_HSUSB>;
|
||||
clocks = <&cpg CPG_MOD 704>;
|
||||
clock-names = "usbhs";
|
||||
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 704>;
|
||||
|
||||
usb-channel@0 {
|
||||
usb0: usb-channel@0 {
|
||||
reg = <0>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
usb-channel@2 {
|
||||
usb2: usb-channel@2 {
|
||||
reg = <2>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
Example (iWave RZ/G1C sbc):
|
||||
|
||||
usbphy0: usb-phy0@e6590100 {
|
||||
compatible = "renesas,usb-phy-r8a77470",
|
||||
"renesas,rcar-gen2-usb-phy";
|
||||
reg = <0 0xe6590100 0 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&cpg CPG_MOD 704>;
|
||||
clock-names = "usbhs";
|
||||
power-domains = <&sysc R8A77470_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 704>;
|
||||
|
||||
usb0: usb-channel@0 {
|
||||
reg = <0>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
usbphy1: usb-phy@e6598100 {
|
||||
compatible = "renesas,usb-phy-r8a77470",
|
||||
"renesas,rcar-gen2-usb-phy";
|
||||
reg = <0 0xe6598100 0 0x100>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&cpg CPG_MOD 706>;
|
||||
clock-names = "usbhs";
|
||||
power-domains = <&sysc R8A77470_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 706>;
|
||||
|
||||
usb1: usb-channel@0 {
|
||||
reg = <0>;
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
@ -1,10 +1,12 @@
|
||||
* Renesas R-Car generation 3 USB 2.0 PHY
|
||||
|
||||
This file provides information on what the device node for the R-Car generation
|
||||
3 and RZ/G2 USB 2.0 PHY contain.
|
||||
3, RZ/G1C and RZ/G2 USB 2.0 PHY contain.
|
||||
|
||||
Required properties:
|
||||
- compatible: "renesas,usb2-phy-r8a774a1" if the device is a part of an R8A774A1
|
||||
- compatible: "renesas,usb2-phy-r8a77470" if the device is a part of an R8A77470
|
||||
SoC.
|
||||
"renesas,usb2-phy-r8a774a1" if the device is a part of an R8A774A1
|
||||
SoC.
|
||||
"renesas,usb2-phy-r8a774c0" if the device is a part of an R8A774C0
|
||||
SoC.
|
||||
@ -27,7 +29,13 @@ Required properties:
|
||||
|
||||
- reg: offset and length of the partial USB 2.0 Host register block.
|
||||
- clocks: clock phandle and specifier pair(s).
|
||||
- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
|
||||
- #phy-cells: see phy-bindings.txt in the same directory, must be <1> (and
|
||||
using <0> is deprecated).
|
||||
|
||||
The phandle's argument in the PHY specifier is the INT_STATUS bit of controller:
|
||||
- 1 = USBH_INTA (OHCI)
|
||||
- 2 = USBH_INTB (EHCI)
|
||||
- 3 = UCOM_INT (OTG and BC)
|
||||
|
||||
Optional properties:
|
||||
To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
|
||||
|
@ -7,12 +7,15 @@ Required properties:
|
||||
- reg: PHY register address offset and length in "general
|
||||
register files"
|
||||
|
||||
Optional clocks using the clock bindings (see ../clock/clock-bindings.txt),
|
||||
specified by name:
|
||||
Optional properties:
|
||||
- clock-names: Should contain "emmcclk". Although this is listed as optional
|
||||
(because most boards can get basic functionality without having
|
||||
access to it), it is strongly suggested.
|
||||
See ../clock/clock-bindings.txt for details.
|
||||
- clocks: Should have a phandle to the card clock exported by the SDHCI driver.
|
||||
- drive-impedance-ohm: Specifies the drive impedance in Ohm.
|
||||
Possible values are 33, 40, 50, 66 and 100.
|
||||
If not set, the default value of 50 will be applied.
|
||||
|
||||
Example:
|
||||
|
||||
@ -29,6 +32,7 @@ grf: syscon@ff770000 {
|
||||
reg = <0xf780 0x20>;
|
||||
clocks = <&sdhci>;
|
||||
clock-names = "emmcclk";
|
||||
drive-impedance-ohm = <50>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,82 @@
|
||||
TI AM654 SERDES
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,phy-am654-serdes"
|
||||
- reg : Address and length of the register set for the device.
|
||||
- #phy-cells: determine the number of cells that should be given in the
|
||||
phandle while referencing this phy. Should be "2". The 1st cell
|
||||
corresponds to the phy type (should be one of the types specified in
|
||||
include/dt-bindings/phy/phy.h) and the 2nd cell should be the serdes
|
||||
lane function.
|
||||
If SERDES0 is referenced 2nd cell should be:
|
||||
0 - USB3
|
||||
1 - PCIe0 Lane0
|
||||
2 - ICSS2 SGMII Lane0
|
||||
If SERDES1 is referenced 2nd cell should be:
|
||||
0 - PCIe1 Lane0
|
||||
1 - PCIe0 Lane1
|
||||
2 - ICSS2 SGMII Lane1
|
||||
- power-domains: As documented by the generic PM domain bindings in
|
||||
Documentation/devicetree/bindings/power/power_domain.txt.
|
||||
- clocks: List of clock-specifiers representing the input to the SERDES.
|
||||
Should have 3 items representing the left input clock, external
|
||||
reference clock and right input clock in that order.
|
||||
- clock-output-names: List of clock names for each of the clock outputs of
|
||||
SERDES. Should have 3 items for CMU reference clock,
|
||||
left output clock and right output clock in that order.
|
||||
- assigned-clocks: As defined in
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- assigned-clock-parents: As defined in
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- #clock-cells: Should be <1> to choose between the 3 output clocks.
|
||||
Defined in Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
The following macros are defined in dt-bindings/phy/phy-am654-serdes.h
|
||||
for selecting the correct reference clock. This can be used while
|
||||
specifying the clocks created by SERDES.
|
||||
=> AM654_SERDES_CMU_REFCLK
|
||||
=> AM654_SERDES_LO_REFCLK
|
||||
=> AM654_SERDES_RO_REFCLK
|
||||
|
||||
- mux-controls: Phandle to the multiplexer that is used to select the lane
|
||||
function. See #phy-cells above to see the multiplex values.
|
||||
|
||||
Example:
|
||||
|
||||
Example for SERDES0 is given below. It has 3 clock inputs;
|
||||
left input reference clock as indicated by <&k3_clks 153 4>, external
|
||||
reference clock as indicated by <&k3_clks 153 1> and right input
|
||||
reference clock as indicated by <&serdes1 AM654_SERDES_LO_REFCLK>. (The
|
||||
right input of SERDES0 is connected to the left output of SERDES1).
|
||||
|
||||
SERDES0 registers 3 clock outputs as indicated in clock-output-names. The
|
||||
first refers to the CMU reference clock, second refers to the left output
|
||||
reference clock and the third refers to the right output reference clock.
|
||||
|
||||
The assigned-clocks and assigned-clock-parents is used here to set the
|
||||
parent of left input reference clock to MAINHSDIV_CLKOUT4 and parent of
|
||||
CMU reference clock to left input reference clock.
|
||||
|
||||
serdes0: serdes@900000 {
|
||||
compatible = "ti,phy-am654-serdes";
|
||||
reg = <0x0 0x900000 0x0 0x2000>;
|
||||
reg-names = "serdes";
|
||||
#phy-cells = <2>;
|
||||
power-domains = <&k3_pds 153>;
|
||||
clocks = <&k3_clks 153 4>, <&k3_clks 153 1>,
|
||||
<&serdes1 AM654_SERDES_LO_REFCLK>;
|
||||
clock-output-names = "serdes0_cmu_refclk", "serdes0_lo_refclk",
|
||||
"serdes0_ro_refclk";
|
||||
assigned-clocks = <&k3_clks 153 4>, <&serdes0 AM654_SERDES_CMU_REFCLK>;
|
||||
assigned-clock-parents = <&k3_clks 153 8>, <&k3_clks 153 4>;
|
||||
ti,serdes-clk = <&serdes0_clk>;
|
||||
mux-controls = <&serdes_mux 0>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
Example for PCIe consumer node using the SERDES PHY specifier is given below.
|
||||
&pcie0_rc {
|
||||
num-lanes = <2>;
|
||||
phys = <&serdes0 PHY_TYPE_PCIE 1>, <&serdes1 PHY_TYPE_PCIE 1>;
|
||||
phy-names = "pcie-phy0", "pcie-phy1";
|
||||
};
|
@ -29,6 +29,7 @@ Optional properties:
|
||||
- vdda-pll-max-microamp : specifies max. load that can be drawn from pll supply
|
||||
- vddp-ref-clk-supply : phandle to UFS device ref_clk pad power supply
|
||||
- vddp-ref-clk-max-microamp : specifies max. load that can be drawn from this supply
|
||||
- resets : specifies the PHY reset in the UFS controller
|
||||
|
||||
Example:
|
||||
|
||||
@ -51,9 +52,11 @@ Example:
|
||||
<&clock_gcc clk_ufs_phy_ldo>,
|
||||
<&clock_gcc clk_gcc_ufs_tx_cfg_clk>,
|
||||
<&clock_gcc clk_gcc_ufs_rx_cfg_clk>;
|
||||
resets = <&ufshc 0>;
|
||||
};
|
||||
|
||||
ufshc@fc598000 {
|
||||
ufshc: ufshc@fc598000 {
|
||||
#reset-cells = <1>;
|
||||
...
|
||||
phys = <&ufsphy1>;
|
||||
phy-names = "ufsphy";
|
||||
|
@ -50,6 +50,8 @@ Optional properties:
|
||||
-lanes-per-direction : number of lanes available per direction - either 1 or 2.
|
||||
Note that it is assume same number of lanes is used both
|
||||
directions at once. If not specified, default is 2 lanes per direction.
|
||||
- #reset-cells : Must be <1> for Qualcomm UFS controllers that expose
|
||||
PHY reset from the UFS controller.
|
||||
- resets : reset node register
|
||||
- reset-names : describe reset node register, the "rst" corresponds to reset the whole UFS IP.
|
||||
|
||||
@ -79,4 +81,5 @@ Example:
|
||||
reset-names = "rst";
|
||||
phys = <&ufsphy1>;
|
||||
phy-names = "ufsphy";
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
@ -40,3 +40,91 @@ Example device nodes:
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
};
|
||||
};
|
||||
|
||||
Amlogic Meson G12A DWC3 USB SoC Controller Glue
|
||||
|
||||
The Amlogic G12A embeds a DWC3 USB IP Core configured for USB2 and USB3
|
||||
in host-only mode, and a DWC2 IP Core configured for USB2 peripheral mode
|
||||
only.
|
||||
|
||||
A glue connects the DWC3 core to USB2 PHYs and optionnaly to an USB3 PHY.
|
||||
|
||||
One of the USB2 PHY can be re-routed in peripheral mode to a DWC2 USB IP.
|
||||
|
||||
The DWC3 Glue controls the PHY routing and power, an interrupt line is
|
||||
connected to the Glue to serve as OTG ID change detection.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "amlogic,meson-g12a-usb-ctrl"
|
||||
- clocks: a handle for the "USB" clock
|
||||
- resets: a handle for the shared "USB" reset line
|
||||
- reg: The base address and length of the registers
|
||||
- interrupts: the interrupt specifier for the OTG detection
|
||||
- phys: handle to used PHYs on the system
|
||||
- a <0> phandle can be used if a PHY is not used
|
||||
- phy-names: names of the used PHYs on the system :
|
||||
- "usb2-phy0" for USB2 PHY0 if USBHOST_A port is used
|
||||
- "usb2-phy1" for USB2 PHY1 if USBOTG_B port is used
|
||||
- "usb3-phy0" for USB3 PHY if USB3_0 is used
|
||||
- dr_mode: should be "host", "peripheral", or "otg" depending on
|
||||
the usage and configuration of the OTG Capable port.
|
||||
- "host" and "peripheral" means a fixed Host or Device only connection
|
||||
- "otg" means the port can be used as both Host or Device and
|
||||
be switched automatically using the OTG ID pin.
|
||||
|
||||
Optional properties:
|
||||
- vbus-supply: should be a phandle to the regulator controlling the VBUS
|
||||
power supply when used in OTG switchable mode
|
||||
|
||||
Required child nodes:
|
||||
|
||||
A child node must exist to represent the core DWC3 IP block. The name of
|
||||
the node is not important. The content of the node is defined in dwc3.txt.
|
||||
|
||||
A child node must exist to represent the core DWC2 IP block. The name of
|
||||
the node is not important. The content of the node is defined in dwc2.txt.
|
||||
|
||||
PHY documentation is provided in the following places:
|
||||
- Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt
|
||||
- Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt
|
||||
|
||||
Example device nodes:
|
||||
usb: usb@ffe09000 {
|
||||
compatible = "amlogic,meson-g12a-usb-ctrl";
|
||||
reg = <0x0 0xffe09000 0x0 0xa0>;
|
||||
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
clocks = <&clkc CLKID_USB>;
|
||||
resets = <&reset RESET_USB>;
|
||||
|
||||
dr_mode = "otg";
|
||||
|
||||
phys = <&usb2_phy0>, <&usb2_phy1>,
|
||||
<&usb3_pcie_phy PHY_TYPE_USB3>;
|
||||
phy-names = "usb2-phy0", "usb2-phy1", "usb3-phy0";
|
||||
|
||||
dwc2: usb@ff400000 {
|
||||
compatible = "amlogic,meson-g12a-usb", "snps,dwc2";
|
||||
reg = <0x0 0xff400000 0x0 0x40000>;
|
||||
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clkc CLKID_USB1_DDR_BRIDGE>;
|
||||
clock-names = "ddr";
|
||||
phys = <&usb2_phy1>;
|
||||
dr_mode = "peripheral";
|
||||
g-rx-fifo-size = <192>;
|
||||
g-np-tx-fifo-size = <128>;
|
||||
g-tx-fifo-size = <128 128 16 16 16>;
|
||||
};
|
||||
|
||||
dwc3: usb@ff500000 {
|
||||
compatible = "snps,dwc3";
|
||||
reg = <0x0 0xff500000 0x0 0x100000>;
|
||||
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
|
||||
dr_mode = "host";
|
||||
snps,dis_u2_susphy_quirk;
|
||||
snps,quirk-frame-length-adjustment;
|
||||
};
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ Required properties:
|
||||
- "amlogic,meson8-usb": The DWC2 USB controller instance in Amlogic Meson8 SoCs;
|
||||
- "amlogic,meson8b-usb": The DWC2 USB controller instance in Amlogic Meson8b SoCs;
|
||||
- "amlogic,meson-gxbb-usb": The DWC2 USB controller instance in Amlogic S905 SoCs;
|
||||
- "amlogic,meson-g12a-usb": The DWC2 USB controller instance in Amlogic G12A SoCs;
|
||||
- "amcc,dwc-otg": The DWC2 USB controller instance in AMCC Canyonlands 460EX SoCs;
|
||||
- snps,dwc2: A generic DWC2 USB controller with default parameters.
|
||||
- "st,stm32f4x9-fsotg": The DWC2 USB FS/HS controller instance in STM32F4x9 SoCs
|
||||
@ -31,12 +32,18 @@ Refer to clk/clock-bindings.txt for generic clock consumer properties
|
||||
Optional properties:
|
||||
- phys: phy provider specifier
|
||||
- phy-names: shall be "usb2-phy"
|
||||
- vbus-supply: reference to the VBUS regulator. Depending on the current mode
|
||||
this is enabled (in "host" mode") or disabled (in "peripheral" mode). The
|
||||
regulator is updated if the controller is configured in "otg" mode and the
|
||||
status changes between "host" and "peripheral".
|
||||
Refer to phy/phy-bindings.txt for generic phy consumer properties
|
||||
- dr_mode: shall be one of "host", "peripheral" and "otg"
|
||||
Refer to usb/generic.txt
|
||||
- g-rx-fifo-size: size of rx fifo size in gadget mode.
|
||||
- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode.
|
||||
- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode.
|
||||
- snps,reset-phy-on-wake: If present indicates that we need to reset the PHY when
|
||||
we detect a wakeup. This is due to a hardware errata.
|
||||
|
||||
Deprecated properties:
|
||||
- g-use-dma: gadget DMA mode is automatically detected
|
||||
|
95
Documentation/devicetree/bindings/usb/generic-ehci.yaml
Normal file
95
Documentation/devicetree/bindings/usb/generic-ehci.yaml
Normal file
@ -0,0 +1,95 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/generic-ehci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: USB EHCI Controller Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "usb-hcd.yaml"
|
||||
|
||||
maintainers:
|
||||
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: generic-ehci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
description: |
|
||||
In case the Renesas R-Car Gen3 SoCs:
|
||||
- if a host only channel: first clock should be host.
|
||||
- if a USB DRD channel: first clock should be host and second
|
||||
one should be peripheral
|
||||
|
||||
big-endian:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set this flag for HCDs with big endian descriptors and big
|
||||
endian registers.
|
||||
|
||||
big-endian-desc:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set this flag for HCDs with big endian descriptors.
|
||||
|
||||
big-endian-regs:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set this flag for HCDs with big endian registers.
|
||||
|
||||
has-transaction-translator:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set this flag if EHCI has a Transaction Translator built into
|
||||
the root hub.
|
||||
|
||||
needs-reset-on-resume:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set this flag to force EHCI reset after resume.
|
||||
|
||||
phys: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
ehci@e0000300 {
|
||||
compatible = "ibm,usb-ehci-440epx", "generic-ehci";
|
||||
interrupt-parent = <&UIC0>;
|
||||
interrupts = <0x1a 4>;
|
||||
reg = <0 0xe0000300 90 0 0xe0000390 70>;
|
||||
big-endian;
|
||||
};
|
||||
|
||||
- |
|
||||
ehci0: usb@1c14000 {
|
||||
compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
|
||||
reg = <0x01c14000 0x100>;
|
||||
interrupts = <39>;
|
||||
clocks = <&ahb_gates 1>;
|
||||
phys = <&usbphy 1>;
|
||||
phy-names = "usb";
|
||||
};
|
||||
|
||||
...
|
89
Documentation/devicetree/bindings/usb/generic-ohci.yaml
Normal file
89
Documentation/devicetree/bindings/usb/generic-ohci.yaml
Normal file
@ -0,0 +1,89 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/generic-ohci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: USB OHCI Controller Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "usb-hcd.yaml"
|
||||
|
||||
maintainers:
|
||||
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: generic-ohci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
description: |
|
||||
In case the Renesas R-Car Gen3 SoCs:
|
||||
- if a host only channel: first clock should be host.
|
||||
- if a USB DRD channel: first clock should be host and second
|
||||
one should be peripheral
|
||||
|
||||
big-endian:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set this flag for HCDs with big endian descriptors and big
|
||||
endian registers.
|
||||
|
||||
big-endian-desc:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set this flag for HCDs with big endian descriptors.
|
||||
|
||||
big-endian-regs:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set this flag for HCDs with big endian registers.
|
||||
|
||||
remote-wakeup-connected:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Remote wakeup is wired on the platform.
|
||||
|
||||
no-big-frame-no:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Set if frame_no lives in bits [15:0] of HCCA
|
||||
|
||||
num-ports:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Overrides the detected port count
|
||||
|
||||
phys: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
ohci0: usb@1c14400 {
|
||||
compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
|
||||
reg = <0x01c14400 0x100>;
|
||||
interrupts = <64>;
|
||||
clocks = <&usb_clk 6>, <&ahb_gates 2>;
|
||||
phys = <&usbphy 1>;
|
||||
};
|
||||
|
||||
...
|
@ -8,9 +8,15 @@ Required properties:
|
||||
- interrupt-names: must be "mc"
|
||||
- clocks: phandle to the "udc" clock
|
||||
- clock-names: must be "udc"
|
||||
- phys: phandle to the USB PHY
|
||||
|
||||
Example:
|
||||
|
||||
usb_phy: usb-phy@0 {
|
||||
compatible = "usb-nop-xceiv";
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
udc: usb@13040000 {
|
||||
compatible = "ingenic,jz4740-musb";
|
||||
reg = <0x13040000 0x10000>;
|
||||
@ -21,4 +27,6 @@ udc: usb@13040000 {
|
||||
|
||||
clocks = <&cgu JZ4740_CLK_UDC>;
|
||||
clock-names = "udc";
|
||||
|
||||
phys = <&usb_phy>;
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ Required properties:
|
||||
- Tegra124: "nvidia,tegra124-xusb"
|
||||
- Tegra132: "nvidia,tegra132-xusb", "nvidia,tegra124-xusb"
|
||||
- Tegra210: "nvidia,tegra210-xusb"
|
||||
- Tegra186: "nvidia,tegra186-xusb"
|
||||
- reg: Must contain the base and length of the xHCI host registers, XUSB FPCI
|
||||
registers and XUSB IPFS registers.
|
||||
- reg-names: Must contain the following entries:
|
||||
@ -59,6 +60,8 @@ For Tegra210:
|
||||
- avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 V.
|
||||
- dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
|
||||
- hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V.
|
||||
|
||||
For Tegra210 and Tegra186:
|
||||
- power-domains: A list of PM domain specifiers that reference each power-domain
|
||||
used by the xHCI controller. This list must comprise of a specifier for the
|
||||
XUSBA and XUSBC power-domains. See ../power/power_domain.txt and
|
||||
@ -78,6 +81,7 @@ Optional properties:
|
||||
- Tegra132: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1
|
||||
- Tegra210: usb2-0, usb2-1, usb2-2, usb2-3, hsic-0, usb3-0, usb3-1, usb3-2,
|
||||
usb3-3
|
||||
- Tegra186: usb2-0, usb2-1, usb2-2, hsic-0, usb3-0, usb3-1, usb3-2
|
||||
|
||||
Example:
|
||||
--------
|
||||
|
@ -6,6 +6,7 @@ Required properties:
|
||||
- "renesas,usbhs-r8a7743" for r8a7743 (RZ/G1M) compatible device
|
||||
- "renesas,usbhs-r8a7744" for r8a7744 (RZ/G1N) compatible device
|
||||
- "renesas,usbhs-r8a7745" for r8a7745 (RZ/G1E) compatible device
|
||||
- "renesas,usbhs-r8a77470" for r8a77470 (RZ/G1C) compatible device
|
||||
- "renesas,usbhs-r8a774a1" for r8a774a1 (RZ/G2M) compatible device
|
||||
- "renesas,usbhs-r8a774c0" for r8a774c0 (RZ/G2E) compatible device
|
||||
- "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
|
||||
|
@ -1,46 +0,0 @@
|
||||
USB EHCI controllers
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "generic-ehci".
|
||||
- reg : should contain at least address and length of the standard EHCI
|
||||
register set for the device. Optional platform-dependent registers
|
||||
(debug-port or other) can be also specified here, but only after
|
||||
definition of standard EHCI registers.
|
||||
- interrupts : one EHCI interrupt should be described here.
|
||||
|
||||
Optional properties:
|
||||
- big-endian-regs : boolean, set this for hcds with big-endian registers
|
||||
- big-endian-desc : boolean, set this for hcds with big-endian descriptors
|
||||
- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
|
||||
- needs-reset-on-resume : boolean, set this to force EHCI reset after resume
|
||||
- has-transaction-translator : boolean, set this if EHCI have a Transaction
|
||||
Translator built into the root hub.
|
||||
- clocks : a list of phandle + clock specifier pairs. In case of Renesas
|
||||
R-Car Gen3 SoCs:
|
||||
- if a host only channel: first clock should be host.
|
||||
- if a USB DRD channel: first clock should be host and second one
|
||||
should be peripheral.
|
||||
- phys : see usb-hcd.txt in the current directory
|
||||
- resets : phandle + reset specifier pair
|
||||
|
||||
additionally the properties from usb-hcd.txt (in the current directory) are
|
||||
supported.
|
||||
|
||||
Example (Sequoia 440EPx):
|
||||
ehci@e0000300 {
|
||||
compatible = "ibm,usb-ehci-440epx", "usb-ehci";
|
||||
interrupt-parent = <&UIC0>;
|
||||
interrupts = <1a 4>;
|
||||
reg = <0 e0000300 90 0 e0000390 70>;
|
||||
big-endian;
|
||||
};
|
||||
|
||||
Example (Allwinner sun4i A10 SoC):
|
||||
ehci0: usb@1c14000 {
|
||||
compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
|
||||
reg = <0x01c14000 0x100>;
|
||||
interrupts = <39>;
|
||||
clocks = <&ahb_gates 1>;
|
||||
phys = <&usbphy 1>;
|
||||
phy-names = "usb";
|
||||
};
|
@ -1,9 +0,0 @@
|
||||
Generic USB HCD (Host Controller Device) Properties
|
||||
|
||||
Optional properties:
|
||||
- phys: a list of all USB PHYs on this HCD
|
||||
|
||||
Example:
|
||||
&usb1 {
|
||||
phys = <&usb2_phy1>, <&usb3_phy1>;
|
||||
};
|
25
Documentation/devicetree/bindings/usb/usb-hcd.yaml
Normal file
25
Documentation/devicetree/bindings/usb/usb-hcd.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/usb/usb-hcd.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Generic USB Host Controller Device Tree Bindings
|
||||
|
||||
maintainers:
|
||||
- Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^usb(@.*)?"
|
||||
|
||||
phys:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||
description:
|
||||
List of all the USB PHYs on this HCD
|
||||
|
||||
examples:
|
||||
- |
|
||||
usb {
|
||||
phys = <&usb2_phy1>, <&usb3_phy1>;
|
||||
};
|
@ -1,35 +0,0 @@
|
||||
USB OHCI controllers
|
||||
|
||||
Required properties:
|
||||
- compatible : "generic-ohci"
|
||||
- reg : ohci controller register range (address and length)
|
||||
- interrupts : ohci controller interrupt
|
||||
|
||||
Optional properties:
|
||||
- big-endian-regs : boolean, set this for hcds with big-endian registers
|
||||
- big-endian-desc : boolean, set this for hcds with big-endian descriptors
|
||||
- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
|
||||
- no-big-frame-no : boolean, set if frame_no lives in bits [15:0] of HCCA
|
||||
- remote-wakeup-connected: remote wakeup is wired on the platform
|
||||
- num-ports : u32, to override the detected port count
|
||||
- clocks : a list of phandle + clock specifier pairs. In case of Renesas
|
||||
R-Car Gen3 SoCs:
|
||||
- if a host only channel: first clock should be host.
|
||||
- if a USB DRD channel: first clock should be host and second one
|
||||
should be peripheral.
|
||||
- phys : see usb-hcd.txt in the current directory
|
||||
- resets : a list of phandle + reset specifier pairs
|
||||
|
||||
additionally the properties from usb-hcd.txt (in the current directory) are
|
||||
supported.
|
||||
|
||||
Example:
|
||||
|
||||
ohci0: usb@1c14400 {
|
||||
compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
|
||||
reg = <0x01c14400 0x100>;
|
||||
interrupts = <64>;
|
||||
clocks = <&usb_clk 6>, <&ahb_gates 2>;
|
||||
phys = <&usbphy 1>;
|
||||
phy-names = "usb";
|
||||
};
|
@ -10,6 +10,7 @@ Required properties:
|
||||
- "renesas,xhci-r8a7743" for r8a7743 SoC
|
||||
- "renesas,xhci-r8a7744" for r8a7744 SoC
|
||||
- "renesas,xhci-r8a774a1" for r8a774a1 SoC
|
||||
- "renesas,xhci-r8a774c0" for r8a774c0 SoC
|
||||
- "renesas,xhci-r8a7790" for r8a7790 SoC
|
||||
- "renesas,xhci-r8a7791" for r8a7791 SoC
|
||||
- "renesas,xhci-r8a7793" for r8a7793 SoC
|
||||
|
@ -64,8 +64,10 @@ Optional properties :
|
||||
- power-on-time-ms : Specifies the time it takes from the time the host
|
||||
initiates the power-on sequence to a port until the port has adequate
|
||||
power. The value is given in ms in a 0 - 510 range (default is 100ms).
|
||||
- swap-dx-lanes : Specifies the ports which will swap the differential-pair
|
||||
(D+/D-), default is not-swapped.
|
||||
- swap-dx-lanes : Specifies the downstream ports which will swap the
|
||||
differential-pair (D+/D-), default is not-swapped.
|
||||
- swap-us-lanes : Selects the upstream port differential-pair (D+/D-)
|
||||
swapping (boolean, default is not-swapped)
|
||||
|
||||
Examples:
|
||||
usb2512b@2c {
|
||||
|
@ -1,7 +1,9 @@
|
||||
|
||||
================================
|
||||
Linux UWB + Wireless USB + WiNET
|
||||
================================
|
||||
|
||||
Copyright (C) 2005-2006 Intel Corporation
|
||||
|
||||
(C) 2005-2006 Intel Corporation
|
||||
Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
@ -29,6 +31,7 @@ drivers for the USB based UWB radio controllers defined in the
|
||||
Wireless USB 1.0 specification (including Wireless USB host controller
|
||||
and an Intel WiNET controller).
|
||||
|
||||
.. Contents
|
||||
1. Introduction
|
||||
1. HWA: Host Wire adapters, your Wireless USB dongle
|
||||
|
||||
@ -51,7 +54,8 @@ and an Intel WiNET controller).
|
||||
4. Glossary
|
||||
|
||||
|
||||
Introduction
|
||||
Introduction
|
||||
============
|
||||
|
||||
UWB is a wide-band communication protocol that is to serve also as the
|
||||
low-level protocol for others (much like TCP sits on IP). Currently
|
||||
@ -93,7 +97,8 @@ The different logical parts of this driver are:
|
||||
do the actual WUSB.
|
||||
|
||||
|
||||
HWA: Host Wire adapters, your Wireless USB dongle
|
||||
HWA: Host Wire adapters, your Wireless USB dongle
|
||||
-------------------------------------------------
|
||||
|
||||
WUSB also defines a device called a Host Wire Adaptor (HWA), which in
|
||||
mere terms is a USB dongle that enables your PC to have UWB and Wireless
|
||||
@ -125,7 +130,8 @@ The HWA itself is broken in two or three main interfaces:
|
||||
their type and kick into gear.
|
||||
|
||||
|
||||
DWA: Device Wired Adaptor, a Wireless USB hub for wired devices
|
||||
DWA: Device Wired Adaptor, a Wireless USB hub for wired devices
|
||||
---------------------------------------------------------------
|
||||
|
||||
These are the complement to HWAs. They are a USB host for connecting
|
||||
wired devices, but it is connected to your PC connected via Wireless
|
||||
@ -137,7 +143,8 @@ code with the HWA-RC driver; there is a bunch of factorization work that
|
||||
has been done to support that in upcoming releases.
|
||||
|
||||
|
||||
WHCI: Wireless Host Controller Interface, the PCI WUSB host adapter
|
||||
WHCI: Wireless Host Controller Interface, the PCI WUSB host adapter
|
||||
-------------------------------------------------------------------
|
||||
|
||||
This is your usual PCI device that implements WHCI. Similar in concept
|
||||
to EHCI, it allows your wireless USB devices (including DWAs) to connect
|
||||
@ -148,7 +155,8 @@ There is still no driver support for this, but will be in upcoming
|
||||
releases.
|
||||
|
||||
|
||||
The UWB stack
|
||||
The UWB stack
|
||||
=============
|
||||
|
||||
The main mission of the UWB stack is to keep a tally of which devices
|
||||
are in radio proximity to allow drivers to connect to them. As well, it
|
||||
@ -156,7 +164,8 @@ provides an API for controlling the local radio controllers (RCs from
|
||||
now on), such as to start/stop beaconing, scan, allocate bandwidth, etc.
|
||||
|
||||
|
||||
Devices and hosts: the basic structure
|
||||
Devices and hosts: the basic structure
|
||||
--------------------------------------
|
||||
|
||||
The main building block here is the UWB device (struct uwb_dev). For
|
||||
each device that pops up in radio presence (ie: the UWB host receives a
|
||||
@ -187,7 +196,8 @@ the USB connected HWA. Eventually, drivers/whci-rc.c will do the same
|
||||
for the PCI connected WHCI controller.
|
||||
|
||||
|
||||
Host Controller life cycle
|
||||
Host Controller life cycle
|
||||
--------------------------
|
||||
|
||||
So let's say we connect a dongle to the system: it is detected and
|
||||
firmware uploaded if needed [for Intel's i1480
|
||||
@ -209,7 +219,8 @@ When a dongle is disconnected, /drivers/uwb/hwa-rc.c:hwarc_disconnect()/
|
||||
takes time of tearing everything down safely (or not...).
|
||||
|
||||
|
||||
On the air: beacons and enumerating the radio neighborhood
|
||||
On the air: beacons and enumerating the radio neighborhood
|
||||
----------------------------------------------------------
|
||||
|
||||
So assuming we have devices and we have agreed for a channel to connect
|
||||
on (let's say 9), we put the new RC to beacon:
|
||||
@ -235,12 +246,14 @@ are received in some time, the device is considered gone and wiped out
|
||||
the beacon cache of dead devices].
|
||||
|
||||
|
||||
Device lists
|
||||
Device lists
|
||||
------------
|
||||
|
||||
All UWB devices are kept in the list of the struct bus_type uwb_bus_type.
|
||||
|
||||
|
||||
Bandwidth allocation
|
||||
Bandwidth allocation
|
||||
--------------------
|
||||
|
||||
The UWB stack maintains a local copy of DRP availability through
|
||||
processing of incoming *DRP Availability Change* notifications. This
|
||||
@ -260,7 +273,8 @@ completion. [Note: The bandwidth reservation work is in progress and
|
||||
subject to change.]
|
||||
|
||||
|
||||
Wireless USB Host Controller drivers
|
||||
Wireless USB Host Controller drivers
|
||||
====================================
|
||||
|
||||
*WARNING* This section needs a lot of work!
|
||||
|
||||
@ -296,7 +310,8 @@ starts sending MMCs.
|
||||
|
||||
Now it all depends on external stimuli.
|
||||
|
||||
*New device connection*
|
||||
New device connection
|
||||
---------------------
|
||||
|
||||
A new device pops up, it scans the radio looking for MMCs that give out
|
||||
the existence of Wireless USB channels. Once one (or more) are found,
|
||||
@ -322,7 +337,8 @@ has seen the port status changes, as we have been toggling them. It will
|
||||
start enumerating and doing transfers through usb_hcd->urb_enqueue() to
|
||||
read descriptors and move our data.
|
||||
|
||||
*Device life cycle and keep alives*
|
||||
Device life cycle and keep alives
|
||||
---------------------------------
|
||||
|
||||
Every time there is a successful transfer to/from a device, we update a
|
||||
per-device activity timestamp. If not, every now and then we check and
|
||||
@ -340,7 +356,8 @@ device list looking for whom needs refreshing.
|
||||
If the device wants to disconnect, it will either die (ugly) or send a
|
||||
/DN_Disconnect/ that will prompt a disconnection from the system.
|
||||
|
||||
*Sending and receiving data*
|
||||
Sending and receiving data
|
||||
--------------------------
|
||||
|
||||
Data is sent and received through /Remote Pipes/ (rpipes). An rpipe is
|
||||
/aimed/ at an endpoint in a WUSB device. This is the same for HWAs and
|
||||
@ -394,7 +411,8 @@ finalize the transfer.
|
||||
For IN xfers, we only issue URBs for the segments we want to read and
|
||||
then wait for the xfer result data.
|
||||
|
||||
*URB mapping into xfers*
|
||||
URB mapping into xfers
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is done by hwahc_op_urb_[en|de]queue(). In enqueue() we aim an
|
||||
rpipe to the endpoint where we have to transmit, create a transfer
|
||||
@ -407,7 +425,8 @@ and not yet done and when all that is done, the xfer callback will be
|
||||
called--this will call the URB callback.
|
||||
|
||||
|
||||
Glossary
|
||||
Glossary
|
||||
========
|
||||
|
||||
*DWA* -- Device Wire Adapter
|
||||
|
||||
@ -436,4 +455,3 @@ the host.
|
||||
|
||||
Design-overview.txt-1.8 (last edited 2006-11-04 12:22:24 by
|
||||
InakyPerezGonzalez)
|
||||
|
||||
|
@ -1,127 +1,131 @@
|
||||
Linux ACM driver v0.16
|
||||
(c) 1999 Vojtech Pavlik <vojtech@suse.cz>
|
||||
Sponsored by SuSE
|
||||
----------------------------------------------------------------------------
|
||||
======================
|
||||
Linux ACM driver v0.16
|
||||
======================
|
||||
|
||||
Copyright (c) 1999 Vojtech Pavlik <vojtech@suse.cz>
|
||||
|
||||
Sponsored by SuSE
|
||||
|
||||
0. Disclaimer
|
||||
~~~~~~~~~~~~~
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 2 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Should you need to contact me, the author, you can do so either by e-mail
|
||||
- mail your message to <vojtech@suse.cz>, or by paper mail: Vojtech Pavlik,
|
||||
Should you need to contact me, the author, you can do so either by e-mail -
|
||||
mail your message to <vojtech@suse.cz>, or by paper mail: Vojtech Pavlik,
|
||||
Ucitelska 1576, Prague 8, 182 00 Czech Republic
|
||||
|
||||
For your convenience, the GNU General Public License version 2 is included
|
||||
For your convenience, the GNU General Public License version 2 is included
|
||||
in the package: See the file COPYING.
|
||||
|
||||
1. Usage
|
||||
~~~~~~~~
|
||||
The drivers/usb/class/cdc-acm.c drivers works with USB modems and USB ISDN terminal
|
||||
The drivers/usb/class/cdc-acm.c drivers works with USB modems and USB ISDN terminal
|
||||
adapters that conform to the Universal Serial Bus Communication Device Class
|
||||
Abstract Control Model (USB CDC ACM) specification.
|
||||
|
||||
Many modems do, here is a list of those I know of:
|
||||
Many modems do, here is a list of those I know of:
|
||||
|
||||
3Com OfficeConnect 56k
|
||||
3Com Voice FaxModem Pro
|
||||
3Com Sportster
|
||||
MultiTech MultiModem 56k
|
||||
Zoom 2986L FaxModem
|
||||
Compaq 56k FaxModem
|
||||
ELSA Microlink 56k
|
||||
- 3Com OfficeConnect 56k
|
||||
- 3Com Voice FaxModem Pro
|
||||
- 3Com Sportster
|
||||
- MultiTech MultiModem 56k
|
||||
- Zoom 2986L FaxModem
|
||||
- Compaq 56k FaxModem
|
||||
- ELSA Microlink 56k
|
||||
|
||||
I know of one ISDN TA that does work with the acm driver:
|
||||
I know of one ISDN TA that does work with the acm driver:
|
||||
|
||||
3Com USR ISDN Pro TA
|
||||
- 3Com USR ISDN Pro TA
|
||||
|
||||
Some cell phones also connect via USB. I know the following phones work:
|
||||
Some cell phones also connect via USB. I know the following phones work:
|
||||
|
||||
SonyEricsson K800i
|
||||
- SonyEricsson K800i
|
||||
|
||||
Unfortunately many modems and most ISDN TAs use proprietary interfaces and
|
||||
Unfortunately many modems and most ISDN TAs use proprietary interfaces and
|
||||
thus won't work with this drivers. Check for ACM compliance before buying.
|
||||
|
||||
To use the modems you need these modules loaded:
|
||||
To use the modems you need these modules loaded::
|
||||
|
||||
usbcore.ko
|
||||
uhci-hcd.ko ohci-hcd.ko or ehci-hcd.ko
|
||||
cdc-acm.ko
|
||||
|
||||
After that, the modem[s] should be accessible. You should be able to use
|
||||
After that, the modem[s] should be accessible. You should be able to use
|
||||
minicom, ppp and mgetty with them.
|
||||
|
||||
2. Verifying that it works
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The first step would be to check /sys/kernel/debug/usb/devices, it should look
|
||||
like this:
|
||||
|
||||
T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2
|
||||
B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
|
||||
D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
||||
P: Vendor=0000 ProdID=0000 Rev= 0.00
|
||||
S: Product=USB UHCI Root Hub
|
||||
S: SerialNumber=6800
|
||||
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA
|
||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
|
||||
E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms
|
||||
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
|
||||
D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
|
||||
P: Vendor=04c1 ProdID=008f Rev= 2.07
|
||||
S: Manufacturer=3Com Inc.
|
||||
S: Product=3Com U.S. Robotics Pro ISDN TA
|
||||
S: SerialNumber=UFT53A49BVT7
|
||||
C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA
|
||||
I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm
|
||||
E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||
E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||
E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
|
||||
C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA
|
||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
|
||||
E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
|
||||
I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
|
||||
E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||
E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||
The first step would be to check /sys/kernel/debug/usb/devices, it should look
|
||||
like this::
|
||||
|
||||
T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2
|
||||
B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
|
||||
D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
||||
P: Vendor=0000 ProdID=0000 Rev= 0.00
|
||||
S: Product=USB UHCI Root Hub
|
||||
S: SerialNumber=6800
|
||||
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA
|
||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
|
||||
E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms
|
||||
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
|
||||
D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
|
||||
P: Vendor=04c1 ProdID=008f Rev= 2.07
|
||||
S: Manufacturer=3Com Inc.
|
||||
S: Product=3Com U.S. Robotics Pro ISDN TA
|
||||
S: SerialNumber=UFT53A49BVT7
|
||||
C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA
|
||||
I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm
|
||||
E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||
E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||
E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
|
||||
C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA
|
||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
|
||||
E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
|
||||
I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
|
||||
E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||
E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||
|
||||
The presence of these three lines (and the Cls= 'comm' and 'data' classes)
|
||||
is important, it means it's an ACM device. The Driver=acm means the acm
|
||||
driver is used for the device. If you see only Cls=ff(vend.) then you're out
|
||||
of luck, you have a device with vendor specific-interface.
|
||||
of luck, you have a device with vendor specific-interface::
|
||||
|
||||
D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
|
||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
|
||||
I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
|
||||
D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
|
||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
|
||||
I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
|
||||
|
||||
In the system log you should see:
|
||||
In the system log you should see::
|
||||
|
||||
usb.c: USB new device connect, assigned device number 2
|
||||
usb.c: kmalloc IF c7691fa0, numif 1
|
||||
usb.c: kmalloc IF c7b5f3e0, numif 2
|
||||
usb.c: skipped 4 class/vendor specific interface descriptors
|
||||
usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3
|
||||
usb.c: USB device number 2 default language ID 0x409
|
||||
Manufacturer: 3Com Inc.
|
||||
Product: 3Com U.S. Robotics Pro ISDN TA
|
||||
SerialNumber: UFT53A49BVT7
|
||||
acm.c: probing config 1
|
||||
acm.c: probing config 2
|
||||
ttyACM0: USB ACM device
|
||||
acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0
|
||||
acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7
|
||||
usb.c: acm driver claimed interface c7b5f3e0
|
||||
usb.c: acm driver claimed interface c7b5f3f8
|
||||
usb.c: acm driver claimed interface c7691fa0
|
||||
usb.c: USB new device connect, assigned device number 2
|
||||
usb.c: kmalloc IF c7691fa0, numif 1
|
||||
usb.c: kmalloc IF c7b5f3e0, numif 2
|
||||
usb.c: skipped 4 class/vendor specific interface descriptors
|
||||
usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3
|
||||
usb.c: USB device number 2 default language ID 0x409
|
||||
Manufacturer: 3Com Inc.
|
||||
Product: 3Com U.S. Robotics Pro ISDN TA
|
||||
SerialNumber: UFT53A49BVT7
|
||||
acm.c: probing config 1
|
||||
acm.c: probing config 2
|
||||
ttyACM0: USB ACM device
|
||||
acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0
|
||||
acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7
|
||||
usb.c: acm driver claimed interface c7b5f3e0
|
||||
usb.c: acm driver claimed interface c7b5f3f8
|
||||
usb.c: acm driver claimed interface c7691fa0
|
||||
|
||||
If all this seems to be OK, fire up minicom and set it to talk to the ttyACM
|
||||
device and try typing 'at'. If it responds with 'OK', then everything is
|
||||
|
@ -1,7 +1,8 @@
|
||||
|
||||
==============================================================
|
||||
Authorizing (or not) your USB devices to connect to the system
|
||||
==============================================================
|
||||
|
||||
(C) 2007 Inaky Perez-Gonzalez <inaky@linux.intel.com> Intel Corporation
|
||||
Copyright (C) 2007 Inaky Perez-Gonzalez <inaky@linux.intel.com> Intel Corporation
|
||||
|
||||
This feature allows you to control if a USB device can be used (or
|
||||
not) in a system. This feature will allow you to implement a lock-down
|
||||
@ -12,24 +13,25 @@ its interfaces are immediately made available to the users. With this
|
||||
modification, only if root authorizes the device to be configured will
|
||||
then it be possible to use it.
|
||||
|
||||
Usage:
|
||||
Usage
|
||||
=====
|
||||
|
||||
Authorize a device to connect:
|
||||
Authorize a device to connect::
|
||||
|
||||
$ echo 1 > /sys/bus/usb/devices/DEVICE/authorized
|
||||
$ echo 1 > /sys/bus/usb/devices/DEVICE/authorized
|
||||
|
||||
Deauthorize a device:
|
||||
De-authorize a device::
|
||||
|
||||
$ echo 0 > /sys/bus/usb/devices/DEVICE/authorized
|
||||
$ echo 0 > /sys/bus/usb/devices/DEVICE/authorized
|
||||
|
||||
Set new devices connected to hostX to be deauthorized by default (ie:
|
||||
lock down):
|
||||
lock down)::
|
||||
|
||||
$ echo 0 > /sys/bus/usb/devices/usbX/authorized_default
|
||||
$ echo 0 > /sys/bus/usb/devices/usbX/authorized_default
|
||||
|
||||
Remove the lock down:
|
||||
Remove the lock down::
|
||||
|
||||
$ echo 1 > /sys/bus/usb/devices/usbX/authorized_default
|
||||
$ echo 1 > /sys/bus/usb/devices/usbX/authorized_default
|
||||
|
||||
By default, Wired USB devices are authorized by default to
|
||||
connect. Wireless USB hosts deauthorize by default all new connected
|
||||
@ -40,21 +42,21 @@ USB ports.
|
||||
|
||||
|
||||
Example system lockdown (lame)
|
||||
-----------------------
|
||||
------------------------------
|
||||
|
||||
Imagine you want to implement a lockdown so only devices of type XYZ
|
||||
can be connected (for example, it is a kiosk machine with a visible
|
||||
USB port):
|
||||
USB port)::
|
||||
|
||||
boot up
|
||||
rc.local ->
|
||||
boot up
|
||||
rc.local ->
|
||||
|
||||
for host in /sys/bus/usb/devices/usb*
|
||||
do
|
||||
echo 0 > $host/authorized_default
|
||||
done
|
||||
|
||||
Hookup an script to udev, for new USB devices
|
||||
Hookup an script to udev, for new USB devices::
|
||||
|
||||
if device_is_my_type $DEV
|
||||
then
|
||||
@ -67,10 +69,10 @@ checking if the class, type and protocol match something is the worse
|
||||
security verification you can make (or the best, for someone willing
|
||||
to break it). If you need something secure, use crypto and Certificate
|
||||
Authentication or stuff like that. Something simple for an storage key
|
||||
could be:
|
||||
could be::
|
||||
|
||||
function device_is_my_type()
|
||||
{
|
||||
function device_is_my_type()
|
||||
{
|
||||
echo 1 > authorized # temporarily authorize it
|
||||
# FIXME: make sure none can mount it
|
||||
mount DEVICENODE /mntpoint
|
||||
@ -83,7 +85,7 @@ function device_is_my_type()
|
||||
else
|
||||
echo 0 > authorized
|
||||
fi
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Of course, this is lame, you'd want to do a real certificate
|
||||
@ -95,30 +97,35 @@ welcome.
|
||||
|
||||
Interface authorization
|
||||
-----------------------
|
||||
|
||||
There is a similar approach to allow or deny specific USB interfaces.
|
||||
That allows to block only a subset of an USB device.
|
||||
|
||||
Authorize an interface:
|
||||
$ echo 1 > /sys/bus/usb/devices/INTERFACE/authorized
|
||||
Authorize an interface::
|
||||
|
||||
Deauthorize an interface:
|
||||
$ echo 0 > /sys/bus/usb/devices/INTERFACE/authorized
|
||||
$ echo 1 > /sys/bus/usb/devices/INTERFACE/authorized
|
||||
|
||||
Deauthorize an interface::
|
||||
|
||||
$ echo 0 > /sys/bus/usb/devices/INTERFACE/authorized
|
||||
|
||||
The default value for new interfaces
|
||||
on a particular USB bus can be changed, too.
|
||||
|
||||
Allow interfaces per default:
|
||||
$ echo 1 > /sys/bus/usb/devices/usbX/interface_authorized_default
|
||||
Allow interfaces per default::
|
||||
|
||||
Deny interfaces per default:
|
||||
$ echo 0 > /sys/bus/usb/devices/usbX/interface_authorized_default
|
||||
$ echo 1 > /sys/bus/usb/devices/usbX/interface_authorized_default
|
||||
|
||||
Deny interfaces per default::
|
||||
|
||||
$ echo 0 > /sys/bus/usb/devices/usbX/interface_authorized_default
|
||||
|
||||
Per default the interface_authorized_default bit is 1.
|
||||
So all interfaces would authorized per default.
|
||||
|
||||
Note:
|
||||
If a deauthorized interface will be authorized so the driver probing must
|
||||
be triggered manually by writing INTERFACE to /sys/bus/usb/drivers_probe
|
||||
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
|
||||
authorized first. After that the drivers should be probed.
|
||||
|
@ -1,22 +1,37 @@
|
||||
==============================================
|
||||
ChipIdea Highspeed Dual Role Controller Driver
|
||||
==============================================
|
||||
|
||||
1. How to test OTG FSM(HNP and SRP)
|
||||
-----------------------------------
|
||||
|
||||
To show how to demo OTG HNP and SRP functions via sys input files
|
||||
with 2 Freescale i.MX6Q sabre SD boards.
|
||||
|
||||
1.1 How to enable OTG FSM
|
||||
---------------------------------------
|
||||
-------------------------
|
||||
|
||||
1.1.1 Select CONFIG_USB_OTG_FSM in menuconfig, rebuild kernel
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Image and modules. If you want to check some internal
|
||||
variables for otg fsm, mount debugfs, there are 2 files
|
||||
which can show otg fsm variables and some controller registers value:
|
||||
cat /sys/kernel/debug/ci_hdrc.0/otg
|
||||
cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||
which can show otg fsm variables and some controller registers value::
|
||||
|
||||
cat /sys/kernel/debug/ci_hdrc.0/otg
|
||||
cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||
|
||||
1.1.2 Add below entries in your dts file for your controller node
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
otg-rev = <0x0200>;
|
||||
adp-disable;
|
||||
|
||||
1.2 Test operations
|
||||
-------------------
|
||||
|
||||
1) Power up 2 Freescale i.MX6Q sabre SD boards with gadget class driver loaded
|
||||
(e.g. g_mass_storage).
|
||||
|
||||
@ -26,18 +41,23 @@ cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||
The A-device(with micro A plug inserted) should enumerate B-device.
|
||||
|
||||
3) Role switch
|
||||
On B-device:
|
||||
|
||||
On B-device::
|
||||
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
||||
|
||||
B-device should take host role and enumerate A-device.
|
||||
|
||||
4) A-device switch back to host.
|
||||
On B-device:
|
||||
|
||||
On B-device::
|
||||
|
||||
echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
||||
|
||||
or, by introducing HNP polling, B-Host can know when A-peripheral wish
|
||||
to be host role, so this role switch also can be trigged in A-peripheral
|
||||
side by answering the polling from B-Host, this can be done on A-device:
|
||||
side by answering the polling from B-Host, this can be done on A-device::
|
||||
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
|
||||
|
||||
A-device should switch back to host and enumerate B-device.
|
||||
@ -49,22 +69,30 @@ cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||
A-device should NOT enumerate B-device.
|
||||
|
||||
if A-device wants to use bus:
|
||||
On A-device:
|
||||
|
||||
On A-device::
|
||||
|
||||
echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_drop
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
|
||||
|
||||
if B-device wants to use bus:
|
||||
On B-device:
|
||||
|
||||
On B-device::
|
||||
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
||||
|
||||
7) A-device power down the bus.
|
||||
On A-device:
|
||||
|
||||
On A-device::
|
||||
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_drop
|
||||
|
||||
A-device should disconnect with B-device and power down the bus.
|
||||
|
||||
8) B-device does data pulse for SRP.
|
||||
On B-device:
|
||||
|
||||
On B-device::
|
||||
|
||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
||||
|
||||
A-device should resume usb bus and enumerate B-device.
|
||||
@ -75,22 +103,31 @@ cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||
July 27, 2012 Revision 2.0 version 1.1a"
|
||||
|
||||
2. How to enable USB as system wakeup source
|
||||
-----------------------------------
|
||||
--------------------------------------------
|
||||
Below is the example for how to enable USB as system wakeup source
|
||||
at imx6 platform.
|
||||
|
||||
2.1 Enable core's wakeup
|
||||
echo enabled > /sys/bus/platform/devices/ci_hdrc.0/power/wakeup
|
||||
2.2 Enable glue layer's wakeup
|
||||
echo enabled > /sys/bus/platform/devices/2184000.usb/power/wakeup
|
||||
2.3 Enable PHY's wakeup (optional)
|
||||
echo enabled > /sys/bus/platform/devices/20c9000.usbphy/power/wakeup
|
||||
2.4 Enable roothub's wakeup
|
||||
echo enabled > /sys/bus/usb/devices/usb1/power/wakeup
|
||||
2.5 Enable related device's wakeup
|
||||
echo enabled > /sys/bus/usb/devices/1-1/power/wakeup
|
||||
2.1 Enable core's wakeup::
|
||||
|
||||
echo enabled > /sys/bus/platform/devices/ci_hdrc.0/power/wakeup
|
||||
|
||||
2.2 Enable glue layer's wakeup::
|
||||
|
||||
echo enabled > /sys/bus/platform/devices/2184000.usb/power/wakeup
|
||||
|
||||
2.3 Enable PHY's wakeup (optional)::
|
||||
|
||||
echo enabled > /sys/bus/platform/devices/20c9000.usbphy/power/wakeup
|
||||
|
||||
2.4 Enable roothub's wakeup::
|
||||
|
||||
echo enabled > /sys/bus/usb/devices/usb1/power/wakeup
|
||||
|
||||
2.5 Enable related device's wakeup::
|
||||
|
||||
echo enabled > /sys/bus/usb/devices/1-1/power/wakeup
|
||||
|
||||
If the system has only one usb port, and you want usb wakeup at this port, you
|
||||
can use below script to enable usb wakeup.
|
||||
for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done;
|
||||
can use below script to enable usb wakeup::
|
||||
|
||||
for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done;
|
||||
|
@ -1,6 +1,11 @@
|
||||
===========
|
||||
DWC3 driver
|
||||
===========
|
||||
|
||||
|
||||
TODO
|
||||
~~~~
|
||||
|
||||
TODO
|
||||
~~~~~~
|
||||
Please pick something while reading :)
|
||||
|
||||
- Convert interrupt handler to per-ep-thread-irq
|
||||
@ -9,6 +14,7 @@ Please pick something while reading :)
|
||||
until the command completes which is bad.
|
||||
|
||||
Implementation idea:
|
||||
|
||||
- dwc core implements a demultiplexing irq chip for interrupts per
|
||||
endpoint. The interrupt numbers are allocated during probe and belong
|
||||
to the device. If MSI provides per-endpoint interrupt this dummy
|
||||
@ -19,6 +25,7 @@ Please pick something while reading :)
|
||||
- dwc3_send_gadget_ep_cmd() will sleep in wait_for_completion_timeout()
|
||||
until the command completes.
|
||||
- the interrupt handler is split into the following pieces:
|
||||
|
||||
- primary handler of the device
|
||||
goes through every event and calls generic_handle_irq() for event
|
||||
it. On return from generic_handle_irq() in acknowledges the event
|
||||
@ -40,6 +47,7 @@ Please pick something while reading :)
|
||||
for command completion.
|
||||
|
||||
Latency:
|
||||
|
||||
There should be no increase in latency since the interrupt-thread has a
|
||||
high priority and will be run before an average task in user land
|
||||
(except the user changed priorities).
|
||||
|
@ -1,3 +1,7 @@
|
||||
===========
|
||||
EHCI driver
|
||||
===========
|
||||
|
||||
27-Dec-2002
|
||||
|
||||
The EHCI driver is used to talk to high speed USB 2.0 devices using
|
||||
@ -40,7 +44,8 @@ APIs exposed to USB device drivers.
|
||||
<dbrownell@users.sourceforge.net>
|
||||
|
||||
|
||||
FUNCTIONALITY
|
||||
Functionality
|
||||
=============
|
||||
|
||||
This driver is regularly tested on x86 hardware, and has also been
|
||||
used on PPC hardware so big/little endianness issues should be gone.
|
||||
@ -48,6 +53,7 @@ It's believed to do all the right PCI magic so that I/O works even on
|
||||
systems with interesting DMA mapping issues.
|
||||
|
||||
Transfer Types
|
||||
--------------
|
||||
|
||||
At this writing the driver should comfortably handle all control, bulk,
|
||||
and interrupt transfers, including requests to USB 1.1 devices through
|
||||
@ -63,6 +69,7 @@ since EHCI represents these with a different data structure. So for now,
|
||||
most USB audio and video devices can't be connected to high speed buses.
|
||||
|
||||
Driver Behavior
|
||||
---------------
|
||||
|
||||
Transfers of all types can be queued. This means that control transfers
|
||||
from a driver on one interface (or through usbfs) won't interfere with
|
||||
@ -83,14 +90,15 @@ limits on the number of periodic transactions that can be scheduled,
|
||||
and prevent use of polling intervals of less than one frame.
|
||||
|
||||
|
||||
USE BY
|
||||
Use by
|
||||
======
|
||||
|
||||
Assuming you have an EHCI controller (on a PCI card or motherboard)
|
||||
and have compiled this driver as a module, load this like:
|
||||
and have compiled this driver as a module, load this like::
|
||||
|
||||
# modprobe ehci-hcd
|
||||
|
||||
and remove it by:
|
||||
and remove it by::
|
||||
|
||||
# rmmod ehci-hcd
|
||||
|
||||
@ -112,13 +120,16 @@ If you're using this driver on a 2.5 kernel, and you've enabled USB
|
||||
debugging support, you'll see three files in the "sysfs" directory for
|
||||
any EHCI controller:
|
||||
|
||||
"async" dumps the asynchronous schedule, used for control
|
||||
"async"
|
||||
dumps the asynchronous schedule, used for control
|
||||
and bulk transfers. Shows each active qh and the qtds
|
||||
pending, usually one qtd per urb. (Look at it with
|
||||
usb-storage doing disk I/O; watch the request queues!)
|
||||
"periodic" dumps the periodic schedule, used for interrupt
|
||||
"periodic"
|
||||
dumps the periodic schedule, used for interrupt
|
||||
and isochronous transfers. Doesn't show qtds.
|
||||
"registers" show controller register state, and
|
||||
"registers"
|
||||
show controller register state, and
|
||||
|
||||
The contents of those files can help identify driver problems.
|
||||
|
||||
@ -136,7 +147,8 @@ transaction translators are in use; some drivers have been seen to behave
|
||||
badly when they see different faults than OHCI or UHCI report.
|
||||
|
||||
|
||||
PERFORMANCE
|
||||
Performance
|
||||
===========
|
||||
|
||||
USB 2.0 throughput is gated by two main factors: how fast the host
|
||||
controller can process requests, and how fast devices can respond to
|
||||
@ -156,6 +168,7 @@ hardware and device driver software allow it. Periodic transfer modes
|
||||
approach the quoted 480 MBit/sec transfer rate.
|
||||
|
||||
Hardware Performance
|
||||
--------------------
|
||||
|
||||
At this writing, individual USB 2.0 devices tend to max out at around
|
||||
20 MByte/sec transfer rates. This is of course subject to change;
|
||||
@ -183,6 +196,7 @@ you issue a control or bulk request you can often expect to learn that
|
||||
it completed in less than 250 usec (depending on transfer size).
|
||||
|
||||
Software Performance
|
||||
--------------------
|
||||
|
||||
To get even 20 MByte/sec transfer rates, Linux-USB device drivers will
|
||||
need to keep the EHCI queue full. That means issuing large requests,
|
||||
@ -206,9 +220,11 @@ mapping (which might apply an IOMMU) and IRQ reduction, all of which will
|
||||
help make high speed transfers run as fast as they can.
|
||||
|
||||
|
||||
TBD: Interrupt and ISO transfer performance issues. Those periodic
|
||||
transfers are fully scheduled, so the main issue is likely to be how
|
||||
to trigger "high bandwidth" modes.
|
||||
TBD:
|
||||
Interrupt and ISO transfer performance issues. Those periodic
|
||||
transfers are fully scheduled, so the main issue is likely to be how
|
||||
to trigger "high bandwidth" modes.
|
||||
|
||||
TBD: More than standard 80% periodic bandwidth allocation is possible
|
||||
through sysfs uframe_periodic_max parameter. Describe that.
|
||||
TBD:
|
||||
More than standard 80% periodic bandwidth allocation is possible
|
||||
through sysfs uframe_periodic_max parameter. Describe that.
|
||||
|
@ -1,4 +1,6 @@
|
||||
*How FunctionFS works*
|
||||
====================
|
||||
How FunctionFS works
|
||||
====================
|
||||
|
||||
From kernel point of view it is just a composite function with some
|
||||
unique behaviour. It may be added to an USB configuration only after
|
||||
@ -38,13 +40,13 @@ when mounting.
|
||||
|
||||
One can imagine a gadget that has an Ethernet, MTP and HID interfaces
|
||||
where the last two are implemented via FunctionFS. On user space
|
||||
level it would look like this:
|
||||
level it would look like this::
|
||||
|
||||
$ insmod g_ffs.ko idVendor=<ID> iSerialNumber=<string> functions=mtp,hid
|
||||
$ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
|
||||
$ ( cd /dev/ffs-mtp && mtp-daemon ) &
|
||||
$ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
|
||||
$ ( cd /dev/ffs-hid && hid-daemon ) &
|
||||
$ insmod g_ffs.ko idVendor=<ID> iSerialNumber=<string> functions=mtp,hid
|
||||
$ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
|
||||
$ ( cd /dev/ffs-mtp && mtp-daemon ) &
|
||||
$ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
|
||||
$ ( cd /dev/ffs-hid && hid-daemon ) &
|
||||
|
||||
On kernel level the gadget checks ffs_data->dev_name to identify
|
||||
whether it's FunctionFS designed for MTP ("mtp") or HID ("hid").
|
||||
@ -64,4 +66,3 @@ have been written to their ep0's.
|
||||
|
||||
Conversely, the gadget is unregistered after the first USB function
|
||||
closes its endpoints.
|
||||
|
||||
|
@ -1,26 +1,32 @@
|
||||
==============
|
||||
Gadget Testing
|
||||
==============
|
||||
|
||||
This file summarizes information on basic testing of USB functions
|
||||
provided by gadgets.
|
||||
|
||||
1. ACM function
|
||||
2. ECM function
|
||||
3. ECM subset function
|
||||
4. EEM function
|
||||
5. FFS function
|
||||
6. HID function
|
||||
7. LOOPBACK function
|
||||
8. MASS STORAGE function
|
||||
9. MIDI function
|
||||
10. NCM function
|
||||
11. OBEX function
|
||||
12. PHONET function
|
||||
13. RNDIS function
|
||||
14. SERIAL function
|
||||
15. SOURCESINK function
|
||||
16. UAC1 function (legacy implementation)
|
||||
17. UAC2 function
|
||||
18. UVC function
|
||||
19. PRINTER function
|
||||
20. UAC1 function (new API)
|
||||
.. contents
|
||||
|
||||
1. ACM function
|
||||
2. ECM function
|
||||
3. ECM subset function
|
||||
4. EEM function
|
||||
5. FFS function
|
||||
6. HID function
|
||||
7. LOOPBACK function
|
||||
8. MASS STORAGE function
|
||||
9. MIDI function
|
||||
10. NCM function
|
||||
11. OBEX function
|
||||
12. PHONET function
|
||||
13. RNDIS function
|
||||
14. SERIAL function
|
||||
15. SOURCESINK function
|
||||
16. UAC1 function (legacy implementation)
|
||||
17. UAC2 function
|
||||
18. UVC function
|
||||
19. PRINTER function
|
||||
20. UAC1 function (new API)
|
||||
|
||||
|
||||
1. ACM function
|
||||
@ -44,13 +50,23 @@ There can be at most 4 ACM/generic serial/OBEX ports in the system.
|
||||
Testing the ACM function
|
||||
------------------------
|
||||
|
||||
On the host: cat > /dev/ttyACM<X>
|
||||
On the device : cat /dev/ttyGS<Y>
|
||||
On the host::
|
||||
|
||||
cat > /dev/ttyACM<X>
|
||||
|
||||
On the device::
|
||||
|
||||
cat /dev/ttyGS<Y>
|
||||
|
||||
then the other way round
|
||||
|
||||
On the device: cat > /dev/ttyGS<Y>
|
||||
On the host: cat /dev/ttyACM<X>
|
||||
On the device::
|
||||
|
||||
cat > /dev/ttyGS<Y>
|
||||
|
||||
On the host::
|
||||
|
||||
cat /dev/ttyACM<X>
|
||||
|
||||
2. ECM function
|
||||
===============
|
||||
@ -63,13 +79,15 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "ecm".
|
||||
The ECM function provides these attributes in its function directory:
|
||||
|
||||
ifname - network device interface name associated with this
|
||||
=============== ==================================================
|
||||
ifname network device interface name associated with this
|
||||
function instance
|
||||
qmult - queue length multiplier for high and super speed
|
||||
host_addr - MAC address of host's end of this
|
||||
qmult queue length multiplier for high and super speed
|
||||
host_addr MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr - MAC address of device's end of this
|
||||
dev_addr MAC address of device's end of this
|
||||
Ethernet over USB link
|
||||
=============== ==================================================
|
||||
|
||||
and after creating the functions/ecm.<instance name> they contain default
|
||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
||||
@ -82,8 +100,13 @@ Testing the ECM function
|
||||
|
||||
Configure IP addresses of the device and the host. Then:
|
||||
|
||||
On the device: ping <host's IP>
|
||||
On the host: ping <device's IP>
|
||||
On the device::
|
||||
|
||||
ping <host's IP>
|
||||
|
||||
On the host::
|
||||
|
||||
ping <device's IP>
|
||||
|
||||
3. ECM subset function
|
||||
======================
|
||||
@ -96,13 +119,15 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "geth".
|
||||
The ECM subset function provides these attributes in its function directory:
|
||||
|
||||
ifname - network device interface name associated with this
|
||||
=============== ==================================================
|
||||
ifname network device interface name associated with this
|
||||
function instance
|
||||
qmult - queue length multiplier for high and super speed
|
||||
host_addr - MAC address of host's end of this
|
||||
qmult queue length multiplier for high and super speed
|
||||
host_addr MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr - MAC address of device's end of this
|
||||
dev_addr MAC address of device's end of this
|
||||
Ethernet over USB link
|
||||
=============== ==================================================
|
||||
|
||||
and after creating the functions/ecm.<instance name> they contain default
|
||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
||||
@ -115,8 +140,13 @@ Testing the ECM subset function
|
||||
|
||||
Configure IP addresses of the device and the host. Then:
|
||||
|
||||
On the device: ping <host's IP>
|
||||
On the host: ping <device's IP>
|
||||
On the device::
|
||||
|
||||
ping <host's IP>
|
||||
|
||||
On the host::
|
||||
|
||||
ping <device's IP>
|
||||
|
||||
4. EEM function
|
||||
===============
|
||||
@ -129,13 +159,15 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "eem".
|
||||
The EEM function provides these attributes in its function directory:
|
||||
|
||||
ifname - network device interface name associated with this
|
||||
=============== ==================================================
|
||||
ifname network device interface name associated with this
|
||||
function instance
|
||||
qmult - queue length multiplier for high and super speed
|
||||
host_addr - MAC address of host's end of this
|
||||
qmult queue length multiplier for high and super speed
|
||||
host_addr MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr - MAC address of device's end of this
|
||||
dev_addr MAC address of device's end of this
|
||||
Ethernet over USB link
|
||||
=============== ==================================================
|
||||
|
||||
and after creating the functions/eem.<instance name> they contain default
|
||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
||||
@ -148,8 +180,13 @@ Testing the EEM function
|
||||
|
||||
Configure IP addresses of the device and the host. Then:
|
||||
|
||||
On the device: ping <host's IP>
|
||||
On the host: ping <device's IP>
|
||||
On the device::
|
||||
|
||||
ping <host's IP>
|
||||
|
||||
On the host::
|
||||
|
||||
ping <device's IP>
|
||||
|
||||
5. FFS function
|
||||
===============
|
||||
@ -172,6 +209,7 @@ Testing the FFS function
|
||||
------------------------
|
||||
|
||||
On the device: start the function's userspace daemon, enable the gadget
|
||||
|
||||
On the host: use the USB function provided by the device
|
||||
|
||||
6. HID function
|
||||
@ -185,39 +223,43 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "hid".
|
||||
The HID function provides these attributes in its function directory:
|
||||
|
||||
protocol - HID protocol to use
|
||||
report_desc - data to be used in HID reports, except data
|
||||
=============== ===========================================
|
||||
protocol HID protocol to use
|
||||
report_desc data to be used in HID reports, except data
|
||||
passed with /dev/hidg<X>
|
||||
report_length - HID report length
|
||||
subclass - HID subclass to use
|
||||
report_length HID report length
|
||||
subclass HID subclass to use
|
||||
=============== ===========================================
|
||||
|
||||
For a keyboard the protocol and the subclass are 1, the report_length is 8,
|
||||
while the report_desc is:
|
||||
while the report_desc is::
|
||||
|
||||
$ hd my_report_desc
|
||||
00000000 05 01 09 06 a1 01 05 07 19 e0 29 e7 15 00 25 01 |..........)...%.|
|
||||
00000010 75 01 95 08 81 02 95 01 75 08 81 03 95 05 75 01 |u.......u.....u.|
|
||||
00000020 05 08 19 01 29 05 91 02 95 01 75 03 91 03 95 06 |....).....u.....|
|
||||
00000030 75 08 15 00 25 65 05 07 19 00 29 65 81 00 c0 |u...%e....)e...|
|
||||
0000003f
|
||||
$ hd my_report_desc
|
||||
00000000 05 01 09 06 a1 01 05 07 19 e0 29 e7 15 00 25 01 |..........)...%.|
|
||||
00000010 75 01 95 08 81 02 95 01 75 08 81 03 95 05 75 01 |u.......u.....u.|
|
||||
00000020 05 08 19 01 29 05 91 02 95 01 75 03 91 03 95 06 |....).....u.....|
|
||||
00000030 75 08 15 00 25 65 05 07 19 00 29 65 81 00 c0 |u...%e....)e...|
|
||||
0000003f
|
||||
|
||||
Such a sequence of bytes can be stored to the attribute with echo:
|
||||
Such a sequence of bytes can be stored to the attribute with echo::
|
||||
|
||||
$ echo -ne \\x05\\x01\\x09\\x06\\xa1.....
|
||||
$ echo -ne \\x05\\x01\\x09\\x06\\xa1.....
|
||||
|
||||
Testing the HID function
|
||||
------------------------
|
||||
|
||||
Device:
|
||||
|
||||
- create the gadget
|
||||
- connect the gadget to a host, preferably not the one used
|
||||
to control the gadget
|
||||
to control the gadget
|
||||
- run a program which writes to /dev/hidg<N>, e.g.
|
||||
a userspace program found in Documentation/usb/gadget_hid.txt:
|
||||
a userspace program found in Documentation/usb/gadget_hid.txt::
|
||||
|
||||
$ ./hid_gadget_test /dev/hidg0 keyboard
|
||||
$ ./hid_gadget_test /dev/hidg0 keyboard
|
||||
|
||||
Host:
|
||||
|
||||
- observe the keystrokes from the gadget
|
||||
|
||||
7. LOOPBACK function
|
||||
@ -231,13 +273,16 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "Loopback".
|
||||
The LOOPBACK function provides these attributes in its function directory:
|
||||
|
||||
qlen - depth of loopback queue
|
||||
bulk_buflen - buffer length
|
||||
=============== =======================
|
||||
qlen depth of loopback queue
|
||||
bulk_buflen buffer length
|
||||
=============== =======================
|
||||
|
||||
Testing the LOOPBACK function
|
||||
-----------------------------
|
||||
|
||||
device: run the gadget
|
||||
|
||||
host: test-usb (tools/usb/testusb.c)
|
||||
|
||||
8. MASS STORAGE function
|
||||
@ -252,18 +297,20 @@ The function name to use when creating the function directory is "mass_storage".
|
||||
The MASS STORAGE function provides these attributes in its directory:
|
||||
files:
|
||||
|
||||
stall - Set to permit function to halt bulk endpoints.
|
||||
=============== ==============================================
|
||||
stall Set to permit function to halt bulk endpoints.
|
||||
Disabled on some USB devices known not to work
|
||||
correctly. You should set it to true.
|
||||
num_buffers - Number of pipeline buffers. Valid numbers
|
||||
num_buffers Number of pipeline buffers. Valid numbers
|
||||
are 2..4. Available only if
|
||||
CONFIG_USB_GADGET_DEBUG_FILES is set.
|
||||
=============== ==============================================
|
||||
|
||||
and a default lun.0 directory corresponding to SCSI LUN #0.
|
||||
|
||||
A new lun can be added with mkdir:
|
||||
A new lun can be added with mkdir::
|
||||
|
||||
$ mkdir functions/mass_storage.0/partition.5
|
||||
$ mkdir functions/mass_storage.0/partition.5
|
||||
|
||||
Lun numbering does not have to be continuous, except for lun #0 which is
|
||||
created by default. A maximum of 8 luns can be specified and they all must be
|
||||
@ -273,18 +320,20 @@ although it is not mandatory.
|
||||
|
||||
In each lun directory there are the following attribute files:
|
||||
|
||||
file - The path to the backing file for the LUN.
|
||||
=============== ==============================================
|
||||
file The path to the backing file for the LUN.
|
||||
Required if LUN is not marked as removable.
|
||||
ro - Flag specifying access to the LUN shall be
|
||||
ro Flag specifying access to the LUN shall be
|
||||
read-only. This is implied if CD-ROM emulation
|
||||
is enabled as well as when it was impossible
|
||||
to open "filename" in R/W mode.
|
||||
removable - Flag specifying that LUN shall be indicated as
|
||||
removable Flag specifying that LUN shall be indicated as
|
||||
being removable.
|
||||
cdrom - Flag specifying that LUN shall be reported as
|
||||
cdrom Flag specifying that LUN shall be reported as
|
||||
being a CD-ROM.
|
||||
nofua - Flag specifying that FUA flag
|
||||
nofua Flag specifying that FUA flag
|
||||
in SCSI WRITE(10,12)
|
||||
=============== ==============================================
|
||||
|
||||
Testing the MASS STORAGE function
|
||||
---------------------------------
|
||||
@ -304,12 +353,14 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "midi".
|
||||
The MIDI function provides these attributes in its function directory:
|
||||
|
||||
buflen - MIDI buffer length
|
||||
id - ID string for the USB MIDI adapter
|
||||
in_ports - number of MIDI input ports
|
||||
index - index value for the USB MIDI adapter
|
||||
out_ports - number of MIDI output ports
|
||||
qlen - USB read request queue length
|
||||
=============== ====================================
|
||||
buflen MIDI buffer length
|
||||
id ID string for the USB MIDI adapter
|
||||
in_ports number of MIDI input ports
|
||||
index index value for the USB MIDI adapter
|
||||
out_ports number of MIDI output ports
|
||||
qlen USB read request queue length
|
||||
=============== ====================================
|
||||
|
||||
Testing the MIDI function
|
||||
-------------------------
|
||||
@ -317,60 +368,63 @@ Testing the MIDI function
|
||||
There are two cases: playing a mid from the gadget to
|
||||
the host and playing a mid from the host to the gadget.
|
||||
|
||||
1) Playing a mid from the gadget to the host
|
||||
host)
|
||||
1) Playing a mid from the gadget to the host:
|
||||
|
||||
$ arecordmidi -l
|
||||
host::
|
||||
|
||||
$ arecordmidi -l
|
||||
Port Client name Port name
|
||||
14:0 Midi Through Midi Through Port-0
|
||||
24:0 MIDI Gadget MIDI Gadget MIDI 1
|
||||
$ arecordmidi -p 24:0 from_gadget.mid
|
||||
$ arecordmidi -p 24:0 from_gadget.mid
|
||||
|
||||
gadget)
|
||||
gadget::
|
||||
|
||||
$ aplaymidi -l
|
||||
$ aplaymidi -l
|
||||
Port Client name Port name
|
||||
20:0 f_midi f_midi
|
||||
|
||||
$ aplaymidi -p 20:0 to_host.mid
|
||||
$ aplaymidi -p 20:0 to_host.mid
|
||||
|
||||
2) Playing a mid from the host to the gadget
|
||||
gadget)
|
||||
|
||||
$ arecordmidi -l
|
||||
gadget::
|
||||
|
||||
$ arecordmidi -l
|
||||
Port Client name Port name
|
||||
20:0 f_midi f_midi
|
||||
|
||||
$ arecordmidi -p 20:0 from_host.mid
|
||||
$ arecordmidi -p 20:0 from_host.mid
|
||||
|
||||
host)
|
||||
host::
|
||||
|
||||
$ aplaymidi -l
|
||||
$ aplaymidi -l
|
||||
Port Client name Port name
|
||||
14:0 Midi Through Midi Through Port-0
|
||||
24:0 MIDI Gadget MIDI Gadget MIDI 1
|
||||
|
||||
$ aplaymidi -p24:0 to_gadget.mid
|
||||
$ aplaymidi -p24:0 to_gadget.mid
|
||||
|
||||
The from_gadget.mid should sound identical to the to_host.mid.
|
||||
|
||||
The from_host.id should sound identical to the to_gadget.mid.
|
||||
|
||||
MIDI files can be played to speakers/headphones with e.g. timidity installed
|
||||
MIDI files can be played to speakers/headphones with e.g. timidity installed::
|
||||
|
||||
$ aplaymidi -l
|
||||
$ aplaymidi -l
|
||||
Port Client name Port name
|
||||
14:0 Midi Through Midi Through Port-0
|
||||
24:0 MIDI Gadget MIDI Gadget MIDI 1
|
||||
128:0 TiMidity TiMidity port 0
|
||||
128:1 TiMidity TiMidity port 1
|
||||
128:2 TiMidity TiMidity port 2
|
||||
128:3 TiMidity TiMidity port 3
|
||||
128:0 TiMidity TiMidity port 0
|
||||
128:1 TiMidity TiMidity port 1
|
||||
128:2 TiMidity TiMidity port 2
|
||||
128:3 TiMidity TiMidity port 3
|
||||
|
||||
$ aplaymidi -p 128:0 file.mid
|
||||
$ aplaymidi -p 128:0 file.mid
|
||||
|
||||
MIDI ports can be logically connected using the aconnect utility, e.g.:
|
||||
MIDI ports can be logically connected using the aconnect utility, e.g.::
|
||||
|
||||
$ aconnect 24:0 128:0 # try it on the host
|
||||
$ aconnect 24:0 128:0 # try it on the host
|
||||
|
||||
After the gadget's MIDI port is connected to timidity's MIDI port,
|
||||
whatever is played at the gadget side with aplaymidi -l is audible
|
||||
@ -387,13 +441,15 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "ncm".
|
||||
The NCM function provides these attributes in its function directory:
|
||||
|
||||
ifname - network device interface name associated with this
|
||||
=============== ==================================================
|
||||
ifname network device interface name associated with this
|
||||
function instance
|
||||
qmult - queue length multiplier for high and super speed
|
||||
host_addr - MAC address of host's end of this
|
||||
qmult queue length multiplier for high and super speed
|
||||
host_addr MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr - MAC address of device's end of this
|
||||
dev_addr MAC address of device's end of this
|
||||
Ethernet over USB link
|
||||
=============== ==================================================
|
||||
|
||||
and after creating the functions/ncm.<instance name> they contain default
|
||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
||||
@ -406,8 +462,13 @@ Testing the NCM function
|
||||
|
||||
Configure IP addresses of the device and the host. Then:
|
||||
|
||||
On the device: ping <host's IP>
|
||||
On the host: ping <device's IP>
|
||||
On the device::
|
||||
|
||||
ping <host's IP>
|
||||
|
||||
On the host::
|
||||
|
||||
ping <device's IP>
|
||||
|
||||
11. OBEX function
|
||||
=================
|
||||
@ -429,13 +490,18 @@ There can be at most 4 ACM/generic serial/OBEX ports in the system.
|
||||
Testing the OBEX function
|
||||
-------------------------
|
||||
|
||||
On device: seriald -f /dev/ttyGS<Y> -s 1024
|
||||
On host: serialc -v <vendorID> -p <productID> -i<interface#> -a1 -s1024 \
|
||||
On device::
|
||||
|
||||
seriald -f /dev/ttyGS<Y> -s 1024
|
||||
|
||||
On host::
|
||||
|
||||
serialc -v <vendorID> -p <productID> -i<interface#> -a1 -s1024 \
|
||||
-t<out endpoint addr> -r<in endpoint addr>
|
||||
|
||||
where seriald and serialc are Felipe's utilities found here:
|
||||
|
||||
https://github.com/felipebalbi/usb-tools.git master
|
||||
https://github.com/felipebalbi/usb-tools.git master
|
||||
|
||||
12. PHONET function
|
||||
===================
|
||||
@ -448,8 +514,10 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "phonet".
|
||||
The PHONET function provides just one attribute in its function directory:
|
||||
|
||||
ifname - network device interface name associated with this
|
||||
=============== ==================================================
|
||||
ifname network device interface name associated with this
|
||||
function instance
|
||||
=============== ==================================================
|
||||
|
||||
Testing the PHONET function
|
||||
---------------------------
|
||||
@ -464,41 +532,41 @@ These tools are required:
|
||||
|
||||
git://git.gitorious.org/meego-cellular/phonet-utils.git
|
||||
|
||||
On the host:
|
||||
On the host::
|
||||
|
||||
$ ./phonet -a 0x10 -i usbpn0
|
||||
$ ./pnroute add 0x6c usbpn0
|
||||
$./pnroute add 0x10 usbpn0
|
||||
$ ifconfig usbpn0 up
|
||||
$ ./phonet -a 0x10 -i usbpn0
|
||||
$ ./pnroute add 0x6c usbpn0
|
||||
$./pnroute add 0x10 usbpn0
|
||||
$ ifconfig usbpn0 up
|
||||
|
||||
On the device:
|
||||
On the device::
|
||||
|
||||
$ ./phonet -a 0x6c -i upnlink0
|
||||
$ ./pnroute add 0x10 upnlink0
|
||||
$ ifconfig upnlink0 up
|
||||
$ ./phonet -a 0x6c -i upnlink0
|
||||
$ ./pnroute add 0x10 upnlink0
|
||||
$ ifconfig upnlink0 up
|
||||
|
||||
Then a test program can be used:
|
||||
Then a test program can be used::
|
||||
|
||||
http://www.spinics.net/lists/linux-usb/msg85690.html
|
||||
http://www.spinics.net/lists/linux-usb/msg85690.html
|
||||
|
||||
On the device:
|
||||
On the device::
|
||||
|
||||
$ ./pnxmit -a 0x6c -r
|
||||
$ ./pnxmit -a 0x6c -r
|
||||
|
||||
On the host:
|
||||
On the host::
|
||||
|
||||
$ ./pnxmit -a 0x10 -s 0x6c
|
||||
$ ./pnxmit -a 0x10 -s 0x6c
|
||||
|
||||
As a result some data should be sent from host to device.
|
||||
Then the other way round:
|
||||
|
||||
On the host:
|
||||
On the host::
|
||||
|
||||
$ ./pnxmit -a 0x10 -r
|
||||
$ ./pnxmit -a 0x10 -r
|
||||
|
||||
On the device:
|
||||
On the device::
|
||||
|
||||
$ ./pnxmit -a 0x6c -s 0x10
|
||||
$ ./pnxmit -a 0x6c -s 0x10
|
||||
|
||||
13. RNDIS function
|
||||
==================
|
||||
@ -511,13 +579,15 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "rndis".
|
||||
The RNDIS function provides these attributes in its function directory:
|
||||
|
||||
ifname - network device interface name associated with this
|
||||
=============== ==================================================
|
||||
ifname network device interface name associated with this
|
||||
function instance
|
||||
qmult - queue length multiplier for high and super speed
|
||||
host_addr - MAC address of host's end of this
|
||||
qmult queue length multiplier for high and super speed
|
||||
host_addr MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr - MAC address of device's end of this
|
||||
dev_addr MAC address of device's end of this
|
||||
Ethernet over USB link
|
||||
=============== ==================================================
|
||||
|
||||
and after creating the functions/rndis.<instance name> they contain default
|
||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
||||
@ -530,8 +600,13 @@ Testing the RNDIS function
|
||||
|
||||
Configure IP addresses of the device and the host. Then:
|
||||
|
||||
On the device: ping <host's IP>
|
||||
On the host: ping <device's IP>
|
||||
On the device::
|
||||
|
||||
ping <host's IP>
|
||||
|
||||
On the host::
|
||||
|
||||
ping <device's IP>
|
||||
|
||||
14. SERIAL function
|
||||
===================
|
||||
@ -553,15 +628,28 @@ There can be at most 4 ACM/generic serial/OBEX ports in the system.
|
||||
Testing the SERIAL function
|
||||
---------------------------
|
||||
|
||||
On host: insmod usbserial
|
||||
On host::
|
||||
|
||||
insmod usbserial
|
||||
echo VID PID >/sys/bus/usb-serial/drivers/generic/new_id
|
||||
On host: cat > /dev/ttyUSB<X>
|
||||
On target: cat /dev/ttyGS<Y>
|
||||
|
||||
On host::
|
||||
|
||||
cat > /dev/ttyUSB<X>
|
||||
|
||||
On target::
|
||||
|
||||
cat /dev/ttyGS<Y>
|
||||
|
||||
then the other way round
|
||||
|
||||
On target: cat > /dev/ttyGS<Y>
|
||||
On host: cat /dev/ttyUSB<X>
|
||||
On target::
|
||||
|
||||
cat > /dev/ttyGS<Y>
|
||||
|
||||
On host::
|
||||
|
||||
cat /dev/ttyUSB<X>
|
||||
|
||||
15. SOURCESINK function
|
||||
=======================
|
||||
@ -574,24 +662,27 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "SourceSink".
|
||||
The SOURCESINK function provides these attributes in its function directory:
|
||||
|
||||
pattern - 0 (all zeros), 1 (mod63), 2 (none)
|
||||
isoc_interval - 1..16
|
||||
isoc_maxpacket - 0 - 1023 (fs), 0 - 1024 (hs/ss)
|
||||
isoc_mult - 0..2 (hs/ss only)
|
||||
isoc_maxburst - 0..15 (ss only)
|
||||
bulk_buflen - buffer length
|
||||
bulk_qlen - depth of queue for bulk
|
||||
iso_qlen - depth of queue for iso
|
||||
=============== ==================================
|
||||
pattern 0 (all zeros), 1 (mod63), 2 (none)
|
||||
isoc_interval 1..16
|
||||
isoc_maxpacket 0 - 1023 (fs), 0 - 1024 (hs/ss)
|
||||
isoc_mult 0..2 (hs/ss only)
|
||||
isoc_maxburst 0..15 (ss only)
|
||||
bulk_buflen buffer length
|
||||
bulk_qlen depth of queue for bulk
|
||||
iso_qlen depth of queue for iso
|
||||
=============== ==================================
|
||||
|
||||
Testing the SOURCESINK function
|
||||
-------------------------------
|
||||
|
||||
device: run the gadget
|
||||
|
||||
host: test-usb (tools/usb/testusb.c)
|
||||
|
||||
|
||||
16. UAC1 function (legacy implementation)
|
||||
=================
|
||||
=========================================
|
||||
|
||||
The function is provided by usb_f_uac1_legacy.ko module.
|
||||
|
||||
@ -602,12 +693,14 @@ The function name to use when creating the function directory
|
||||
is "uac1_legacy".
|
||||
The uac1 function provides these attributes in its function directory:
|
||||
|
||||
audio_buf_size - audio buffer size
|
||||
fn_cap - capture pcm device file name
|
||||
fn_cntl - control device file name
|
||||
fn_play - playback pcm device file name
|
||||
req_buf_size - ISO OUT endpoint request buffer size
|
||||
req_count - ISO OUT endpoint request count
|
||||
=============== ====================================
|
||||
audio_buf_size audio buffer size
|
||||
fn_cap capture pcm device file name
|
||||
fn_cntl control device file name
|
||||
fn_play playback pcm device file name
|
||||
req_buf_size ISO OUT endpoint request buffer size
|
||||
req_count ISO OUT endpoint request count
|
||||
=============== ====================================
|
||||
|
||||
The attributes have sane default values.
|
||||
|
||||
@ -615,7 +708,10 @@ Testing the UAC1 function
|
||||
-------------------------
|
||||
|
||||
device: run the gadget
|
||||
host: aplay -l # should list our USB Audio Gadget
|
||||
|
||||
host::
|
||||
|
||||
aplay -l # should list our USB Audio Gadget
|
||||
|
||||
17. UAC2 function
|
||||
=================
|
||||
@ -628,14 +724,16 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "uac2".
|
||||
The uac2 function provides these attributes in its function directory:
|
||||
|
||||
c_chmask - capture channel mask
|
||||
c_srate - capture sampling rate
|
||||
c_ssize - capture sample size (bytes)
|
||||
p_chmask - playback channel mask
|
||||
p_srate - playback sampling rate
|
||||
p_ssize - playback sample size (bytes)
|
||||
req_number - the number of pre-allocated request for both capture
|
||||
=============== ====================================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_ssize capture sample size (bytes)
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_ssize playback sample size (bytes)
|
||||
req_number the number of pre-allocated request for both capture
|
||||
and playback
|
||||
=============== ====================================================
|
||||
|
||||
The attributes have sane default values.
|
||||
|
||||
@ -648,14 +746,14 @@ host: aplay -l # should list our USB Audio Gadget
|
||||
This function does not require real hardware support, it just
|
||||
sends a stream of audio data to/from the host. In order to
|
||||
actually hear something at the device side, a command similar
|
||||
to this must be used at the device side:
|
||||
to this must be used at the device side::
|
||||
|
||||
$ arecord -f dat -t wav -D hw:2,0 | aplay -D hw:0,0 &
|
||||
$ arecord -f dat -t wav -D hw:2,0 | aplay -D hw:0,0 &
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ arecord -f dat -t wav -D hw:CARD=UAC2Gadget,DEV=0 | \
|
||||
aplay -D default:CARD=OdroidU3
|
||||
$ arecord -f dat -t wav -D hw:CARD=UAC2Gadget,DEV=0 | \
|
||||
aplay -D default:CARD=OdroidU3
|
||||
|
||||
18. UVC function
|
||||
================
|
||||
@ -668,66 +766,73 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "uvc".
|
||||
The uvc function provides these attributes in its function directory:
|
||||
|
||||
streaming_interval - interval for polling endpoint for data transfers
|
||||
streaming_maxburst - bMaxBurst for super speed companion descriptor
|
||||
streaming_maxpacket - maximum packet size this endpoint is capable of
|
||||
=================== ================================================
|
||||
streaming_interval interval for polling endpoint for data transfers
|
||||
streaming_maxburst bMaxBurst for super speed companion descriptor
|
||||
streaming_maxpacket maximum packet size this endpoint is capable of
|
||||
sending or receiving when this configuration is
|
||||
selected
|
||||
=================== ================================================
|
||||
|
||||
There are also "control" and "streaming" subdirectories, each of which contain
|
||||
a number of their subdirectories. There are some sane defaults provided, but
|
||||
the user must provide the following:
|
||||
|
||||
control header - create in control/header, link from control/class/fs
|
||||
================== ====================================================
|
||||
control header create in control/header, link from control/class/fs
|
||||
and/or control/class/ss
|
||||
streaming header - create in streaming/header, link from
|
||||
streaming header create in streaming/header, link from
|
||||
streaming/class/fs and/or streaming/class/hs and/or
|
||||
streaming/class/ss
|
||||
format description - create in streaming/mjpeg and/or
|
||||
format description create in streaming/mjpeg and/or
|
||||
streaming/uncompressed
|
||||
frame description - create in streaming/mjpeg/<format> and/or in
|
||||
frame description create in streaming/mjpeg/<format> and/or in
|
||||
streaming/uncompressed/<format>
|
||||
================== ====================================================
|
||||
|
||||
Each frame description contains frame interval specification, and each
|
||||
such specification consists of a number of lines with an inverval value
|
||||
in each line. The rules stated above are best illustrated with an example:
|
||||
in each line. The rules stated above are best illustrated with an example::
|
||||
|
||||
# mkdir functions/uvc.usb0/control/header/h
|
||||
# cd functions/uvc.usb0/control/
|
||||
# ln -s header/h class/fs
|
||||
# ln -s header/h class/ss
|
||||
# mkdir -p functions/uvc.usb0/streaming/uncompressed/u/360p
|
||||
# cat <<EOF > functions/uvc.usb0/streaming/uncompressed/u/360p/dwFrameInterval
|
||||
666666
|
||||
1000000
|
||||
5000000
|
||||
EOF
|
||||
# cd $GADGET_CONFIGFS_ROOT
|
||||
# mkdir functions/uvc.usb0/streaming/header/h
|
||||
# cd functions/uvc.usb0/streaming/header/h
|
||||
# ln -s ../../uncompressed/u
|
||||
# cd ../../class/fs
|
||||
# ln -s ../../header/h
|
||||
# cd ../../class/hs
|
||||
# ln -s ../../header/h
|
||||
# cd ../../class/ss
|
||||
# ln -s ../../header/h
|
||||
# mkdir functions/uvc.usb0/control/header/h
|
||||
# cd functions/uvc.usb0/control/
|
||||
# ln -s header/h class/fs
|
||||
# ln -s header/h class/ss
|
||||
# mkdir -p functions/uvc.usb0/streaming/uncompressed/u/360p
|
||||
# cat <<EOF > functions/uvc.usb0/streaming/uncompressed/u/360p/dwFrameInterval
|
||||
666666
|
||||
1000000
|
||||
5000000
|
||||
EOF
|
||||
# cd $GADGET_CONFIGFS_ROOT
|
||||
# mkdir functions/uvc.usb0/streaming/header/h
|
||||
# cd functions/uvc.usb0/streaming/header/h
|
||||
# ln -s ../../uncompressed/u
|
||||
# cd ../../class/fs
|
||||
# ln -s ../../header/h
|
||||
# cd ../../class/hs
|
||||
# ln -s ../../header/h
|
||||
# cd ../../class/ss
|
||||
# ln -s ../../header/h
|
||||
|
||||
|
||||
Testing the UVC function
|
||||
------------------------
|
||||
|
||||
device: run the gadget, modprobe vivid
|
||||
device: run the gadget, modprobe vivid::
|
||||
|
||||
# uvc-gadget -u /dev/video<uvc video node #> -v /dev/video<vivid video node #>
|
||||
# uvc-gadget -u /dev/video<uvc video node #> -v /dev/video<vivid video node #>
|
||||
|
||||
where uvc-gadget is this program:
|
||||
http://git.ideasonboard.org/uvc-gadget.git
|
||||
http://git.ideasonboard.org/uvc-gadget.git
|
||||
|
||||
with these patches:
|
||||
http://www.spinics.net/lists/linux-usb/msg99220.html
|
||||
|
||||
host: luvcview -f yuv
|
||||
http://www.spinics.net/lists/linux-usb/msg99220.html
|
||||
|
||||
host::
|
||||
|
||||
luvcview -f yuv
|
||||
|
||||
19. PRINTER function
|
||||
====================
|
||||
@ -740,16 +845,19 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "printer".
|
||||
The printer function provides these attributes in its function directory:
|
||||
|
||||
pnp_string - Data to be passed to the host in pnp string
|
||||
q_len - Number of requests per endpoint
|
||||
========== ===========================================
|
||||
pnp_string Data to be passed to the host in pnp string
|
||||
q_len Number of requests per endpoint
|
||||
========== ===========================================
|
||||
|
||||
Testing the PRINTER function
|
||||
----------------------------
|
||||
|
||||
The most basic testing:
|
||||
|
||||
device: run the gadget
|
||||
# ls -l /devices/virtual/usb_printer_gadget/
|
||||
device: run the gadget::
|
||||
|
||||
# ls -l /devices/virtual/usb_printer_gadget/
|
||||
|
||||
should show g_printer<number>.
|
||||
|
||||
@ -761,23 +869,28 @@ If udev is active, then e.g. /dev/usb/lp0 should appear.
|
||||
|
||||
host->device transmission:
|
||||
|
||||
device:
|
||||
# cat /dev/g_printer<number>
|
||||
host:
|
||||
# cat > /dev/usb/lp0
|
||||
device::
|
||||
|
||||
device->host transmission:
|
||||
# cat /dev/g_printer<number>
|
||||
|
||||
# cat > /dev/g_printer<number>
|
||||
host:
|
||||
# cat /dev/usb/lp0
|
||||
host::
|
||||
|
||||
# cat > /dev/usb/lp0
|
||||
|
||||
device->host transmission::
|
||||
|
||||
# cat > /dev/g_printer<number>
|
||||
|
||||
host::
|
||||
|
||||
# cat /dev/usb/lp0
|
||||
|
||||
More advanced testing can be done with the prn_example
|
||||
described in Documentation/usb/gadget_printer.txt.
|
||||
|
||||
|
||||
20. UAC1 function (virtual ALSA card, using u_audio API)
|
||||
=================
|
||||
========================================================
|
||||
|
||||
The function is provided by usb_f_uac1.ko module.
|
||||
It will create a virtual ALSA card and the audio streams are simply
|
||||
@ -789,14 +902,16 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "uac1".
|
||||
The uac1 function provides these attributes in its function directory:
|
||||
|
||||
c_chmask - capture channel mask
|
||||
c_srate - capture sampling rate
|
||||
c_ssize - capture sample size (bytes)
|
||||
p_chmask - playback channel mask
|
||||
p_srate - playback sampling rate
|
||||
p_ssize - playback sample size (bytes)
|
||||
req_number - the number of pre-allocated request for both capture
|
||||
========== ====================================================
|
||||
c_chmask capture channel mask
|
||||
c_srate capture sampling rate
|
||||
c_ssize capture sample size (bytes)
|
||||
p_chmask playback channel mask
|
||||
p_srate playback sampling rate
|
||||
p_ssize playback sample size (bytes)
|
||||
req_number the number of pre-allocated request for both capture
|
||||
and playback
|
||||
========== ====================================================
|
||||
|
||||
The attributes have sane default values.
|
||||
|
||||
@ -809,11 +924,11 @@ host: aplay -l # should list our USB Audio Gadget
|
||||
This function does not require real hardware support, it just
|
||||
sends a stream of audio data to/from the host. In order to
|
||||
actually hear something at the device side, a command similar
|
||||
to this must be used at the device side:
|
||||
to this must be used at the device side::
|
||||
|
||||
$ arecord -f dat -t wav -D hw:2,0 | aplay -D hw:0,0 &
|
||||
$ arecord -f dat -t wav -D hw:2,0 | aplay -D hw:0,0 &
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ arecord -f dat -t wav -D hw:CARD=UAC1Gadget,DEV=0 | \
|
||||
aplay -D default:CARD=OdroidU3
|
||||
$ arecord -f dat -t wav -D hw:CARD=UAC1Gadget,DEV=0 | \
|
||||
aplay -D default:CARD=OdroidU3
|
||||
|
@ -1,11 +1,9 @@
|
||||
============================================
|
||||
Linux USB gadget configured through configfs
|
||||
============================================
|
||||
|
||||
|
||||
|
||||
|
||||
Linux USB gadget configured through configfs
|
||||
|
||||
|
||||
25th April 2013
|
||||
25th April 2013
|
||||
|
||||
|
||||
|
||||
@ -26,7 +24,7 @@ Linux provides a number of functions for gadgets to use.
|
||||
Creating a gadget means deciding what configurations there will be
|
||||
and which functions each configuration will provide.
|
||||
|
||||
Configfs (please see Documentation/filesystems/configfs/*) lends itself nicely
|
||||
Configfs (please see `Documentation/filesystems/configfs/*`) lends itself nicely
|
||||
for the purpose of telling the kernel about the above mentioned decision.
|
||||
This document is about how to do it.
|
||||
|
||||
@ -51,44 +49,46 @@ Usage
|
||||
made available through configfs can be seen here:
|
||||
http://www.spinics.net/lists/linux-usb/msg76388.html)
|
||||
|
||||
$ modprobe libcomposite
|
||||
$ mount none $CONFIGFS_HOME -t configfs
|
||||
::
|
||||
|
||||
$ modprobe libcomposite
|
||||
$ mount none $CONFIGFS_HOME -t configfs
|
||||
|
||||
where CONFIGFS_HOME is the mount point for configfs
|
||||
|
||||
1. Creating the gadgets
|
||||
-----------------------
|
||||
|
||||
For each gadget to be created its corresponding directory must be created:
|
||||
For each gadget to be created its corresponding directory must be created::
|
||||
|
||||
$ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>
|
||||
$ mkdir $CONFIGFS_HOME/usb_gadget/<gadget name>
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ mkdir $CONFIGFS_HOME/usb_gadget/g1
|
||||
$ mkdir $CONFIGFS_HOME/usb_gadget/g1
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
$ cd $CONFIGFS_HOME/usb_gadget/g1
|
||||
$ cd $CONFIGFS_HOME/usb_gadget/g1
|
||||
|
||||
Each gadget needs to have its vendor id <VID> and product id <PID> specified:
|
||||
Each gadget needs to have its vendor id <VID> and product id <PID> specified::
|
||||
|
||||
$ echo <VID> > idVendor
|
||||
$ echo <PID> > idProduct
|
||||
$ echo <VID> > idVendor
|
||||
$ echo <PID> > idProduct
|
||||
|
||||
A gadget also needs its serial number, manufacturer and product strings.
|
||||
In order to have a place to store them, a strings subdirectory must be created
|
||||
for each language, e.g.:
|
||||
for each language, e.g.::
|
||||
|
||||
$ mkdir strings/0x409
|
||||
$ mkdir strings/0x409
|
||||
|
||||
Then the strings can be specified:
|
||||
Then the strings can be specified::
|
||||
|
||||
$ echo <serial number> > strings/0x409/serialnumber
|
||||
$ echo <manufacturer> > strings/0x409/manufacturer
|
||||
$ echo <product> > strings/0x409/product
|
||||
$ echo <serial number> > strings/0x409/serialnumber
|
||||
$ echo <manufacturer> > strings/0x409/manufacturer
|
||||
$ echo <product> > strings/0x409/product
|
||||
|
||||
2. Creating the configurations
|
||||
------------------------------
|
||||
@ -99,43 +99,43 @@ directories must be created:
|
||||
$ mkdir configs/<name>.<number>
|
||||
|
||||
where <name> can be any string which is legal in a filesystem and the
|
||||
<number> is the configuration's number, e.g.:
|
||||
<number> is the configuration's number, e.g.::
|
||||
|
||||
$ mkdir configs/c.1
|
||||
$ mkdir configs/c.1
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
Each configuration also needs its strings, so a subdirectory must be created
|
||||
for each language, e.g.:
|
||||
for each language, e.g.::
|
||||
|
||||
$ mkdir configs/c.1/strings/0x409
|
||||
$ mkdir configs/c.1/strings/0x409
|
||||
|
||||
Then the configuration string can be specified:
|
||||
Then the configuration string can be specified::
|
||||
|
||||
$ echo <configuration> > configs/c.1/strings/0x409/configuration
|
||||
$ echo <configuration> > configs/c.1/strings/0x409/configuration
|
||||
|
||||
Some attributes can also be set for a configuration, e.g.:
|
||||
Some attributes can also be set for a configuration, e.g.::
|
||||
|
||||
$ echo 120 > configs/c.1/MaxPower
|
||||
$ echo 120 > configs/c.1/MaxPower
|
||||
|
||||
3. Creating the functions
|
||||
-------------------------
|
||||
|
||||
The gadget will provide some functions, for each function its corresponding
|
||||
directory must be created:
|
||||
directory must be created::
|
||||
|
||||
$ mkdir functions/<name>.<instance name>
|
||||
$ mkdir functions/<name>.<instance name>
|
||||
|
||||
where <name> corresponds to one of allowed function names and instance name
|
||||
is an arbitrary string allowed in a filesystem, e.g.:
|
||||
is an arbitrary string allowed in a filesystem, e.g.::
|
||||
|
||||
$ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
|
||||
$ mkdir functions/ncm.usb0 # usb_f_ncm.ko gets loaded with request_module()
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
Each function provides its specific set of attributes, with either read-only
|
||||
or read-write access. Where applicable they need to be written to as
|
||||
@ -149,17 +149,17 @@ At this moment a number of gadgets is created, each of which has a number of
|
||||
configurations specified and a number of functions available. What remains
|
||||
is specifying which function is available in which configuration (the same
|
||||
function can be used in multiple configurations). This is achieved with
|
||||
creating symbolic links:
|
||||
creating symbolic links::
|
||||
|
||||
$ ln -s functions/<name>.<instance name> configs/<name>.<number>
|
||||
$ ln -s functions/<name>.<instance name> configs/<name>.<number>
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ ln -s functions/ncm.usb0 configs/c.1
|
||||
$ ln -s functions/ncm.usb0 configs/c.1
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
5. Enabling the gadget
|
||||
----------------------
|
||||
@ -167,123 +167,127 @@ $ ln -s functions/ncm.usb0 configs/c.1
|
||||
All the above steps serve the purpose of composing the gadget of
|
||||
configurations and functions.
|
||||
|
||||
An example directory structure might look like this:
|
||||
An example directory structure might look like this::
|
||||
|
||||
.
|
||||
./strings
|
||||
./strings/0x409
|
||||
./strings/0x409/serialnumber
|
||||
./strings/0x409/product
|
||||
./strings/0x409/manufacturer
|
||||
./configs
|
||||
./configs/c.1
|
||||
./configs/c.1/ncm.usb0 -> ../../../../usb_gadget/g1/functions/ncm.usb0
|
||||
./configs/c.1/strings
|
||||
./configs/c.1/strings/0x409
|
||||
./configs/c.1/strings/0x409/configuration
|
||||
./configs/c.1/bmAttributes
|
||||
./configs/c.1/MaxPower
|
||||
./functions
|
||||
./functions/ncm.usb0
|
||||
./functions/ncm.usb0/ifname
|
||||
./functions/ncm.usb0/qmult
|
||||
./functions/ncm.usb0/host_addr
|
||||
./functions/ncm.usb0/dev_addr
|
||||
./UDC
|
||||
./bcdUSB
|
||||
./bcdDevice
|
||||
./idProduct
|
||||
./idVendor
|
||||
./bMaxPacketSize0
|
||||
./bDeviceProtocol
|
||||
./bDeviceSubClass
|
||||
./bDeviceClass
|
||||
.
|
||||
./strings
|
||||
./strings/0x409
|
||||
./strings/0x409/serialnumber
|
||||
./strings/0x409/product
|
||||
./strings/0x409/manufacturer
|
||||
./configs
|
||||
./configs/c.1
|
||||
./configs/c.1/ncm.usb0 -> ../../../../usb_gadget/g1/functions/ncm.usb0
|
||||
./configs/c.1/strings
|
||||
./configs/c.1/strings/0x409
|
||||
./configs/c.1/strings/0x409/configuration
|
||||
./configs/c.1/bmAttributes
|
||||
./configs/c.1/MaxPower
|
||||
./functions
|
||||
./functions/ncm.usb0
|
||||
./functions/ncm.usb0/ifname
|
||||
./functions/ncm.usb0/qmult
|
||||
./functions/ncm.usb0/host_addr
|
||||
./functions/ncm.usb0/dev_addr
|
||||
./UDC
|
||||
./bcdUSB
|
||||
./bcdDevice
|
||||
./idProduct
|
||||
./idVendor
|
||||
./bMaxPacketSize0
|
||||
./bDeviceProtocol
|
||||
./bDeviceSubClass
|
||||
./bDeviceClass
|
||||
|
||||
|
||||
Such a gadget must be finally enabled so that the USB host can enumerate it.
|
||||
In order to enable the gadget it must be bound to a UDC (USB Device Controller).
|
||||
|
||||
$ echo <udc name> > UDC
|
||||
In order to enable the gadget it must be bound to a UDC (USB Device
|
||||
Controller)::
|
||||
|
||||
$ echo <udc name> > UDC
|
||||
|
||||
where <udc name> is one of those found in /sys/class/udc/*
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ echo s3c-hsotg > UDC
|
||||
$ echo s3c-hsotg > UDC
|
||||
|
||||
|
||||
6. Disabling the gadget
|
||||
-----------------------
|
||||
|
||||
$ echo "" > UDC
|
||||
::
|
||||
|
||||
$ echo "" > UDC
|
||||
|
||||
7. Cleaning up
|
||||
--------------
|
||||
|
||||
Remove functions from configurations:
|
||||
Remove functions from configurations::
|
||||
|
||||
$ rm configs/<config name>.<number>/<function>
|
||||
$ rm configs/<config name>.<number>/<function>
|
||||
|
||||
where <config name>.<number> specify the configuration and <function> is
|
||||
a symlink to a function being removed from the configuration, e.g.:
|
||||
a symlink to a function being removed from the configuration, e.g.::
|
||||
|
||||
$ rm configs/c.1/ncm.usb0
|
||||
$ rm configs/c.1/ncm.usb0
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
Remove strings directories in configurations
|
||||
Remove strings directories in configurations:
|
||||
|
||||
$ rmdir configs/<config name>.<number>/strings/<lang>
|
||||
$ rmdir configs/<config name>.<number>/strings/<lang>
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ rmdir configs/c.1/strings/0x409
|
||||
$ rmdir configs/c.1/strings/0x409
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
and remove the configurations
|
||||
and remove the configurations::
|
||||
|
||||
$ rmdir configs/<config name>.<number>
|
||||
$ rmdir configs/<config name>.<number>
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
rmdir configs/c.1
|
||||
rmdir configs/c.1
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
Remove functions (function modules are not unloaded, though)
|
||||
Remove functions (function modules are not unloaded, though):
|
||||
|
||||
$ rmdir functions/<name>.<instance name>
|
||||
$ rmdir functions/<name>.<instance name>
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ rmdir functions/ncm.usb0
|
||||
$ rmdir functions/ncm.usb0
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
Remove strings directories in the gadget
|
||||
Remove strings directories in the gadget::
|
||||
|
||||
$ rmdir strings/<lang>
|
||||
$ rmdir strings/<lang>
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ rmdir strings/0x409
|
||||
$ rmdir strings/0x409
|
||||
|
||||
and finally remove the gadget:
|
||||
and finally remove the gadget::
|
||||
|
||||
$ cd ..
|
||||
$ rmdir <gadget name>
|
||||
$ cd ..
|
||||
$ rmdir <gadget name>
|
||||
|
||||
e.g.:
|
||||
e.g.::
|
||||
|
||||
$ rmdir g1
|
||||
$ rmdir g1
|
||||
|
||||
|
||||
|
||||
@ -305,10 +309,10 @@ configured elements. However, they are embedded in usage-specific
|
||||
larger structures. In the picture below there is a "cs" which contains
|
||||
a config_item and an "sa" which contains a configfs_attribute.
|
||||
|
||||
The filesystem view would be like this:
|
||||
The filesystem view would be like this::
|
||||
|
||||
./
|
||||
./cs (directory)
|
||||
./
|
||||
./cs (directory)
|
||||
|
|
||||
+--sa (file)
|
||||
|
|
||||
@ -326,29 +330,31 @@ buffer), while the "store" is for modifying the file's contents (copy data
|
||||
from the buffer to the cs), but it is up to the implementer of the
|
||||
two functions to decide what they actually do.
|
||||
|
||||
typedef struct configured_structure cs;
|
||||
typedef struct specific_attribute sa;
|
||||
::
|
||||
|
||||
typedef struct configured_structure cs;
|
||||
typedef struct specific_attribute sa;
|
||||
|
||||
sa
|
||||
+----------------------------------+
|
||||
cs | (*show)(cs *, buffer); |
|
||||
+-----------------+ | (*store)(cs *, buffer, length); |
|
||||
| | | |
|
||||
| +-------------+ | | +------------------+ |
|
||||
| | struct |-|----|------>|struct | |
|
||||
| | config_item | | | |configfs_attribute| |
|
||||
| +-------------+ | | +------------------+ |
|
||||
| | +----------------------------------+
|
||||
| data to be set | .
|
||||
| | .
|
||||
+-----------------+ .
|
||||
+-----------------+ | (*store)(cs *, buffer, length); |
|
||||
| | | |
|
||||
| +-------------+ | | +------------------+ |
|
||||
| | struct |-|----|------>|struct | |
|
||||
| | config_item | | | |configfs_attribute| |
|
||||
| +-------------+ | | +------------------+ |
|
||||
| | +----------------------------------+
|
||||
| data to be set | .
|
||||
| | .
|
||||
+-----------------+ .
|
||||
|
||||
The file names are decided by the config item/group designer, while
|
||||
the directories in general can be named at will. A group can have
|
||||
a number of its default sub-groups created automatically.
|
||||
|
||||
For more information on configfs please see
|
||||
Documentation/filesystems/configfs/*.
|
||||
`Documentation/filesystems/configfs/*`.
|
||||
|
||||
The concepts described above translate to USB gadgets like this:
|
||||
|
||||
|
@ -1,28 +1,31 @@
|
||||
|
||||
Linux USB HID gadget driver
|
||||
===========================
|
||||
Linux USB HID gadget driver
|
||||
===========================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The HID Gadget driver provides emulation of USB Human Interface
|
||||
Devices (HID). The basic HID handling is done in the kernel,
|
||||
and HID reports can be sent/received through I/O on the
|
||||
/dev/hidgX character devices.
|
||||
The HID Gadget driver provides emulation of USB Human Interface
|
||||
Devices (HID). The basic HID handling is done in the kernel,
|
||||
and HID reports can be sent/received through I/O on the
|
||||
/dev/hidgX character devices.
|
||||
|
||||
For more details about HID, see the developer page on
|
||||
http://www.usb.org/developers/hidpage/
|
||||
For more details about HID, see the developer page on
|
||||
http://www.usb.org/developers/hidpage/
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
g_hid is a platform driver, so to use it you need to add
|
||||
struct platform_device(s) to your platform code defining the
|
||||
HID function descriptors you want to use - E.G. something
|
||||
like:
|
||||
g_hid is a platform driver, so to use it you need to add
|
||||
struct platform_device(s) to your platform code defining the
|
||||
HID function descriptors you want to use - E.G. something
|
||||
like::
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/g_hid.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/g_hid.h>
|
||||
|
||||
/* hid descriptor for a keyboard */
|
||||
static struct hidg_func_descriptor my_hid_data = {
|
||||
/* hid descriptor for a keyboard */
|
||||
static struct hidg_func_descriptor my_hid_data = {
|
||||
.subclass = 0, /* No subclass */
|
||||
.protocol = 1, /* Keyboard */
|
||||
.report_length = 8,
|
||||
@ -61,85 +64,87 @@ static struct hidg_func_descriptor my_hid_data = {
|
||||
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
|
||||
0xc0 /* END_COLLECTION */
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static struct platform_device my_hid = {
|
||||
static struct platform_device my_hid = {
|
||||
.name = "hidg",
|
||||
.id = 0,
|
||||
.num_resources = 0,
|
||||
.resource = 0,
|
||||
.dev.platform_data = &my_hid_data,
|
||||
};
|
||||
};
|
||||
|
||||
You can add as many HID functions as you want, only limited by
|
||||
the amount of interrupt endpoints your gadget driver supports.
|
||||
You can add as many HID functions as you want, only limited by
|
||||
the amount of interrupt endpoints your gadget driver supports.
|
||||
|
||||
Configuration with configfs
|
||||
===========================
|
||||
|
||||
Instead of adding fake platform devices and drivers in order to pass
|
||||
some data to the kernel, if HID is a part of a gadget composed with
|
||||
configfs the hidg_func_descriptor.report_desc is passed to the kernel
|
||||
by writing the appropriate stream of bytes to a configfs attribute.
|
||||
Instead of adding fake platform devices and drivers in order to pass
|
||||
some data to the kernel, if HID is a part of a gadget composed with
|
||||
configfs the hidg_func_descriptor.report_desc is passed to the kernel
|
||||
by writing the appropriate stream of bytes to a configfs attribute.
|
||||
|
||||
Send and receive HID reports
|
||||
============================
|
||||
|
||||
HID reports can be sent/received using read/write on the
|
||||
/dev/hidgX character devices. See below for an example program
|
||||
to do this.
|
||||
HID reports can be sent/received using read/write on the
|
||||
/dev/hidgX character devices. See below for an example program
|
||||
to do this.
|
||||
|
||||
hid_gadget_test is a small interactive program to test the HID
|
||||
gadget driver. To use, point it at a hidg device and set the
|
||||
device type (keyboard / mouse / joystick) - E.G.:
|
||||
hid_gadget_test is a small interactive program to test the HID
|
||||
gadget driver. To use, point it at a hidg device and set the
|
||||
device type (keyboard / mouse / joystick) - E.G.::
|
||||
|
||||
# hid_gadget_test /dev/hidg0 keyboard
|
||||
|
||||
You are now in the prompt of hid_gadget_test. You can type any
|
||||
combination of options and values. Available options and
|
||||
values are listed at program start. In keyboard mode you can
|
||||
send up to six values.
|
||||
You are now in the prompt of hid_gadget_test. You can type any
|
||||
combination of options and values. Available options and
|
||||
values are listed at program start. In keyboard mode you can
|
||||
send up to six values.
|
||||
|
||||
For example type: g i s t r --left-shift
|
||||
For example type: g i s t r --left-shift
|
||||
|
||||
Hit return and the corresponding report will be sent by the
|
||||
HID gadget.
|
||||
Hit return and the corresponding report will be sent by the
|
||||
HID gadget.
|
||||
|
||||
Another interesting example is the caps lock test. Type
|
||||
--caps-lock and hit return. A report is then sent by the
|
||||
gadget and you should receive the host answer, corresponding
|
||||
to the caps lock LED status.
|
||||
Another interesting example is the caps lock test. Type
|
||||
--caps-lock and hit return. A report is then sent by the
|
||||
gadget and you should receive the host answer, corresponding
|
||||
to the caps lock LED status::
|
||||
|
||||
--caps-lock
|
||||
recv report:2
|
||||
|
||||
With this command:
|
||||
With this command::
|
||||
|
||||
# hid_gadget_test /dev/hidg1 mouse
|
||||
|
||||
You can test the mouse emulation. Values are two signed numbers.
|
||||
You can test the mouse emulation. Values are two signed numbers.
|
||||
|
||||
|
||||
Sample code
|
||||
Sample code::
|
||||
|
||||
/* hid_gadget_test */
|
||||
/* hid_gadget_test */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BUF_LEN 512
|
||||
#define BUF_LEN 512
|
||||
|
||||
struct options {
|
||||
struct options {
|
||||
const char *opt;
|
||||
unsigned char val;
|
||||
};
|
||||
};
|
||||
|
||||
static struct options kmod[] = {
|
||||
static struct options kmod[] = {
|
||||
{.opt = "--left-ctrl", .val = 0x01},
|
||||
{.opt = "--right-ctrl", .val = 0x10},
|
||||
{.opt = "--left-shift", .val = 0x02},
|
||||
@ -149,9 +154,9 @@ static struct options kmod[] = {
|
||||
{.opt = "--left-meta", .val = 0x08},
|
||||
{.opt = "--right-meta", .val = 0x80},
|
||||
{.opt = NULL}
|
||||
};
|
||||
};
|
||||
|
||||
static struct options kval[] = {
|
||||
static struct options kval[] = {
|
||||
{.opt = "--return", .val = 0x28},
|
||||
{.opt = "--esc", .val = 0x29},
|
||||
{.opt = "--bckspc", .val = 0x2a},
|
||||
@ -183,10 +188,10 @@ static struct options kval[] = {
|
||||
{.opt = "--up", .val = 0x52},
|
||||
{.opt = "--num-lock", .val = 0x53},
|
||||
{.opt = NULL}
|
||||
};
|
||||
};
|
||||
|
||||
int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
{
|
||||
int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
{
|
||||
char *tok = strtok(buf, " ");
|
||||
int key = 0;
|
||||
int i = 0;
|
||||
@ -229,17 +234,17 @@ int keyboard_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
fprintf(stderr, "unknown option: %s\n", tok);
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
static struct options mmod[] = {
|
||||
static struct options mmod[] = {
|
||||
{.opt = "--b1", .val = 0x01},
|
||||
{.opt = "--b2", .val = 0x02},
|
||||
{.opt = "--b3", .val = 0x04},
|
||||
{.opt = NULL}
|
||||
};
|
||||
};
|
||||
|
||||
int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
{
|
||||
int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
{
|
||||
char *tok = strtok(buf, " ");
|
||||
int mvt = 0;
|
||||
int i = 0;
|
||||
@ -274,9 +279,9 @@ int mouse_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
fprintf(stderr, "unknown option: %s\n", tok);
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
static struct options jmod[] = {
|
||||
static struct options jmod[] = {
|
||||
{.opt = "--b1", .val = 0x10},
|
||||
{.opt = "--b2", .val = 0x20},
|
||||
{.opt = "--b3", .val = 0x40},
|
||||
@ -287,10 +292,10 @@ static struct options jmod[] = {
|
||||
{.opt = "--hat4", .val = 0x03},
|
||||
{.opt = "--hatneutral", .val = 0x04},
|
||||
{.opt = NULL}
|
||||
};
|
||||
};
|
||||
|
||||
int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
{
|
||||
int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
{
|
||||
char *tok = strtok(buf, " ");
|
||||
int mvt = 0;
|
||||
int i = 0;
|
||||
@ -326,10 +331,10 @@ int joystick_fill_report(char report[8], char buf[BUF_LEN], int *hold)
|
||||
fprintf(stderr, "unknown option: %s\n", tok);
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
void print_options(char c)
|
||||
{
|
||||
void print_options(char c)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (c == 'k') {
|
||||
@ -358,10 +363,10 @@ void print_options(char c)
|
||||
" three signed numbers\n"
|
||||
"--quit to close\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
const char *filename = NULL;
|
||||
int fd = 0;
|
||||
char buf[BUF_LEN];
|
||||
@ -449,4 +454,4 @@ int main(int argc, const char *argv[])
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
-*- org -*-
|
||||
==============================
|
||||
Multifunction Composite Gadget
|
||||
==============================
|
||||
|
||||
* Overview
|
||||
Overview
|
||||
========
|
||||
|
||||
The Multifunction Composite Gadget (or g_multi) is a composite gadget
|
||||
that makes extensive use of the composite framework to provide
|
||||
@ -17,13 +20,15 @@ have two configurations -- one with RNDIS and another with CDC ECM[3].
|
||||
Please note that if you use non-standard configuration (that is enable
|
||||
CDC ECM) you may need to change vendor and/or product ID.
|
||||
|
||||
* Host drivers
|
||||
Host drivers
|
||||
============
|
||||
|
||||
To make use of the gadget one needs to make it work on host side --
|
||||
without that there's no hope of achieving anything with the gadget.
|
||||
As one might expect, things one need to do very from system to system.
|
||||
|
||||
** Linux host drivers
|
||||
Linux host drivers
|
||||
------------------
|
||||
|
||||
Since the gadget uses standard composite framework and appears as such
|
||||
to Linux host it does not need any additional drivers on Linux host
|
||||
@ -34,11 +39,13 @@ This is also true for two configuration set-up with RNDIS
|
||||
configuration being the first one. Linux host will use the second
|
||||
configuration with CDC ECM which should work better under Linux.
|
||||
|
||||
** Windows host drivers
|
||||
Windows host drivers
|
||||
--------------------
|
||||
|
||||
For the gadget to work under Windows two conditions have to be met:
|
||||
|
||||
*** Detecting as composite gadget
|
||||
Detecting as composite gadget
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
First of all, Windows need to detect the gadget as an USB composite
|
||||
gadget which on its own have some conditions[4]. If they are met,
|
||||
@ -53,7 +60,8 @@ The only thing to worry is that the gadget has to have a single
|
||||
configuration so a dual RNDIS and CDC ECM gadget won't work unless you
|
||||
create a proper INF -- and of course, if you do submit it!
|
||||
|
||||
*** Installing drivers for each function
|
||||
Installing drivers for each function
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The other, trickier thing is making Windows install drivers for each
|
||||
individual function.
|
||||
@ -63,7 +71,8 @@ implementing USB Mass Storage class and selects appropriate driver.
|
||||
|
||||
Things are harder with RDNIS and CDC ACM.
|
||||
|
||||
**** RNDIS
|
||||
RNDIS
|
||||
.....
|
||||
|
||||
To make Windows select RNDIS drivers for the first function in the
|
||||
gadget, one needs to use the [[file:linux.inf]] file provided with this
|
||||
@ -75,11 +84,13 @@ RNDIS was not the first interface. You do not need to worry abut it
|
||||
unless you are trying to develop your own gadget in which case watch
|
||||
out for this bug.
|
||||
|
||||
**** CDC ACM
|
||||
CDC ACM
|
||||
.......
|
||||
|
||||
Similarly, [[file:linux-cdc-acm.inf]] is provided for CDC ACM.
|
||||
|
||||
**** Customising the gadget
|
||||
Customising the gadget
|
||||
......................
|
||||
|
||||
If you intend to hack the g_multi gadget be advised that rearranging
|
||||
functions will obviously change interface numbers for each of the
|
||||
@ -97,14 +108,16 @@ things don't work as intended before realising Windows have cached
|
||||
some drivers information (changing USB port may sometimes help plus
|
||||
you might try using USBDeview[8] to remove the phantom device).
|
||||
|
||||
**** INF testing
|
||||
INF testing
|
||||
...........
|
||||
|
||||
Provided INF files have been tested on Windows XP SP3, Windows Vista
|
||||
and Windows 7, all 32-bit versions. It should work on 64-bit versions
|
||||
as well. It most likely won't work on Windows prior to Windows XP
|
||||
SP2.
|
||||
|
||||
** Other systems
|
||||
Other systems
|
||||
-------------
|
||||
|
||||
At this moment, drivers for any other systems have not been tested.
|
||||
Knowing how MacOS is based on BSD and BSD is an Open Source it is
|
||||
@ -115,7 +128,8 @@ For more exotic systems I have even less to say...
|
||||
|
||||
Any testing and drivers *are* *welcome*!
|
||||
|
||||
* Authors
|
||||
Authors
|
||||
=======
|
||||
|
||||
This document has been written by Michal Nazarewicz
|
||||
([[mailto:mina86@mina86.com]]). INF files have been hacked with
|
||||
@ -124,7 +138,8 @@ Xiaofan Chen ([[mailto:xiaofanc@gmail.com]]) basing on the MS RNDIS
|
||||
template[9], Microchip's CDC ACM INF file and David Brownell's
|
||||
([[mailto:dbrownell@users.sourceforge.net]]) original INF files.
|
||||
|
||||
* Footnotes
|
||||
Footnotes
|
||||
=========
|
||||
|
||||
[1] Remote Network Driver Interface Specification,
|
||||
[[http://msdn.microsoft.com/en-us/library/ee484414.aspx]].
|
||||
|
@ -1,12 +1,14 @@
|
||||
===============================
|
||||
Linux USB Printer Gadget Driver
|
||||
===============================
|
||||
|
||||
Linux USB Printer Gadget Driver
|
||||
06/04/2007
|
||||
06/04/2007
|
||||
|
||||
Copyright (C) 2007 Craig W. Nadler <craig@nadler.us>
|
||||
Copyright (C) 2007 Craig W. Nadler <craig@nadler.us>
|
||||
|
||||
|
||||
|
||||
GENERAL
|
||||
General
|
||||
=======
|
||||
|
||||
This driver may be used if you are writing printer firmware using Linux as
|
||||
@ -29,52 +31,60 @@ user space firmware can read or write this status byte using a device file
|
||||
|
||||
|
||||
|
||||
HOWTO USE THIS DRIVER
|
||||
Howto Use This Driver
|
||||
=====================
|
||||
|
||||
To load the USB device controller driver and the printer gadget driver. The
|
||||
following example uses the Netchip 2280 USB device controller driver:
|
||||
following example uses the Netchip 2280 USB device controller driver::
|
||||
|
||||
modprobe net2280
|
||||
modprobe g_printer
|
||||
modprobe net2280
|
||||
modprobe g_printer
|
||||
|
||||
|
||||
The follow command line parameter can be used when loading the printer gadget
|
||||
(ex: modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 ):
|
||||
|
||||
idVendor - This is the Vendor ID used in the device descriptor. The default is
|
||||
idVendor
|
||||
This is the Vendor ID used in the device descriptor. The default is
|
||||
the Netchip vendor id 0x0525. YOU MUST CHANGE TO YOUR OWN VENDOR ID
|
||||
BEFORE RELEASING A PRODUCT. If you plan to release a product and don't
|
||||
already have a Vendor ID please see www.usb.org for details on how to
|
||||
get one.
|
||||
|
||||
idProduct - This is the Product ID used in the device descriptor. The default
|
||||
idProduct
|
||||
This is the Product ID used in the device descriptor. The default
|
||||
is 0xa4a8, you should change this to an ID that's not used by any of
|
||||
your other USB products if you have any. It would be a good idea to
|
||||
start numbering your products starting with say 0x0001.
|
||||
|
||||
bcdDevice - This is the version number of your product. It would be a good idea
|
||||
bcdDevice
|
||||
This is the version number of your product. It would be a good idea
|
||||
to put your firmware version here.
|
||||
|
||||
iManufacturer - A string containing the name of the Vendor.
|
||||
iManufacturer
|
||||
A string containing the name of the Vendor.
|
||||
|
||||
iProduct - A string containing the Product Name.
|
||||
iProduct
|
||||
A string containing the Product Name.
|
||||
|
||||
iSerialNum - A string containing the Serial Number. This should be changed for
|
||||
iSerialNum
|
||||
A string containing the Serial Number. This should be changed for
|
||||
each unit of your product.
|
||||
|
||||
iPNPstring - The PNP ID string used for this printer. You will want to set
|
||||
iPNPstring
|
||||
The PNP ID string used for this printer. You will want to set
|
||||
either on the command line or hard code the PNP ID string used for
|
||||
your printer product.
|
||||
|
||||
qlen - The number of 8k buffers to use per endpoint. The default is 10, you
|
||||
qlen
|
||||
The number of 8k buffers to use per endpoint. The default is 10, you
|
||||
should tune this for your product. You may also want to tune the
|
||||
size of each buffer for your product.
|
||||
|
||||
|
||||
|
||||
|
||||
USING THE EXAMPLE CODE
|
||||
Using The Example Code
|
||||
======================
|
||||
|
||||
This example code talks to stdout, instead of a print engine.
|
||||
@ -82,22 +92,23 @@ This example code talks to stdout, instead of a print engine.
|
||||
To compile the test code below:
|
||||
|
||||
1) save it to a file called prn_example.c
|
||||
2) compile the code with the follow command:
|
||||
2) compile the code with the follow command::
|
||||
|
||||
gcc prn_example.c -o prn_example
|
||||
|
||||
|
||||
|
||||
To read printer data from the host to stdout:
|
||||
To read printer data from the host to stdout::
|
||||
|
||||
# prn_example -read_data
|
||||
|
||||
|
||||
To write printer data from a file (data_file) to the host:
|
||||
To write printer data from a file (data_file) to the host::
|
||||
|
||||
# cat data_file | prn_example -write_data
|
||||
|
||||
|
||||
To get the current printer status for the gadget driver:
|
||||
To get the current printer status for the gadget driver:::
|
||||
|
||||
# prn_example -get_status
|
||||
|
||||
@ -107,60 +118,62 @@ To get the current printer status for the gadget driver:
|
||||
Printer OK
|
||||
|
||||
|
||||
To set printer to Selected/On-line:
|
||||
To set printer to Selected/On-line::
|
||||
|
||||
# prn_example -selected
|
||||
|
||||
|
||||
To set printer to Not Selected/Off-line:
|
||||
To set printer to Not Selected/Off-line::
|
||||
|
||||
# prn_example -not_selected
|
||||
|
||||
|
||||
To set paper status to paper out:
|
||||
To set paper status to paper out::
|
||||
|
||||
# prn_example -paper_out
|
||||
|
||||
|
||||
To set paper status to paper loaded:
|
||||
To set paper status to paper loaded::
|
||||
|
||||
# prn_example -paper_loaded
|
||||
|
||||
|
||||
To set error status to printer OK:
|
||||
To set error status to printer OK::
|
||||
|
||||
# prn_example -no_error
|
||||
|
||||
|
||||
To set error status to ERROR:
|
||||
To set error status to ERROR::
|
||||
|
||||
# prn_example -error
|
||||
|
||||
|
||||
|
||||
|
||||
EXAMPLE CODE
|
||||
Example Code
|
||||
============
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/usb/g_printer.h>
|
||||
|
||||
#define PRINTER_FILE "/dev/g_printer"
|
||||
#define BUF_SIZE 512
|
||||
::
|
||||
|
||||
|
||||
/*
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/usb/g_printer.h>
|
||||
|
||||
#define PRINTER_FILE "/dev/g_printer"
|
||||
#define BUF_SIZE 512
|
||||
|
||||
|
||||
/*
|
||||
* 'usage()' - Show program usage.
|
||||
*/
|
||||
|
||||
static void
|
||||
usage(const char *option) /* I - Option string or NULL */
|
||||
{
|
||||
static void
|
||||
usage(const char *option) /* I - Option string or NULL */
|
||||
{
|
||||
if (option) {
|
||||
fprintf(stderr,"prn_example: Unknown option \"%s\"!\n",
|
||||
option);
|
||||
@ -186,12 +199,12 @@ usage(const char *option) /* I - Option string or NULL */
|
||||
fputs("\n\n", stderr);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
read_printer_data()
|
||||
{
|
||||
static int
|
||||
read_printer_data()
|
||||
{
|
||||
struct pollfd fd[1];
|
||||
|
||||
/* Open device file for printer gadget. */
|
||||
@ -236,12 +249,12 @@ read_printer_data()
|
||||
close(fd[0].fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_printer_data()
|
||||
{
|
||||
static int
|
||||
write_printer_data()
|
||||
{
|
||||
struct pollfd fd[1];
|
||||
|
||||
/* Open device file for printer gadget. */
|
||||
@ -295,12 +308,12 @@ write_printer_data()
|
||||
close(fd[0].fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
read_NB_printer_data()
|
||||
{
|
||||
static int
|
||||
read_NB_printer_data()
|
||||
{
|
||||
int fd;
|
||||
static char buf[BUF_SIZE];
|
||||
int bytes_read;
|
||||
@ -329,12 +342,12 @@ read_NB_printer_data()
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
get_printer_status()
|
||||
{
|
||||
static int
|
||||
get_printer_status()
|
||||
{
|
||||
int retval;
|
||||
int fd;
|
||||
|
||||
@ -357,12 +370,12 @@ get_printer_status()
|
||||
close(fd);
|
||||
|
||||
return(retval);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
set_printer_status(unsigned char buf, int clear_printer_status_bit)
|
||||
{
|
||||
static int
|
||||
set_printer_status(unsigned char buf, int clear_printer_status_bit)
|
||||
{
|
||||
int retval;
|
||||
int fd;
|
||||
|
||||
@ -397,12 +410,12 @@ set_printer_status(unsigned char buf, int clear_printer_status_bit)
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
display_printer_status()
|
||||
{
|
||||
static int
|
||||
display_printer_status()
|
||||
{
|
||||
char printer_status;
|
||||
|
||||
printer_status = get_printer_status();
|
||||
@ -429,12 +442,12 @@ display_printer_status()
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int i; /* Looping var */
|
||||
int retval = 0;
|
||||
|
||||
@ -507,4 +520,4 @@ main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
exit(retval);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
===============================
|
||||
Linux Gadget Serial Driver v2.0
|
||||
===============================
|
||||
|
||||
Linux Gadget Serial Driver v2.0
|
||||
11/20/2004
|
||||
(updated 8-May-2008 for v2.3)
|
||||
11/20/2004
|
||||
|
||||
(updated 8-May-2008 for v2.3)
|
||||
|
||||
|
||||
License and Disclaimer
|
||||
@ -56,7 +59,7 @@ hardware; for example, a PDA, an embedded Linux system, or a PC
|
||||
with a USB development card.
|
||||
|
||||
The gadget serial driver talks over USB to either a CDC ACM driver
|
||||
or a generic USB serial driver running on a host PC.
|
||||
or a generic USB serial driver running on a host PC::
|
||||
|
||||
Host
|
||||
--------------------------------------
|
||||
@ -112,11 +115,11 @@ configuring the kernel. Then rebuild and install the kernel or
|
||||
modules.
|
||||
|
||||
Then you must load the gadget serial driver. To load it as an
|
||||
ACM device (recommended for interoperability), do this:
|
||||
ACM device (recommended for interoperability), do this::
|
||||
|
||||
modprobe g_serial
|
||||
|
||||
To load it as a vendor specific bulk in/out device, do this:
|
||||
To load it as a vendor specific bulk in/out device, do this::
|
||||
|
||||
modprobe g_serial use_acm=0
|
||||
|
||||
@ -127,7 +130,7 @@ desired.
|
||||
|
||||
Your system should use mdev (from busybox) or udev to make the
|
||||
device nodes. After this gadget driver has been set up you should
|
||||
then see a /dev/ttyGS0 node:
|
||||
then see a /dev/ttyGS0 node::
|
||||
|
||||
# ls -l /dev/ttyGS0 | cat
|
||||
crw-rw---- 1 root root 253, 0 May 8 14:10 /dev/ttyGS0
|
||||
@ -187,24 +190,24 @@ support".
|
||||
|
||||
Once the gadget serial driver is loaded and the USB device connected
|
||||
to the Linux host with a USB cable, the host system should recognize
|
||||
the gadget serial device. For example, the command
|
||||
the gadget serial device. For example, the command::
|
||||
|
||||
cat /sys/kernel/debug/usb/devices
|
||||
|
||||
should show something like this:
|
||||
should show something like this:::
|
||||
|
||||
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 5 Spd=480 MxCh= 0
|
||||
D: Ver= 2.00 Cls=02(comm.) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
|
||||
P: Vendor=0525 ProdID=a4a7 Rev= 2.01
|
||||
S: Manufacturer=Linux 2.6.8.1 with net2280
|
||||
S: Product=Gadget Serial
|
||||
S: SerialNumber=0
|
||||
C:* #Ifs= 2 Cfg#= 2 Atr=c0 MxPwr= 2mA
|
||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
|
||||
E: Ad=83(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
|
||||
I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
|
||||
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 5 Spd=480 MxCh= 0
|
||||
D: Ver= 2.00 Cls=02(comm.) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
|
||||
P: Vendor=0525 ProdID=a4a7 Rev= 2.01
|
||||
S: Manufacturer=Linux 2.6.8.1 with net2280
|
||||
S: Product=Gadget Serial
|
||||
S: SerialNumber=0
|
||||
C:* #Ifs= 2 Cfg#= 2 Atr=c0 MxPwr= 2mA
|
||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
|
||||
E: Ad=83(I) Atr=03(Int.) MxPS= 8 Ivl=32ms
|
||||
I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm
|
||||
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
|
||||
If the host side Linux system is configured properly, the ACM driver
|
||||
should be loaded automatically. The command "lsmod" should show the
|
||||
@ -219,29 +222,29 @@ Serial Converter support", and for the "USB Generic Serial Driver".
|
||||
|
||||
Once the gadget serial driver is loaded and the USB device connected
|
||||
to the Linux host with a USB cable, the host system should recognize
|
||||
the gadget serial device. For example, the command
|
||||
the gadget serial device. For example, the command::
|
||||
|
||||
cat /sys/kernel/debug/usb/devices
|
||||
|
||||
should show something like this:
|
||||
should show something like this:::
|
||||
|
||||
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 6 Spd=480 MxCh= 0
|
||||
D: Ver= 2.00 Cls=ff(vend.) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
|
||||
P: Vendor=0525 ProdID=a4a6 Rev= 2.01
|
||||
S: Manufacturer=Linux 2.6.8.1 with net2280
|
||||
S: Product=Gadget Serial
|
||||
S: SerialNumber=0
|
||||
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 2mA
|
||||
I: If#= 0 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=serial
|
||||
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 6 Spd=480 MxCh= 0
|
||||
D: Ver= 2.00 Cls=ff(vend.) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
|
||||
P: Vendor=0525 ProdID=a4a6 Rev= 2.01
|
||||
S: Manufacturer=Linux 2.6.8.1 with net2280
|
||||
S: Product=Gadget Serial
|
||||
S: SerialNumber=0
|
||||
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 2mA
|
||||
I: If#= 0 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=serial
|
||||
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||
|
||||
You must load the usbserial driver and explicitly set its parameters
|
||||
to configure it to recognize the gadget serial device, like this:
|
||||
to configure it to recognize the gadget serial device, like this::
|
||||
|
||||
echo 0x0525 0xA4A6 >/sys/bus/usb-serial/drivers/generic/new_id
|
||||
|
||||
The legacy way is to use module parameters:
|
||||
The legacy way is to use module parameters::
|
||||
|
||||
modprobe usbserial vendor=0x0525 product=0xA4A6
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
=============================
|
||||
Infinity Usb Unlimited Readme
|
||||
-----------------------------
|
||||
=============================
|
||||
|
||||
Hi all,
|
||||
|
||||
@ -19,7 +20,8 @@ have his own device file(/dev/ttyUSB0,/dev/ttyUSB1,...)
|
||||
|
||||
|
||||
|
||||
How to tune the reader speed ?
|
||||
How to tune the reader speed?
|
||||
=============================
|
||||
|
||||
A few parameters can be used at load time
|
||||
To use parameters, just unload the module if it is
|
||||
@ -27,26 +29,33 @@ How to tune the reader speed ?
|
||||
In case of prebuilt module, use the command
|
||||
insmod iuu_phoenix param=value.
|
||||
|
||||
Example:
|
||||
Example::
|
||||
|
||||
modprobe iuu_phoenix clockmode=3
|
||||
|
||||
The parameters are:
|
||||
|
||||
parm: clockmode:1=3Mhz579,2=3Mhz680,3=6Mhz (int)
|
||||
parm: boost:overclock boost percent 100 to 500 (int)
|
||||
parm: cdmode:Card detect mode 0=none, 1=CD, 2=!CD, 3=DSR, 4=!DSR, 5=CTS, 6=!CTS, 7=RING, 8=!RING (int)
|
||||
parm: xmas:xmas color enabled or not (bool)
|
||||
parm: debug:Debug enabled or not (bool)
|
||||
clockmode:
|
||||
1=3Mhz579,2=3Mhz680,3=6Mhz (int)
|
||||
boost:
|
||||
overclock boost percent 100 to 500 (int)
|
||||
cdmode:
|
||||
Card detect mode
|
||||
0=none, 1=CD, 2=!CD, 3=DSR, 4=!DSR, 5=CTS, 6=!CTS, 7=RING, 8=!RING (int)
|
||||
xmas:
|
||||
xmas color enabled or not (bool)
|
||||
debug:
|
||||
Debug enabled or not (bool)
|
||||
|
||||
- clockmode will provide 3 different base settings commonly adopted by
|
||||
different software:
|
||||
|
||||
1. 3Mhz579
|
||||
2. 3Mhz680
|
||||
3. 6Mhz
|
||||
|
||||
- boost provide a way to overclock the reader ( my favorite :-) )
|
||||
For example to have best performance than a simple clockmode=3, try this:
|
||||
For example to have best performance than a simple clockmode=3, try this::
|
||||
|
||||
modprobe boost=195
|
||||
|
||||
@ -66,7 +75,8 @@ How to tune the reader speed ?
|
||||
- debug will produce a lot of debugging messages...
|
||||
|
||||
|
||||
Last notes:
|
||||
Last notes
|
||||
==========
|
||||
|
||||
Don't worry about the serial settings, the serial emulation
|
||||
is an abstraction, so use any speed or parity setting will
|
||||
|
@ -1,4 +1,9 @@
|
||||
* Overview
|
||||
=========================
|
||||
Mass Storage Gadget (MSG)
|
||||
=========================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Mass Storage Gadget (or MSG) acts as a USB Mass Storage device,
|
||||
appearing to the host as a disk or a CD-ROM drive. It supports
|
||||
@ -24,7 +29,8 @@
|
||||
(which is no longer included in Linux). It will talk only briefly
|
||||
about how to use MSF within composite gadgets.
|
||||
|
||||
* Module parameters
|
||||
Module parameters
|
||||
=================
|
||||
|
||||
The mass storage gadget accepts the following mass storage specific
|
||||
module parameters:
|
||||
@ -146,7 +152,8 @@
|
||||
- iProduct -- USB Product string (string)
|
||||
- iSerialNumber -- SerialNumber string (sting)
|
||||
|
||||
* sysfs entries
|
||||
sysfs entries
|
||||
=============
|
||||
|
||||
For each logical unit, the gadget creates a directory in the sysfs
|
||||
hierarchy. Inside of it the following three files are created:
|
||||
@ -177,7 +184,8 @@
|
||||
Other then those, as usual, the values of module parameters can be
|
||||
read from /sys/module/g_mass_storage/parameters/* files.
|
||||
|
||||
* Other gadgets using mass storage function
|
||||
Other gadgets using mass storage function
|
||||
=========================================
|
||||
|
||||
The Mass Storage Gadget uses the Mass Storage Function to handle
|
||||
mass storage protocol. As a composite function, MSF may be used by
|
||||
@ -193,7 +201,8 @@
|
||||
may take a look at mass_storage.c, acm_ms.c and multi.c (sorted by
|
||||
complexity).
|
||||
|
||||
* Relation to file storage gadget
|
||||
Relation to file storage gadget
|
||||
===============================
|
||||
|
||||
The Mass Storage Function and thus the Mass Storage Gadget has been
|
||||
based on the File Storage Gadget. The difference between the two is
|
||||
|
@ -1,4 +1,7 @@
|
||||
=============================
|
||||
USB 7-Segment Numeric Display
|
||||
=============================
|
||||
|
||||
Manufactured by Delcom Engineering
|
||||
|
||||
Device Information
|
||||
@ -13,9 +16,13 @@ Device Modes
|
||||
------------
|
||||
By default, the driver assumes the display is only 6 characters
|
||||
The mode for 6 characters is:
|
||||
|
||||
MSB 0x06; LSB 0x3f
|
||||
|
||||
For the 8 character display:
|
||||
|
||||
MSB 0x08; LSB 0xff
|
||||
|
||||
The device can accept "text" either in raw, hex, or ascii textmode.
|
||||
raw controls each segment manually,
|
||||
hex expects a value between 0-15 per character,
|
||||
@ -42,5 +49,3 @@ Device Operation
|
||||
To set multiple decimals points sum up each power.
|
||||
For example, to set the 0th and 3rd decimal place
|
||||
echo 1001 > /sys/bus/usb/.../decimals
|
||||
|
||||
|
||||
|
@ -1,4 +1,9 @@
|
||||
CHANGES
|
||||
================
|
||||
mtouchusb driver
|
||||
================
|
||||
|
||||
Changes
|
||||
=======
|
||||
|
||||
- 0.3 - Created based off of scanner & INSTALL from the original touchscreen
|
||||
driver on freecode (http://freecode.com/projects/3mtouchscreendriver)
|
||||
@ -13,7 +18,10 @@ CHANGES
|
||||
Eliminated vendor/product module params
|
||||
Performed multiple successful tests with an EXII-5010UC
|
||||
|
||||
SUPPORTED HARDWARE:
|
||||
Supported Hardware
|
||||
==================
|
||||
|
||||
::
|
||||
|
||||
All controllers have the Vendor: 0x0596 & Product: 0x0001
|
||||
|
||||
@ -29,7 +37,8 @@ SUPPORTED HARDWARE:
|
||||
USB Capacitive - Black Case EXII-5030UC
|
||||
USB Capacitive - No Case EXII-5050UC
|
||||
|
||||
DRIVER NOTES:
|
||||
Driver Notes
|
||||
============
|
||||
|
||||
Installation is simple, you only need to add Linux Input, Linux USB, and the
|
||||
driver to the kernel. The driver can also be optionally built as a module.
|
||||
@ -54,19 +63,22 @@ generic functions like calibrations, resets, and vendor information can be
|
||||
requested from the userspace (And the drivers would handle the vendor specific
|
||||
tasks).
|
||||
|
||||
TODO:
|
||||
TODO
|
||||
====
|
||||
|
||||
Implement a control urb again to handle requests to and from the device
|
||||
such as calibration, etc once/if it becomes available.
|
||||
|
||||
DISCLAIMER:
|
||||
Disclaimer
|
||||
==========
|
||||
|
||||
I am not a MicroTouch/3M employee, nor have I ever been. 3M does not support
|
||||
this driver! If you want touch drivers only supported within X, please go to:
|
||||
|
||||
http://www.3m.com/3MTouchSystems/
|
||||
|
||||
THANKS:
|
||||
Thanks
|
||||
======
|
||||
|
||||
A huge thank you to 3M Touch Systems for the EXII-5010UC controllers for
|
||||
testing!
|
||||
|
@ -1,3 +1,7 @@
|
||||
====
|
||||
OHCI
|
||||
====
|
||||
|
||||
23-Aug-2002
|
||||
|
||||
The "ohci-hcd" driver is a USB Host Controller Driver (HCD) that is derived
|
||||
@ -29,4 +33,3 @@ work on while the OS is getting around to the relevant IRQ processing.
|
||||
|
||||
- David Brownell
|
||||
<dbrownell@users.sourceforge.net>
|
||||
|
||||
|
@ -1,27 +1,34 @@
|
||||
============
|
||||
Diamonds Rio
|
||||
============
|
||||
|
||||
Copyright (C) 1999, 2000 Bruce Tenison
|
||||
|
||||
Portions Copyright (C) 1999, 2000 David Nelson
|
||||
|
||||
Thanks to David Nelson for guidance and the usage of the scanner.txt
|
||||
and scanner.c files to model our driver and this informative file.
|
||||
|
||||
Mar. 2, 2000
|
||||
|
||||
CHANGES
|
||||
Changes
|
||||
=======
|
||||
|
||||
- Initial Revision
|
||||
|
||||
|
||||
OVERVIEW
|
||||
Overview
|
||||
========
|
||||
|
||||
This README will address issues regarding how to configure the kernel
|
||||
to access a RIO 500 mp3 player.
|
||||
Before I explain how to use this to access the Rio500 please be warned:
|
||||
|
||||
W A R N I N G:
|
||||
--------------
|
||||
.. warning::
|
||||
|
||||
Please note that this software is still under development. The authors
|
||||
are in no way responsible for any damage that may occur, no matter how
|
||||
inconsequential.
|
||||
Please note that this software is still under development. The authors
|
||||
are in no way responsible for any damage that may occur, no matter how
|
||||
inconsequential.
|
||||
|
||||
It seems that the Rio has a problem when sending .mp3 with low batteries.
|
||||
I suggest when the batteries are low and you want to transfer stuff that you
|
||||
@ -42,15 +49,15 @@ power on problems with some chipsets. If you are having problems
|
||||
connecting to your RIO 500, try turning it on first and then plugging it
|
||||
into the USB cable.
|
||||
|
||||
Contact information:
|
||||
--------------------
|
||||
Contact Information
|
||||
-------------------
|
||||
|
||||
The main page for the project is hosted at sourceforge.net in the following
|
||||
URL: <http://rio500.sourceforge.net>. You can also go to the project's
|
||||
sourceforge home page at: <http://sourceforge.net/projects/rio500/>.
|
||||
There is also a mailing list: rio500-users@lists.sourceforge.net
|
||||
|
||||
Authors:
|
||||
Authors
|
||||
-------
|
||||
|
||||
Most of the code was written by Cesar Miquel <miquel@df.uba.ar>. Keith
|
||||
@ -61,12 +68,13 @@ re-written and Pete Ikusz along with the rest will re-design it. I would
|
||||
also like to thank Tri Nguyen <tmn_3022000@hotmail.com> who provided use
|
||||
with some important information regarding the communication with the Rio.
|
||||
|
||||
ADDITIONAL INFORMATION and Userspace tools
|
||||
Additional Information and userspace tools
|
||||
|
||||
http://rio500.sourceforge.net/
|
||||
http://rio500.sourceforge.net/
|
||||
|
||||
|
||||
REQUIREMENTS
|
||||
Requirements
|
||||
============
|
||||
|
||||
A host with a USB port. Ideally, either a UHCI (Intel) or OHCI
|
||||
(Compaq and others) hardware port should work.
|
||||
@ -80,11 +88,11 @@ A Linux kernel with RIO 500 support enabled.
|
||||
'lspci' which is only needed to determine the type of USB hardware
|
||||
available in your machine.
|
||||
|
||||
CONFIGURATION
|
||||
Configuration
|
||||
|
||||
Using `lspci -v`, determine the type of USB hardware available.
|
||||
|
||||
If you see something like:
|
||||
If you see something like::
|
||||
|
||||
USB Controller: ......
|
||||
Flags: .....
|
||||
@ -92,7 +100,7 @@ Using `lspci -v`, determine the type of USB hardware available.
|
||||
|
||||
Then you have a UHCI based controller.
|
||||
|
||||
If you see something like:
|
||||
If you see something like::
|
||||
|
||||
USB Controller: .....
|
||||
Flags: ....
|
||||
@ -107,8 +115,9 @@ hardware (determined from the steps above), 'USB Diamond Rio500 support', and
|
||||
(you may need to execute `depmod -a` to update the module
|
||||
dependencies).
|
||||
|
||||
Add a device for the USB rio500:
|
||||
`mknod /dev/usb/rio500 c 180 64`
|
||||
Add a device for the USB rio500::
|
||||
|
||||
mknod /dev/usb/rio500 c 180 64
|
||||
|
||||
Set appropriate permissions for /dev/usb/rio500 (don't forget about
|
||||
group and world permissions). Both read and write permissions are
|
||||
@ -116,12 +125,14 @@ required for proper operation.
|
||||
|
||||
Load the appropriate modules (if compiled as modules):
|
||||
|
||||
OHCI:
|
||||
OHCI::
|
||||
|
||||
modprobe usbcore
|
||||
modprobe usb-ohci
|
||||
modprobe rio500
|
||||
|
||||
UHCI:
|
||||
UHCI::
|
||||
|
||||
modprobe usbcore
|
||||
modprobe usb-uhci (or uhci)
|
||||
modprobe rio500
|
||||
@ -129,10 +140,10 @@ Load the appropriate modules (if compiled as modules):
|
||||
That's it. The Rio500 Utils at: http://rio500.sourceforge.net should
|
||||
be able to access the rio500.
|
||||
|
||||
BUGS
|
||||
Bugs
|
||||
====
|
||||
|
||||
If you encounter any problems feel free to drop me an email.
|
||||
|
||||
Bruce Tenison
|
||||
btenison@dibbs.net
|
||||
|
||||
|
@ -1,16 +1,17 @@
|
||||
usb-help.txt
|
||||
==============
|
||||
USB references
|
||||
==============
|
||||
|
||||
2008-Mar-7
|
||||
|
||||
For USB help other than the readme files that are located in
|
||||
Documentation/usb/*, see the following:
|
||||
`Documentation/usb/*`, see the following:
|
||||
|
||||
Linux-USB project: http://www.linux-usb.org
|
||||
- Linux-USB project: http://www.linux-usb.org
|
||||
mirrors at http://usb.in.tum.de/linux-usb/
|
||||
and http://it.linux-usb.org
|
||||
Linux USB Guide: http://linux-usb.sourceforge.net
|
||||
Linux-USB device overview (working devices and drivers):
|
||||
- Linux USB Guide: http://linux-usb.sourceforge.net
|
||||
- Linux-USB device overview (working devices and drivers):
|
||||
http://www.qbik.ch/usb/devices/
|
||||
|
||||
The Linux-USB mailing list is at linux-usb@vger.kernel.org
|
||||
|
||||
###
|
||||
|
@ -1,4 +1,9 @@
|
||||
INTRODUCTION
|
||||
==========
|
||||
USB serial
|
||||
==========
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The USB serial driver currently supports a number of different USB to
|
||||
serial converter products, as well as some devices that use a serial
|
||||
@ -8,13 +13,15 @@ INTRODUCTION
|
||||
the different devices.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Currently the driver can handle up to 256 different serial interfaces at
|
||||
one time.
|
||||
|
||||
The major number that the driver uses is 188 so to use the driver,
|
||||
create the following nodes:
|
||||
create the following nodes::
|
||||
|
||||
mknod /dev/ttyUSB0 c 188 0
|
||||
mknod /dev/ttyUSB1 c 188 1
|
||||
mknod /dev/ttyUSB2 c 188 2
|
||||
@ -30,10 +37,12 @@ CONFIGURATION
|
||||
to.
|
||||
|
||||
|
||||
SPECIFIC DEVICES SUPPORTED
|
||||
Specific Devices Supported
|
||||
==========================
|
||||
|
||||
|
||||
ConnectTech WhiteHEAT 4 port converter
|
||||
--------------------------------------
|
||||
|
||||
ConnectTech has been very forthcoming with information about their
|
||||
device, including providing a unit to test with.
|
||||
@ -46,6 +55,7 @@ ConnectTech WhiteHEAT 4 port converter
|
||||
|
||||
|
||||
HandSpring Visor, Palm USB, and Clié USB driver
|
||||
-----------------------------------------------
|
||||
|
||||
This driver works with all HandSpring USB, Palm USB, and Sony Clié USB
|
||||
devices.
|
||||
@ -90,6 +100,7 @@ HandSpring Visor, Palm USB, and Clié USB driver
|
||||
|
||||
|
||||
PocketPC PDA Driver
|
||||
-------------------
|
||||
|
||||
This driver can be used to connect to Compaq iPAQ, HP Jornada, Casio EM500
|
||||
and other PDAs running Windows CE 3.0 or PocketPC 2002 using a USB
|
||||
@ -141,6 +152,7 @@ PocketPC PDA Driver
|
||||
|
||||
|
||||
Keyspan PDA Serial Adapter
|
||||
--------------------------
|
||||
|
||||
Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly
|
||||
sold in Macintosh catalogs, comes in a translucent white/green dongle).
|
||||
@ -148,32 +160,37 @@ Keyspan PDA Serial Adapter
|
||||
This driver also works for the Xircom/Entrega single port serial adapter.
|
||||
|
||||
Current status:
|
||||
|
||||
Things that work:
|
||||
basic input/output (tested with 'cu')
|
||||
blocking write when serial line can't keep up
|
||||
changing baud rates (up to 115200)
|
||||
getting/setting modem control pins (TIOCM{GET,SET,BIS,BIC})
|
||||
sending break (although duration looks suspect)
|
||||
- basic input/output (tested with 'cu')
|
||||
- blocking write when serial line can't keep up
|
||||
- changing baud rates (up to 115200)
|
||||
- getting/setting modem control pins (TIOCM{GET,SET,BIS,BIC})
|
||||
- sending break (although duration looks suspect)
|
||||
|
||||
Things that don't:
|
||||
device strings (as logged by kernel) have trailing binary garbage
|
||||
device ID isn't right, might collide with other Keyspan products
|
||||
changing baud rates ought to flush tx/rx to avoid mangled half characters
|
||||
- device strings (as logged by kernel) have trailing binary garbage
|
||||
- device ID isn't right, might collide with other Keyspan products
|
||||
- changing baud rates ought to flush tx/rx to avoid mangled half characters
|
||||
|
||||
Big Things on the todo list:
|
||||
parity, 7 vs 8 bits per char, 1 or 2 stop bits
|
||||
HW flow control
|
||||
not all of the standard USB descriptors are handled: Get_Status, Set_Feature
|
||||
O_NONBLOCK, select()
|
||||
- parity, 7 vs 8 bits per char, 1 or 2 stop bits
|
||||
- HW flow control
|
||||
- not all of the standard USB descriptors are handled:
|
||||
Get_Status, Set_Feature, O_NONBLOCK, select()
|
||||
|
||||
For any questions or problems with this driver, please contact Brian
|
||||
Warner at warner@lothar.com
|
||||
|
||||
|
||||
Keyspan USA-series Serial Adapters
|
||||
----------------------------------
|
||||
|
||||
Single, Dual and Quad port adapters - driver uses Keyspan supplied
|
||||
firmware and is being developed with their support.
|
||||
|
||||
Current status:
|
||||
|
||||
The USA-18X, USA-28X, USA-19, USA-19W and USA-49W are supported and
|
||||
have been pretty thoroughly tested at various baud rates with 8-N-1
|
||||
character settings. Other character lengths and parity setups are
|
||||
@ -184,6 +201,7 @@ Keyspan USA-series Serial Adapters
|
||||
functionality.
|
||||
|
||||
More information is available at:
|
||||
|
||||
http://www.carnationsoftware.com/carnation/Keyspan.html
|
||||
|
||||
For any questions or problems with this driver, please contact Hugh
|
||||
@ -191,23 +209,27 @@ Keyspan USA-series Serial Adapters
|
||||
|
||||
|
||||
FTDI Single Port Serial Driver
|
||||
------------------------------
|
||||
|
||||
This is a single port DB-25 serial adapter.
|
||||
|
||||
Devices supported include:
|
||||
-TripNav TN-200 USB GPS
|
||||
-Navis Engineering Bureau CH-4711 USB GPS
|
||||
|
||||
- TripNav TN-200 USB GPS
|
||||
- Navis Engineering Bureau CH-4711 USB GPS
|
||||
|
||||
For any questions or problems with this driver, please contact Bill Ryder.
|
||||
|
||||
|
||||
ZyXEL omni.net lcd plus ISDN TA
|
||||
-------------------------------
|
||||
|
||||
This is an ISDN TA. Please report both successes and troubles to
|
||||
azummo@towertech.it
|
||||
|
||||
|
||||
Cypress M8 CY4601 Family Serial Driver
|
||||
--------------------------------------
|
||||
|
||||
This driver was in most part developed by Neil "koyama" Whelchel. It
|
||||
has been improved since that previous form to support dynamic serial
|
||||
@ -220,10 +242,11 @@ Cypress M8 CY4601 Family Serial Driver
|
||||
|
||||
Devices supported:
|
||||
|
||||
-DeLorme's USB Earthmate GPS (SiRF Star II lp arch)
|
||||
-Cypress HID->COM RS232 adapter
|
||||
- DeLorme's USB Earthmate GPS (SiRF Star II lp arch)
|
||||
- Cypress HID->COM RS232 adapter
|
||||
|
||||
Note: Cypress Semiconductor claims no affiliation with the
|
||||
Note:
|
||||
Cypress Semiconductor claims no affiliation with the
|
||||
hid->com device.
|
||||
|
||||
Most devices using chipsets under the CY4601 family should
|
||||
@ -237,7 +260,8 @@ Cypress M8 CY4601 Family Serial Driver
|
||||
of the termios settings, along with some custom termios so that the
|
||||
output is in proper format and parsable.
|
||||
|
||||
The device can be put into sirf mode by issuing NMEA command:
|
||||
The device can be put into sirf mode by issuing NMEA command::
|
||||
|
||||
$PSRF100,<protocol>,<baud>,<databits>,<stopbits>,<parity>*CHECKSUM
|
||||
$PSRF100,0,9600,8,1,0*0C
|
||||
|
||||
@ -259,11 +283,14 @@ Cypress M8 CY4601 Family Serial Driver
|
||||
|
||||
If you have any questions, problems, patches, feature requests, etc. you can
|
||||
contact me here via email:
|
||||
|
||||
dignome@gmail.com
|
||||
|
||||
(your problems/patches can alternately be submitted to usb-devel)
|
||||
|
||||
|
||||
Digi AccelePort Driver
|
||||
----------------------
|
||||
|
||||
This driver supports the Digi AccelePort USB 2 and 4 devices, 2 port
|
||||
(plus a parallel port) and 4 port USB serial converters. The driver
|
||||
@ -285,42 +312,49 @@ Digi AccelePort Driver
|
||||
|
||||
|
||||
Belkin USB Serial Adapter F5U103
|
||||
--------------------------------
|
||||
|
||||
Single port DB-9/PS-2 serial adapter from Belkin with firmware by eTEK Labs.
|
||||
The Peracom single port serial adapter also works with this driver, as
|
||||
well as the GoHubs adapter.
|
||||
|
||||
Current status:
|
||||
The following have been tested and work:
|
||||
Baud rate 300-230400
|
||||
Data bits 5-8
|
||||
Stop bits 1-2
|
||||
Parity N,E,O,M,S
|
||||
Handshake None, Software (XON/XOFF), Hardware (CTSRTS,CTSDTR)*
|
||||
Break Set and clear
|
||||
Line control Input/Output query and control **
|
||||
|
||||
* Hardware input flow control is only enabled for firmware
|
||||
The following have been tested and work:
|
||||
|
||||
- Baud rate 300-230400
|
||||
- Data bits 5-8
|
||||
- Stop bits 1-2
|
||||
- Parity N,E,O,M,S
|
||||
- Handshake None, Software (XON/XOFF), Hardware (CTSRTS,CTSDTR) [1]_
|
||||
- Break Set and clear
|
||||
- Line control Input/Output query and control [2]_
|
||||
|
||||
.. [1]
|
||||
Hardware input flow control is only enabled for firmware
|
||||
levels above 2.06. Read source code comments describing Belkin
|
||||
firmware errata. Hardware output flow control is working for all
|
||||
firmware versions.
|
||||
** Queries of inputs (CTS,DSR,CD,RI) show the last
|
||||
|
||||
.. [2]
|
||||
Queries of inputs (CTS,DSR,CD,RI) show the last
|
||||
reported state. Queries of outputs (DTR,RTS) show the last
|
||||
requested state and may not reflect current state as set by
|
||||
automatic hardware flow control.
|
||||
|
||||
TO DO List:
|
||||
-- Add true modem control line query capability. Currently tracks the
|
||||
- Add true modem control line query capability. Currently tracks the
|
||||
states reported by the interrupt and the states requested.
|
||||
-- Add error reporting back to application for UART error conditions.
|
||||
-- Add support for flush ioctls.
|
||||
-- Add everything else that is missing :)
|
||||
- Add error reporting back to application for UART error conditions.
|
||||
- Add support for flush ioctls.
|
||||
- Add everything else that is missing :)
|
||||
|
||||
For any questions or problems with this driver, please contact William
|
||||
Greathouse at wgreathouse@smva.com
|
||||
|
||||
|
||||
Empeg empeg-car Mark I/II Driver
|
||||
--------------------------------
|
||||
|
||||
This is an experimental driver to provide connectivity support for the
|
||||
client synchronization tools for an Empeg empeg-car mp3 player.
|
||||
@ -335,6 +369,7 @@ Empeg empeg-car Mark I/II Driver
|
||||
|
||||
|
||||
MCT USB Single Port Serial Adapter U232
|
||||
---------------------------------------
|
||||
|
||||
This driver is for the MCT USB-RS232 Converter (25 pin, Model No.
|
||||
U232-P25) from Magic Control Technology Corp. (there is also a 9 pin
|
||||
@ -355,35 +390,39 @@ MCT USB Single Port Serial Adapter U232
|
||||
|
||||
|
||||
Inside Out Networks Edgeport Driver
|
||||
-----------------------------------
|
||||
|
||||
This driver supports all devices made by Inside Out Networks, specifically
|
||||
the following models:
|
||||
Edgeport/4
|
||||
Rapidport/4
|
||||
Edgeport/4t
|
||||
Edgeport/2
|
||||
Edgeport/4i
|
||||
Edgeport/2i
|
||||
Edgeport/421
|
||||
Edgeport/21
|
||||
Edgeport/8
|
||||
Edgeport/8 Dual
|
||||
Edgeport/2D8
|
||||
Edgeport/4D8
|
||||
Edgeport/8i
|
||||
Edgeport/2 DIN
|
||||
Edgeport/4 DIN
|
||||
Edgeport/16 Dual
|
||||
|
||||
- Edgeport/4
|
||||
- Rapidport/4
|
||||
- Edgeport/4t
|
||||
- Edgeport/2
|
||||
- Edgeport/4i
|
||||
- Edgeport/2i
|
||||
- Edgeport/421
|
||||
- Edgeport/21
|
||||
- Edgeport/8
|
||||
- Edgeport/8 Dual
|
||||
- Edgeport/2D8
|
||||
- Edgeport/4D8
|
||||
- Edgeport/8i
|
||||
- Edgeport/2 DIN
|
||||
- Edgeport/4 DIN
|
||||
- Edgeport/16 Dual
|
||||
|
||||
For any questions or problems with this driver, please contact Greg
|
||||
Kroah-Hartman at greg@kroah.com
|
||||
|
||||
|
||||
REINER SCT cyberJack pinpad/e-com USB chipcard reader
|
||||
-----------------------------------------------------
|
||||
|
||||
Interface to ISO 7816 compatible contactbased chipcards, e.g. GSM SIMs.
|
||||
|
||||
Current status:
|
||||
|
||||
This is the kernel part of the driver for this USB card reader.
|
||||
There is also a user part for a CT-API driver available. A site
|
||||
for downloading is TBA. For now, you can request it from the
|
||||
@ -394,6 +433,7 @@ REINER SCT cyberJack pinpad/e-com USB chipcard reader
|
||||
|
||||
|
||||
Prolific PL2303 Driver
|
||||
----------------------
|
||||
|
||||
This driver supports any device that has the PL2303 chip from Prolific
|
||||
in it. This includes a number of single port USB to serial converters,
|
||||
@ -406,8 +446,10 @@ Prolific PL2303 Driver
|
||||
|
||||
|
||||
KL5KUSB105 chipset / PalmConnect USB single-port adapter
|
||||
--------------------------------------------------------
|
||||
|
||||
Current status:
|
||||
|
||||
The driver was put together by looking at the usb bus transactions
|
||||
done by Palm's driver under Windows, so a lot of functionality is
|
||||
still missing. Notably, serial ioctls are sometimes faked or not yet
|
||||
@ -422,16 +464,20 @@ Current status:
|
||||
information on this driver.
|
||||
|
||||
Winchiphead CH341 Driver
|
||||
------------------------
|
||||
|
||||
This driver is for the Winchiphead CH341 USB-RS232 Converter. This chip
|
||||
also implements an IEEE 1284 parallel port, I2C and SPI, but that is not
|
||||
supported by the driver. The protocol was analyzed from the behaviour
|
||||
of the Windows driver, no datasheet is available at present.
|
||||
|
||||
The manufacturer's website: http://www.winchiphead.com/.
|
||||
|
||||
For any questions or problems with this driver, please contact
|
||||
frank@kingswood-consulting.co.uk.
|
||||
|
||||
Moschip MCS7720, MCS7715 driver
|
||||
-------------------------------
|
||||
|
||||
These chips are present in devices sold by various manufacturers, such as Syba
|
||||
and Cables Unlimited. There may be others. The 7720 provides two serial
|
||||
@ -449,6 +495,7 @@ Moschip MCS7720, MCS7715 driver
|
||||
don't have one of these devices, so I can't say for sure.
|
||||
|
||||
Generic Serial driver
|
||||
---------------------
|
||||
|
||||
If your device is not one of the above listed devices, compatible with
|
||||
the above models, you can try out the "generic" interface. This
|
||||
@ -457,12 +504,15 @@ Generic Serial driver
|
||||
is required of your device is that it has at least one bulk in endpoint,
|
||||
or one bulk out endpoint.
|
||||
|
||||
To enable the generic driver to recognize your device, provide
|
||||
To enable the generic driver to recognize your device, provide::
|
||||
|
||||
echo <vid> <pid> >/sys/bus/usb-serial/drivers/generic/new_id
|
||||
|
||||
where the <vid> and <pid> is replaced with the hex representation of your
|
||||
device's vendor id and product id.
|
||||
If the driver is compiled as a module you can also provide one id when
|
||||
loading the module
|
||||
loading the module::
|
||||
|
||||
insmod usbserial vendor=0x#### product=0x####
|
||||
|
||||
This driver has been successfully used to connect to the NetChip USB
|
||||
@ -473,7 +523,8 @@ Generic Serial driver
|
||||
Kroah-Hartman at greg@kroah.com
|
||||
|
||||
|
||||
CONTACT:
|
||||
Contact
|
||||
=======
|
||||
|
||||
If anyone has any problems using these drivers, with any of the above
|
||||
specified products, please contact the specific driver's author listed
|
||||
|
@ -1,3 +1,7 @@
|
||||
===============
|
||||
USB/IP protocol
|
||||
===============
|
||||
|
||||
PRELIMINARY DRAFT, MAY CONTAIN MISTAKES!
|
||||
28 Jun 2011
|
||||
|
||||
@ -12,6 +16,8 @@ in one or more pieces at the low level transport layer). The server sends back
|
||||
the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the
|
||||
TCP/IP connection is closed.
|
||||
|
||||
::
|
||||
|
||||
virtual host controller usb host
|
||||
"client" "server"
|
||||
(imports USB devices) (exports USB devices)
|
||||
@ -32,6 +38,8 @@ send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and
|
||||
USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the
|
||||
server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively.
|
||||
|
||||
::
|
||||
|
||||
virtual host controller usb host
|
||||
"client" "server"
|
||||
(imports USB devices) (exports USB devices)
|
||||
@ -88,270 +96,316 @@ The fields are in network (big endian) byte order meaning that the most signific
|
||||
byte (MSB) is stored at the lowest address.
|
||||
|
||||
|
||||
OP_REQ_DEVLIST: Retrieve the list of exported USB devices.
|
||||
OP_REQ_DEVLIST:
|
||||
Retrieve the list of exported USB devices.
|
||||
|
||||
Offset | Length | Value | Description
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB
|
||||
| | | devices.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
4 | 4 | 0x00000000 | Status: unused, shall be set to 0
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| Offset | Length | Value | Description |
|
||||
+===========+========+============+===================================================+
|
||||
| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB |
|
||||
| | | | devices. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
|
||||
OP_REP_DEVLIST: Reply with the list of exported USB devices.
|
||||
OP_REP_DEVLIST:
|
||||
Reply with the list of exported USB devices.
|
||||
|
||||
Offset | Length | Value | Description
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
2 | 2 | 0x0005 | Reply code: The list of exported USB devices.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
4 | 4 | 0x00000000 | Status: 0 for OK
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
8 | 4 | n | Number of exported devices: 0 means no exported
|
||||
| | | devices.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x0C | | | From now on the exported n devices are described,
|
||||
| | | if any. If no devices are exported the message
|
||||
| | | ends with the previous "number of exported
|
||||
| | | devices" field.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
| 256 | | path: Path of the device on the host exporting the
|
||||
| | | USB device, string closed with zero byte, e.g.
|
||||
| | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
|
||||
| | | The unused bytes shall be filled with zero
|
||||
| | | bytes.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x10C | 32 | | busid: Bus ID of the exported device, string
|
||||
| | | closed with zero byte, e.g. "3-2". The unused
|
||||
| | | bytes shall be filled with zero bytes.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x12C | 4 | | busnum
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x130 | 4 | | devnum
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x134 | 4 | | speed
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x138 | 2 | | idVendor
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13A | 2 | | idProduct
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13C | 2 | | bcdDevice
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13E | 1 | | bDeviceClass
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13F | 1 | | bDeviceSubClass
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x140 | 1 | | bDeviceProtocol
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x141 | 1 | | bConfigurationValue
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x142 | 1 | | bNumConfigurations
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x143 | 1 | | bNumInterfaces
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x144 | | m_0 | From now on each interface is described, all
|
||||
| | | together bNumInterfaces times, with the
|
||||
| | | the following 4 fields:
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
| 1 | | bInterfaceClass
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x145 | 1 | | bInterfaceSubClass
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x146 | 1 | | bInterfaceProtocol
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x147 | 1 | | padding byte for alignment, shall be set to zero
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0xC + | | | The second exported USB device starts at i=1
|
||||
i*0x138 + | | | with the busid field.
|
||||
m_(i-1)*4 | | |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| Offset | Length | Value | Description |
|
||||
+===========+========+============+===================================================+
|
||||
| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0.|
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 2 | 2 | 0x0005 | Reply code: The list of exported USB devices. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 4 | 4 | 0x00000000 | Status: 0 for OK |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 8 | 4 | n | Number of exported devices: 0 means no exported |
|
||||
| | | | devices. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x0C | | | From now on the exported n devices are described, |
|
||||
| | | | if any. If no devices are exported the message |
|
||||
| | | | ends with the previous "number of exported |
|
||||
| | | | devices" field. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| | 256 | | path: Path of the device on the host exporting the|
|
||||
| | | | USB device, string closed with zero byte, e.g. |
|
||||
| | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" |
|
||||
| | | | The unused bytes shall be filled with zero |
|
||||
| | | | bytes. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x10C | 32 | | busid: Bus ID of the exported device, string |
|
||||
| | | | closed with zero byte, e.g. "3-2". The unused |
|
||||
| | | | bytes shall be filled with zero bytes. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x12C | 4 | | busnum |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x130 | 4 | | devnum |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x134 | 4 | | speed |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x138 | 2 | | idVendor |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13A | 2 | | idProduct |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13C | 2 | | bcdDevice |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13E | 1 | | bDeviceClass |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13F | 1 | | bDeviceSubClass |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x140 | 1 | | bDeviceProtocol |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x141 | 1 | | bConfigurationValue |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x142 | 1 | | bNumConfigurations |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x143 | 1 | | bNumInterfaces |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x144 | | m_0 | From now on each interface is described, all |
|
||||
| | | | together bNumInterfaces times, with the |
|
||||
| | | | the following 4 fields: |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| | 1 | | bInterfaceClass |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x145 | 1 | | bInterfaceSubClass |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x146 | 1 | | bInterfaceProtocol |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x147 | 1 | | padding byte for alignment, shall be set to zero |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0xC + | | | The second exported USB device starts at i=1 |
|
||||
| i*0x138 + | | | with the busid field. |
|
||||
| m_(i-1)*4 | | | |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
|
||||
OP_REQ_IMPORT: Request to import (attach) a remote USB device.
|
||||
OP_REQ_IMPORT:
|
||||
Request to import (attach) a remote USB device.
|
||||
|
||||
Offset | Length | Value | Description
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
2 | 2 | 0x8003 | Command code: import a remote USB device.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
4 | 4 | 0x00000000 | Status: unused, shall be set to 0
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
8 | 32 | | busid: the busid of the exported device on the
|
||||
| | | remote host. The possible values are taken
|
||||
| | | from the message field OP_REP_DEVLIST.busid.
|
||||
| | | A string closed with zero, the unused bytes
|
||||
| | | shall be filled with zeros.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| Offset | Length | Value | Description |
|
||||
+===========+========+============+===================================================+
|
||||
| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 2 | 2 | 0x8003 | Command code: import a remote USB device. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 8 | 32 | | busid: the busid of the exported device on the |
|
||||
| | | | remote host. The possible values are taken |
|
||||
| | | | from the message field OP_REP_DEVLIST.busid. |
|
||||
| | | | A string closed with zero, the unused bytes |
|
||||
| | | | shall be filled with zeros. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
|
||||
OP_REP_IMPORT: Reply to import (attach) a remote USB device.
|
||||
OP_REP_IMPORT:
|
||||
Reply to import (attach) a remote USB device.
|
||||
|
||||
Offset | Length | Value | Description
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
2 | 2 | 0x0003 | Reply code: Reply to import.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
4 | 4 | 0x00000000 | Status: 0 for OK
|
||||
| | | 1 for error
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
8 | | | From now on comes the details of the imported
|
||||
| | | device, if the previous status field was OK (0),
|
||||
| | | otherwise the reply ends with the status field.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
| 256 | | path: Path of the device on the host exporting the
|
||||
| | | USB device, string closed with zero byte, e.g.
|
||||
| | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
|
||||
| | | The unused bytes shall be filled with zero
|
||||
| | | bytes.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x108 | 32 | | busid: Bus ID of the exported device, string
|
||||
| | | closed with zero byte, e.g. "3-2". The unused
|
||||
| | | bytes shall be filled with zero bytes.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x128 | 4 | | busnum
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x12C | 4 | | devnum
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x130 | 4 | | speed
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x134 | 2 | | idVendor
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x136 | 2 | | idProduct
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x138 | 2 | | bcdDevice
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x139 | 1 | | bDeviceClass
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13A | 1 | | bDeviceSubClass
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13B | 1 | | bDeviceProtocol
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13C | 1 | | bConfigurationValue
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13D | 1 | | bNumConfigurations
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x13E | 1 | | bNumInterfaces
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| Offset | Length | Value | Description |
|
||||
+===========+========+============+===================================================+
|
||||
| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 2 | 2 | 0x0003 | Reply code: Reply to import. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 4 | 4 | 0x00000000 | Status: |
|
||||
| | | | |
|
||||
| | | | - 0 for OK |
|
||||
| | | | - 1 for error |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 8 | | | From now on comes the details of the imported |
|
||||
| | | | device, if the previous status field was OK (0), |
|
||||
| | | | otherwise the reply ends with the status field. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| | 256 | | path: Path of the device on the host exporting the|
|
||||
| | | | USB device, string closed with zero byte, e.g. |
|
||||
| | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" |
|
||||
| | | | The unused bytes shall be filled with zero |
|
||||
| | | | bytes. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x108 | 32 | | busid: Bus ID of the exported device, string |
|
||||
| | | | closed with zero byte, e.g. "3-2". The unused |
|
||||
| | | | bytes shall be filled with zero bytes. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x128 | 4 | | busnum |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x12C | 4 | | devnum |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x130 | 4 | | speed |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x134 | 2 | | idVendor |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x136 | 2 | | idProduct |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x138 | 2 | | bcdDevice |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x139 | 1 | | bDeviceClass |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13A | 1 | | bDeviceSubClass |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13B | 1 | | bDeviceProtocol |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13C | 1 | | bConfigurationValue |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13D | 1 | | bNumConfigurations |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x13E | 1 | | bNumInterfaces |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
|
||||
USBIP_CMD_SUBMIT: Submit an URB
|
||||
USBIP_CMD_SUBMIT:
|
||||
Submit an URB
|
||||
|
||||
Offset | Length | Value | Description
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0 | 4 | 0x00000001 | command: Submit an URB
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
4 | 4 | | seqnum: the sequence number of the URB to submit
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
8 | 4 | | devid
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0xC | 4 | | direction: 0: USBIP_DIR_OUT
|
||||
| | | 1: USBIP_DIR_IN
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x10 | 4 | | ep: endpoint number, possible values are: 0...15
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x14 | 4 | | transfer_flags: possible values depend on the
|
||||
| | | URB transfer type, see below
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x18 | 4 | | transfer_buffer_length
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x1C | 4 | | start_frame: specify the selected frame to
|
||||
| | | transmit an ISO frame, ignored if URB_ISO_ASAP
|
||||
| | | is specified at transfer_flags
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x20 | 4 | | number_of_packets: number of ISO packets
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x24 | 4 | | interval: maximum time for the request on the
|
||||
| | | server-side host controller
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x28 | 8 | | setup: data bytes for USB setup, filled with
|
||||
| | | zeros if not used
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x30 | | | URB data. For ISO transfers the padding between
|
||||
| | | each ISO packets is not transmitted.
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| Offset | Length | Value | Description |
|
||||
+===========+========+============+===================================================+
|
||||
| 0 | 4 | 0x00000001 | command: Submit an URB |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 4 | 4 | | seqnum: the sequence number of the URB to submit |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 8 | 4 | | devid |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0xC | 4 | | direction: |
|
||||
| | | | |
|
||||
| | | | - 0: USBIP_DIR_OUT |
|
||||
| | | | - 1: USBIP_DIR_IN |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x10 | 4 | | ep: endpoint number, possible values are: 0...15 |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x14 | 4 | | transfer_flags: possible values depend on the |
|
||||
| | | | URB transfer type, see below |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x18 | 4 | | transfer_buffer_length |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x1C | 4 | | start_frame: specify the selected frame to |
|
||||
| | | | transmit an ISO frame, ignored if URB_ISO_ASAP |
|
||||
| | | | is specified at transfer_flags |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x20 | 4 | | number_of_packets: number of ISO packets |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x24 | 4 | | interval: maximum time for the request on the |
|
||||
| | | | server-side host controller |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x28 | 8 | | setup: data bytes for USB setup, filled with |
|
||||
| | | | zeros if not used |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x30 | | | URB data. For ISO transfers the padding between |
|
||||
| | | | each ISO packets is not transmitted. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
|
||||
|
||||
Allowed transfer_flags | value | control | interrupt | bulk | isochronous
|
||||
-------------------------+------------+---------+-----------+----------+-------------
|
||||
URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no
|
||||
URB_ISO_ASAP | 0x00000002 | no | no | no | yes
|
||||
URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes
|
||||
URB_ZERO_PACKET | 0x00000040 | no | no | only out | no
|
||||
URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes
|
||||
URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes
|
||||
URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes
|
||||
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||
| Allowed transfer_flags | value | control | interrupt | bulk | isochronous |
|
||||
+=========================+============+=========+===========+==========+=============+
|
||||
| URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no |
|
||||
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||
| URB_ISO_ASAP | 0x00000002 | no | no | no | yes |
|
||||
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||
| URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes |
|
||||
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||
| URB_ZERO_PACKET | 0x00000040 | no | no | only out | no |
|
||||
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||
| URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes |
|
||||
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||
| URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes |
|
||||
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||
| URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes |
|
||||
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||
|
||||
|
||||
USBIP_RET_SUBMIT: Reply for submitting an URB
|
||||
USBIP_RET_SUBMIT:
|
||||
Reply for submitting an URB
|
||||
|
||||
Offset | Length | Value | Description
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0 | 4 | 0x00000003 | command
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
4 | 4 | | seqnum: URB sequence number
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
8 | 4 | | devid
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0xC | 4 | | direction: 0: USBIP_DIR_OUT
|
||||
| | | 1: USBIP_DIR_IN
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x10 | 4 | | ep: endpoint number
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x14 | 4 | | status: zero for successful URB transaction,
|
||||
| | | otherwise some kind of error happened.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x18 | 4 | n | actual_length: number of URB data bytes
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x1C | 4 | | start_frame: for an ISO frame the actually
|
||||
| | | selected frame for transmit.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x20 | 4 | | number_of_packets
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x24 | 4 | | error_count
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x28 | 8 | | setup: data bytes for USB setup, filled with
|
||||
| | | zeros if not used
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x30 | n | | URB data bytes. For ISO transfers the padding
|
||||
| | | between each ISO packets is not transmitted.
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| Offset | Length | Value | Description |
|
||||
+===========+========+============+===================================================+
|
||||
| 0 | 4 | 0x00000003 | command |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 4 | 4 | | seqnum: URB sequence number |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 8 | 4 | | devid |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0xC | 4 | | direction: |
|
||||
| | | | |
|
||||
| | | | - 0: USBIP_DIR_OUT |
|
||||
| | | | - 1: USBIP_DIR_IN |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x10 | 4 | | ep: endpoint number |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x14 | 4 | | status: zero for successful URB transaction, |
|
||||
| | | | otherwise some kind of error happened. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x18 | 4 | n | actual_length: number of URB data bytes |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x1C | 4 | | start_frame: for an ISO frame the actually |
|
||||
| | | | selected frame for transmit. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x20 | 4 | | number_of_packets |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x24 | 4 | | error_count |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x28 | 8 | | setup: data bytes for USB setup, filled with |
|
||||
| | | | zeros if not used |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x30 | n | | URB data bytes. For ISO transfers the padding |
|
||||
| | | | between each ISO packets is not transmitted. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
|
||||
USBIP_CMD_UNLINK: Unlink an URB
|
||||
USBIP_CMD_UNLINK:
|
||||
Unlink an URB
|
||||
|
||||
Offset | Length | Value | Description
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0 | 4 | 0x00000002 | command: URB unlink command
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so?
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
8 | 4 | | devid
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0xC | 4 | | direction: 0: USBIP_DIR_OUT
|
||||
| | | 1: USBIP_DIR_IN
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x10 | 4 | | ep: endpoint number: zero
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x14 | 4 | | seqnum: the URB sequence number given previously
|
||||
| | | at USBIP_CMD_SUBMIT.seqnum field
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x30 | n | | URB data bytes. For ISO transfers the padding
|
||||
| | | between each ISO packets is not transmitted.
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| Offset | Length | Value | Description |
|
||||
+===========+========+============+===================================================+
|
||||
| 0 | 4 | 0x00000002 | command: URB unlink command |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 4 | 4 | | seqnum: URB sequence number to unlink: |
|
||||
| | | | |
|
||||
| | | | FIXME: |
|
||||
| | | | is this so? |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 8 | 4 | | devid |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0xC | 4 | | direction: |
|
||||
| | | | |
|
||||
| | | | - 0: USBIP_DIR_OUT |
|
||||
| | | | - 1: USBIP_DIR_IN |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x10 | 4 | | ep: endpoint number: zero |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x14 | 4 | | seqnum: the URB sequence number given previously |
|
||||
| | | | at USBIP_CMD_SUBMIT.seqnum field |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x30 | n | | URB data bytes. For ISO transfers the padding |
|
||||
| | | | between each ISO packets is not transmitted. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
|
||||
USBIP_RET_UNLINK: Reply for URB unlink
|
||||
USBIP_RET_UNLINK:
|
||||
Reply for URB unlink
|
||||
|
||||
Offset | Length | Value | Description
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0 | 4 | 0x00000004 | command: reply for the URB unlink command
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
4 | 4 | | seqnum: the unlinked URB sequence number
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
8 | 4 | | devid
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0xC | 4 | | direction: 0: USBIP_DIR_OUT
|
||||
| | | 1: USBIP_DIR_IN
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x10 | 4 | | ep: endpoint number
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x14 | 4 | | status: This is the value contained in the
|
||||
| | | urb->status in the URB completition handler.
|
||||
| | | FIXME: a better explanation needed.
|
||||
-----------+--------+------------+---------------------------------------------------
|
||||
0x30 | n | | URB data bytes. For ISO transfers the padding
|
||||
| | | between each ISO packets is not transmitted.
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| Offset | Length | Value | Description |
|
||||
+===========+========+============+===================================================+
|
||||
| 0 | 4 | 0x00000004 | command: reply for the URB unlink command |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 4 | 4 | | seqnum: the unlinked URB sequence number |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 8 | 4 | | devid |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0xC | 4 | | direction: |
|
||||
| | | | |
|
||||
| | | | - 0: USBIP_DIR_OUT |
|
||||
| | | | - 1: USBIP_DIR_IN |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x10 | 4 | | ep: endpoint number |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x14 | 4 | | status: This is the value contained in the |
|
||||
| | | | urb->status in the URB completition handler. |
|
||||
| | | | |
|
||||
| | | | FIXME: |
|
||||
| | | | a better explanation needed. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
| 0x30 | n | | URB data bytes. For ISO transfers the padding |
|
||||
| | | | between each ISO packets is not transmitted. |
|
||||
+-----------+--------+------------+---------------------------------------------------+
|
||||
|
@ -1,4 +1,9 @@
|
||||
* Introduction
|
||||
======
|
||||
usbmon
|
||||
======
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The name "usbmon" in lowercase refers to a facility in kernel which is
|
||||
used to collect traces of I/O on the USB bus. This function is analogous
|
||||
@ -16,7 +21,8 @@ Two APIs are currently implemented: "text" and "binary". The binary API
|
||||
is available through a character device in /dev namespace and is an ABI.
|
||||
The text API is deprecated since 2.6.35, but available for convenience.
|
||||
|
||||
* How to use usbmon to collect raw text traces
|
||||
How to use usbmon to collect raw text traces
|
||||
============================================
|
||||
|
||||
Unlike the packet socket, usbmon has an interface which provides traces
|
||||
in a text format. This is used for two purposes. First, it serves as a
|
||||
@ -26,38 +32,41 @@ are finalized. Second, humans can read it in case tools are not available.
|
||||
To collect a raw text trace, execute following steps.
|
||||
|
||||
1. Prepare
|
||||
----------
|
||||
|
||||
Mount debugfs (it has to be enabled in your kernel configuration), and
|
||||
load the usbmon module (if built as module). The second step is skipped
|
||||
if usbmon is built into the kernel.
|
||||
if usbmon is built into the kernel::
|
||||
|
||||
# mount -t debugfs none_debugs /sys/kernel/debug
|
||||
# modprobe usbmon
|
||||
#
|
||||
# mount -t debugfs none_debugs /sys/kernel/debug
|
||||
# modprobe usbmon
|
||||
#
|
||||
|
||||
Verify that bus sockets are present.
|
||||
Verify that bus sockets are present:
|
||||
|
||||
# ls /sys/kernel/debug/usb/usbmon
|
||||
0s 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
|
||||
#
|
||||
# ls /sys/kernel/debug/usb/usbmon
|
||||
0s 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
|
||||
#
|
||||
|
||||
Now you can choose to either use the socket '0u' (to capture packets on all
|
||||
buses), and skip to step #3, or find the bus used by your device with step #2.
|
||||
This allows to filter away annoying devices that talk continuously.
|
||||
|
||||
2. Find which bus connects to the desired device
|
||||
------------------------------------------------
|
||||
|
||||
Run "cat /sys/kernel/debug/usb/devices", and find the T-line which corresponds
|
||||
to the device. Usually you do it by looking for the vendor string. If you have
|
||||
many similar devices, unplug one and compare the two
|
||||
/sys/kernel/debug/usb/devices outputs. The T-line will have a bus number.
|
||||
Example:
|
||||
|
||||
T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
|
||||
D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
||||
P: Vendor=0557 ProdID=2004 Rev= 1.00
|
||||
S: Manufacturer=ATEN
|
||||
S: Product=UC100KM V2.00
|
||||
Example::
|
||||
|
||||
T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
|
||||
D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
||||
P: Vendor=0557 ProdID=2004 Rev= 1.00
|
||||
S: Manufacturer=ATEN
|
||||
S: Product=UC100KM V2.00
|
||||
|
||||
"Bus=03" means it's bus 3. Alternatively, you can look at the output from
|
||||
"lsusb" and get the bus number from the appropriate line. Example:
|
||||
@ -65,23 +74,28 @@ S: Product=UC100KM V2.00
|
||||
Bus 003 Device 002: ID 0557:2004 ATEN UC100KM V2.00
|
||||
|
||||
3. Start 'cat'
|
||||
--------------
|
||||
|
||||
# cat /sys/kernel/debug/usb/usbmon/3u > /tmp/1.mon.out
|
||||
::
|
||||
|
||||
to listen on a single bus, otherwise, to listen on all buses, type:
|
||||
# cat /sys/kernel/debug/usb/usbmon/3u > /tmp/1.mon.out
|
||||
|
||||
# cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out
|
||||
to listen on a single bus, otherwise, to listen on all buses, type::
|
||||
|
||||
# cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out
|
||||
|
||||
This process will read until it is killed. Naturally, the output can be
|
||||
redirected to a desirable location. This is preferred, because it is going
|
||||
to be quite long.
|
||||
|
||||
4. Perform the desired operation on the USB bus
|
||||
-----------------------------------------------
|
||||
|
||||
This is where you do something that creates the traffic: plug in a flash key,
|
||||
copy files, control a webcam, etc.
|
||||
|
||||
5. Kill cat
|
||||
-----------
|
||||
|
||||
Usually it's done with a keyboard interrupt (Control-C).
|
||||
|
||||
@ -89,7 +103,8 @@ At this point the output file (/tmp/1.mon.out in this example) can be saved,
|
||||
sent by e-mail, or inspected with a text editor. In the last case make sure
|
||||
that the file size is not excessive for your favourite editor.
|
||||
|
||||
* Raw text data format
|
||||
Raw text data format
|
||||
====================
|
||||
|
||||
Two formats are supported currently: the original, or '1t' format, and
|
||||
the '1u' format. The '1t' format is deprecated in kernel 2.6.21. The '1u'
|
||||
@ -122,10 +137,14 @@ Here is the list of words, from left to right:
|
||||
- "Address" word (formerly a "pipe"). It consists of four fields, separated by
|
||||
colons: URB type and direction, Bus number, Device address, Endpoint number.
|
||||
Type and direction are encoded with two bytes in the following manner:
|
||||
|
||||
== == =============================
|
||||
Ci Co Control input and output
|
||||
Zi Zo Isochronous input and output
|
||||
Ii Io Interrupt input and output
|
||||
Bi Bo Bulk input and output
|
||||
== == =============================
|
||||
|
||||
Bus number, Device address, and Endpoint are decimal numbers, but they may
|
||||
have leading zeros, for the sake of human readers.
|
||||
|
||||
@ -178,24 +197,25 @@ Here is the list of words, from left to right:
|
||||
|
||||
Examples:
|
||||
|
||||
An input control transfer to get a port status.
|
||||
An input control transfer to get a port status::
|
||||
|
||||
d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
|
||||
d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
|
||||
d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
|
||||
d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
|
||||
|
||||
An output bulk transfer to send a SCSI command 0x28 (READ_10) in a 31-byte
|
||||
Bulk wrapper to a storage device at address 5:
|
||||
Bulk wrapper to a storage device at address 5::
|
||||
|
||||
dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000
|
||||
dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
|
||||
dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 ad000000 00800000 80010a28 20000000 20000040 00000000 000000
|
||||
dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
|
||||
|
||||
* Raw binary format and API
|
||||
Raw binary format and API
|
||||
=========================
|
||||
|
||||
The overall architecture of the API is about the same as the one above,
|
||||
only the events are delivered in binary format. Each event is sent in
|
||||
the following structure (its name is made up, so that we can refer to it):
|
||||
the following structure (its name is made up, so that we can refer to it)::
|
||||
|
||||
struct usbmon_packet {
|
||||
struct usbmon_packet {
|
||||
u64 id; /* 0: URB ID - from submission to callback */
|
||||
unsigned char type; /* 8: Same as text; extensible. */
|
||||
unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */
|
||||
@ -220,7 +240,7 @@ struct usbmon_packet {
|
||||
int start_frame; /* 52: For ISO */
|
||||
unsigned int xfer_flags; /* 56: copy of URB's transfer_flags */
|
||||
unsigned int ndesc; /* 60: Actual number of ISO descriptors */
|
||||
}; /* 64 total length */
|
||||
}; /* 64 total length */
|
||||
|
||||
These events can be received from a character device by reading with read(2),
|
||||
with an ioctl(2), or by accessing the buffer with mmap. However, read(2)
|
||||
@ -244,12 +264,12 @@ no events are available.
|
||||
|
||||
MON_IOCG_STATS, defined as _IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats)
|
||||
|
||||
The argument is a pointer to the following structure:
|
||||
The argument is a pointer to the following structure::
|
||||
|
||||
struct mon_bin_stats {
|
||||
struct mon_bin_stats {
|
||||
u32 queued;
|
||||
u32 dropped;
|
||||
};
|
||||
};
|
||||
|
||||
The member "queued" refers to the number of events currently queued in the
|
||||
buffer (and not to the number of events processed since the last reset).
|
||||
@ -273,13 +293,13 @@ This call returns the current size of the buffer in bytes.
|
||||
|
||||
These calls wait for events to arrive if none were in the kernel buffer,
|
||||
then return the first event. The argument is a pointer to the following
|
||||
structure:
|
||||
structure::
|
||||
|
||||
struct mon_get_arg {
|
||||
struct mon_get_arg {
|
||||
struct usbmon_packet *hdr;
|
||||
void *data;
|
||||
size_t alloc; /* Length of data (can be zero) */
|
||||
};
|
||||
};
|
||||
|
||||
Before the call, hdr, data, and alloc should be filled. Upon return, the area
|
||||
pointed by hdr contains the next event structure, and the data buffer contains
|
||||
@ -290,13 +310,13 @@ The MON_IOCX_GET copies 48 bytes to hdr area, MON_IOCX_GETX copies 64 bytes.
|
||||
MON_IOCX_MFETCH, defined as _IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg)
|
||||
|
||||
This ioctl is primarily used when the application accesses the buffer
|
||||
with mmap(2). Its argument is a pointer to the following structure:
|
||||
with mmap(2). Its argument is a pointer to the following structure::
|
||||
|
||||
struct mon_mfetch_arg {
|
||||
struct mon_mfetch_arg {
|
||||
uint32_t *offvec; /* Vector of events fetched */
|
||||
uint32_t nfetch; /* Number of events to fetch (out: fetched) */
|
||||
uint32_t nflush; /* Number of events to flush */
|
||||
};
|
||||
};
|
||||
|
||||
The ioctl operates in 3 stages.
|
||||
|
||||
@ -329,7 +349,7 @@ be polled with select(2) and poll(2). But lseek(2) does not work.
|
||||
The basic idea is simple:
|
||||
|
||||
To prepare, map the buffer by getting the current size, then using mmap(2).
|
||||
Then, execute a loop similar to the one written in pseudo-code below:
|
||||
Then, execute a loop similar to the one written in pseudo-code below::
|
||||
|
||||
struct mon_mfetch_arg fetch;
|
||||
struct usbmon_packet *hdr;
|
||||
|
13
MAINTAINERS
13
MAINTAINERS
@ -15377,6 +15377,11 @@ M: Laxman Dewangan <ldewangan@nvidia.com>
|
||||
S: Supported
|
||||
F: drivers/spi/spi-tegra*
|
||||
|
||||
TEGRA XUSB PADCTL DRIVER
|
||||
M: JC Kuo <jckuo@nvidia.com>
|
||||
S: Supported
|
||||
F: drivers/phy/tegra/xusb*
|
||||
|
||||
TEHUTI ETHERNET DRIVER
|
||||
M: Andy Gospodarek <andy@greyhouse.net>
|
||||
L: netdev@vger.kernel.org
|
||||
@ -16155,6 +16160,14 @@ L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/usb/roles/intel-xhci-usb-role-switch.c
|
||||
|
||||
USB IP DRIVER FOR HISILICON KIRIN
|
||||
M: Yu Chen <chenyu56@huawei.com>
|
||||
M: Binghui Wang <wangbinghui@hisilicon.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/phy/phy-hi3660-usb3.txt
|
||||
F: drivers/phy/hisilicon/phy-hi3660-usb3.c
|
||||
|
||||
USB ISP116X DRIVER
|
||||
M: Olav Kongas <ok@artecdesign.ee>
|
||||
L: linux-usb@vger.kernel.org
|
||||
|
@ -616,6 +616,7 @@
|
||||
dr_mode = "host";
|
||||
phys = <&usbphy2>;
|
||||
phy-names = "usb2-phy";
|
||||
snps,reset-phy-on-wake;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
@ -904,6 +905,8 @@
|
||||
clocks = <&cru SCLK_OTGPHY0>;
|
||||
clock-names = "phyclk";
|
||||
#clock-cells = <0>;
|
||||
resets = <&cru SRST_USBOTG_PHY>;
|
||||
reset-names = "phy-reset";
|
||||
};
|
||||
|
||||
usbphy1: usb-phy@334 {
|
||||
@ -912,6 +915,8 @@
|
||||
clocks = <&cru SCLK_OTGPHY1>;
|
||||
clock-names = "phyclk";
|
||||
#clock-cells = <0>;
|
||||
resets = <&cru SRST_USBHOST0_PHY>;
|
||||
reset-names = "phy-reset";
|
||||
};
|
||||
|
||||
usbphy2: usb-phy@348 {
|
||||
@ -920,6 +925,8 @@
|
||||
clocks = <&cru SCLK_OTGPHY2>;
|
||||
clock-names = "phyclk";
|
||||
#clock-cells = <0>;
|
||||
resets = <&cru SRST_USBHOST1_PHY>;
|
||||
reset-names = "phy-reset";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -253,6 +253,12 @@ static const struct pci_device_id gpu_i2c_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, gpu_i2c_ids);
|
||||
|
||||
static const struct property_entry ccgx_props[] = {
|
||||
/* Use FW built for NVIDIA (nv) only */
|
||||
PROPERTY_ENTRY_U16("ccgx,firmware-build", ('n' << 8) | 'v'),
|
||||
{ }
|
||||
};
|
||||
|
||||
static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
|
||||
{
|
||||
struct i2c_client *ccgx_client;
|
||||
@ -267,6 +273,7 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
|
||||
sizeof(i2cd->gpu_ccgx_ucsi->type));
|
||||
i2cd->gpu_ccgx_ucsi->addr = 0x8;
|
||||
i2cd->gpu_ccgx_ucsi->irq = irq;
|
||||
i2cd->gpu_ccgx_ucsi->properties = ccgx_props;
|
||||
ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
|
||||
if (!ccgx_client)
|
||||
return -ENODEV;
|
||||
|
@ -36,3 +36,25 @@ config PHY_MESON_GXL_USB3
|
||||
Enable this to support the Meson USB3 PHY and OTG detection
|
||||
IP block found in Meson GXL and GXM SoCs.
|
||||
If unsure, say N.
|
||||
|
||||
config PHY_MESON_G12A_USB2
|
||||
tristate "Meson G12A USB2 PHY driver"
|
||||
default ARCH_MESON
|
||||
depends on OF && (ARCH_MESON || COMPILE_TEST)
|
||||
select GENERIC_PHY
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Enable this to support the Meson USB2 PHYs found in Meson
|
||||
G12A SoCs.
|
||||
If unsure, say N.
|
||||
|
||||
config PHY_MESON_G12A_USB3_PCIE
|
||||
tristate "Meson G12A USB3+PCIE Combo PHY driver"
|
||||
default ARCH_MESON
|
||||
depends on OF && (ARCH_MESON || COMPILE_TEST)
|
||||
select GENERIC_PHY
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Enable this to support the Meson USB3 + PCIE Combo PHY found
|
||||
in Meson G12A SoCs.
|
||||
If unsure, say N.
|
||||
|
@ -1,3 +1,5 @@
|
||||
obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o
|
||||
obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o
|
||||
obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o
|
||||
obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o
|
||||
obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o
|
||||
|
341
drivers/phy/amlogic/phy-meson-g12a-usb2.c
Normal file
341
drivers/phy/amlogic/phy-meson-g12a-usb2.c
Normal file
@ -0,0 +1,341 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Meson G12A USB2 PHY driver
|
||||
*
|
||||
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved
|
||||
* Copyright (C) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define PHY_CTRL_R0 0x0
|
||||
#define PHY_CTRL_R1 0x4
|
||||
#define PHY_CTRL_R2 0x8
|
||||
#define PHY_CTRL_R3 0xc
|
||||
#define PHY_CTRL_R3_SQUELCH_REF GENMASK(1, 0)
|
||||
#define PHY_CTRL_R3_HSDIC_REF GENMASK(3, 2)
|
||||
#define PHY_CTRL_R3_DISC_THRESH GENMASK(7, 4)
|
||||
|
||||
#define PHY_CTRL_R4 0x10
|
||||
#define PHY_CTRL_R4_CALIB_CODE_7_0 GENMASK(7, 0)
|
||||
#define PHY_CTRL_R4_CALIB_CODE_15_8 GENMASK(15, 8)
|
||||
#define PHY_CTRL_R4_CALIB_CODE_23_16 GENMASK(23, 16)
|
||||
#define PHY_CTRL_R4_I_C2L_CAL_EN BIT(24)
|
||||
#define PHY_CTRL_R4_I_C2L_CAL_RESET_N BIT(25)
|
||||
#define PHY_CTRL_R4_I_C2L_CAL_DONE BIT(26)
|
||||
#define PHY_CTRL_R4_TEST_BYPASS_MODE_EN BIT(27)
|
||||
#define PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0 GENMASK(29, 28)
|
||||
#define PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2 GENMASK(31, 30)
|
||||
|
||||
#define PHY_CTRL_R5 0x14
|
||||
#define PHY_CTRL_R6 0x18
|
||||
#define PHY_CTRL_R7 0x1c
|
||||
#define PHY_CTRL_R8 0x20
|
||||
#define PHY_CTRL_R9 0x24
|
||||
#define PHY_CTRL_R10 0x28
|
||||
#define PHY_CTRL_R11 0x2c
|
||||
#define PHY_CTRL_R12 0x30
|
||||
#define PHY_CTRL_R13 0x34
|
||||
#define PHY_CTRL_R13_CUSTOM_PATTERN_19 GENMASK(7, 0)
|
||||
#define PHY_CTRL_R13_LOAD_STAT BIT(14)
|
||||
#define PHY_CTRL_R13_UPDATE_PMA_SIGNALS BIT(15)
|
||||
#define PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET GENMASK(20, 16)
|
||||
#define PHY_CTRL_R13_CLEAR_HOLD_HS_DISCONNECT BIT(21)
|
||||
#define PHY_CTRL_R13_BYPASS_HOST_DISCONNECT_VAL BIT(22)
|
||||
#define PHY_CTRL_R13_BYPASS_HOST_DISCONNECT_EN BIT(23)
|
||||
#define PHY_CTRL_R13_I_C2L_HS_EN BIT(24)
|
||||
#define PHY_CTRL_R13_I_C2L_FS_EN BIT(25)
|
||||
#define PHY_CTRL_R13_I_C2L_LS_EN BIT(26)
|
||||
#define PHY_CTRL_R13_I_C2L_HS_OE BIT(27)
|
||||
#define PHY_CTRL_R13_I_C2L_FS_OE BIT(28)
|
||||
#define PHY_CTRL_R13_I_C2L_HS_RX_EN BIT(29)
|
||||
#define PHY_CTRL_R13_I_C2L_FSLS_RX_EN BIT(30)
|
||||
|
||||
#define PHY_CTRL_R14 0x38
|
||||
#define PHY_CTRL_R14_I_RDP_EN BIT(0)
|
||||
#define PHY_CTRL_R14_I_RPU_SW1_EN BIT(1)
|
||||
#define PHY_CTRL_R14_I_RPU_SW2_EN GENMASK(2, 3)
|
||||
#define PHY_CTRL_R14_PG_RSTN BIT(4)
|
||||
#define PHY_CTRL_R14_I_C2L_DATA_16_8 BIT(5)
|
||||
#define PHY_CTRL_R14_I_C2L_ASSERT_SINGLE_EN_ZERO BIT(6)
|
||||
#define PHY_CTRL_R14_BYPASS_CTRL_7_0 GENMASK(15, 8)
|
||||
#define PHY_CTRL_R14_BYPASS_CTRL_15_8 GENMASK(23, 16)
|
||||
|
||||
#define PHY_CTRL_R15 0x3c
|
||||
#define PHY_CTRL_R16 0x40
|
||||
#define PHY_CTRL_R16_MPLL_M GENMASK(8, 0)
|
||||
#define PHY_CTRL_R16_MPLL_N GENMASK(14, 10)
|
||||
#define PHY_CTRL_R16_MPLL_TDC_MODE BIT(20)
|
||||
#define PHY_CTRL_R16_MPLL_SDM_EN BIT(21)
|
||||
#define PHY_CTRL_R16_MPLL_LOAD BIT(22)
|
||||
#define PHY_CTRL_R16_MPLL_DCO_SDM_EN BIT(23)
|
||||
#define PHY_CTRL_R16_MPLL_LOCK_LONG GENMASK(25, 24)
|
||||
#define PHY_CTRL_R16_MPLL_LOCK_F BIT(26)
|
||||
#define PHY_CTRL_R16_MPLL_FAST_LOCK BIT(27)
|
||||
#define PHY_CTRL_R16_MPLL_EN BIT(28)
|
||||
#define PHY_CTRL_R16_MPLL_RESET BIT(29)
|
||||
#define PHY_CTRL_R16_MPLL_LOCK BIT(30)
|
||||
#define PHY_CTRL_R16_MPLL_LOCK_DIG BIT(31)
|
||||
|
||||
#define PHY_CTRL_R17 0x44
|
||||
#define PHY_CTRL_R17_MPLL_FRAC_IN GENMASK(13, 0)
|
||||
#define PHY_CTRL_R17_MPLL_FIX_EN BIT(16)
|
||||
#define PHY_CTRL_R17_MPLL_LAMBDA1 GENMASK(19, 17)
|
||||
#define PHY_CTRL_R17_MPLL_LAMBDA0 GENMASK(22, 20)
|
||||
#define PHY_CTRL_R17_MPLL_FILTER_MODE BIT(23)
|
||||
#define PHY_CTRL_R17_MPLL_FILTER_PVT2 GENMASK(27, 24)
|
||||
#define PHY_CTRL_R17_MPLL_FILTER_PVT1 GENMASK(31, 28)
|
||||
|
||||
#define PHY_CTRL_R18 0x48
|
||||
#define PHY_CTRL_R18_MPLL_LKW_SEL GENMASK(1, 0)
|
||||
#define PHY_CTRL_R18_MPLL_LK_W GENMASK(5, 2)
|
||||
#define PHY_CTRL_R18_MPLL_LK_S GENMASK(11, 6)
|
||||
#define PHY_CTRL_R18_MPLL_DCO_M_EN BIT(12)
|
||||
#define PHY_CTRL_R18_MPLL_DCO_CLK_SEL BIT(13)
|
||||
#define PHY_CTRL_R18_MPLL_PFD_GAIN GENMASK(15, 14)
|
||||
#define PHY_CTRL_R18_MPLL_ROU GENMASK(18, 16)
|
||||
#define PHY_CTRL_R18_MPLL_DATA_SEL GENMASK(21, 19)
|
||||
#define PHY_CTRL_R18_MPLL_BIAS_ADJ GENMASK(23, 22)
|
||||
#define PHY_CTRL_R18_MPLL_BB_MODE GENMASK(25, 24)
|
||||
#define PHY_CTRL_R18_MPLL_ALPHA GENMASK(28, 26)
|
||||
#define PHY_CTRL_R18_MPLL_ADJ_LDO GENMASK(30, 29)
|
||||
#define PHY_CTRL_R18_MPLL_ACG_RANGE BIT(31)
|
||||
|
||||
#define PHY_CTRL_R19 0x4c
|
||||
#define PHY_CTRL_R20 0x50
|
||||
#define PHY_CTRL_R20_USB2_IDDET_EN BIT(0)
|
||||
#define PHY_CTRL_R20_USB2_OTG_VBUS_TRIM_2_0 GENMASK(3, 1)
|
||||
#define PHY_CTRL_R20_USB2_OTG_VBUSDET_EN BIT(4)
|
||||
#define PHY_CTRL_R20_USB2_AMON_EN BIT(5)
|
||||
#define PHY_CTRL_R20_USB2_CAL_CODE_R5 BIT(6)
|
||||
#define PHY_CTRL_R20_BYPASS_OTG_DET BIT(7)
|
||||
#define PHY_CTRL_R20_USB2_DMON_EN BIT(8)
|
||||
#define PHY_CTRL_R20_USB2_DMON_SEL_3_0 GENMASK(12, 9)
|
||||
#define PHY_CTRL_R20_USB2_EDGE_DRV_EN BIT(13)
|
||||
#define PHY_CTRL_R20_USB2_EDGE_DRV_TRIM_1_0 GENMASK(15, 14)
|
||||
#define PHY_CTRL_R20_USB2_BGR_ADJ_4_0 GENMASK(20, 16)
|
||||
#define PHY_CTRL_R20_USB2_BGR_START BIT(21)
|
||||
#define PHY_CTRL_R20_USB2_BGR_VREF_4_0 GENMASK(28, 24)
|
||||
#define PHY_CTRL_R20_USB2_BGR_DBG_1_0 GENMASK(30, 29)
|
||||
#define PHY_CTRL_R20_BYPASS_CAL_DONE_R5 BIT(31)
|
||||
|
||||
#define PHY_CTRL_R21 0x54
|
||||
#define PHY_CTRL_R21_USB2_BGR_FORCE BIT(0)
|
||||
#define PHY_CTRL_R21_USB2_CAL_ACK_EN BIT(1)
|
||||
#define PHY_CTRL_R21_USB2_OTG_ACA_EN BIT(2)
|
||||
#define PHY_CTRL_R21_USB2_TX_STRG_PD BIT(3)
|
||||
#define PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0 GENMASK(5, 4)
|
||||
#define PHY_CTRL_R21_BYPASS_UTMI_CNTR GENMASK(15, 6)
|
||||
#define PHY_CTRL_R21_BYPASS_UTMI_REG GENMASK(25, 20)
|
||||
|
||||
#define PHY_CTRL_R22 0x58
|
||||
#define PHY_CTRL_R23 0x5c
|
||||
|
||||
#define RESET_COMPLETE_TIME 1000
|
||||
#define PLL_RESET_COMPLETE_TIME 100
|
||||
|
||||
struct phy_meson_g12a_usb2_priv {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
struct reset_control *reset;
|
||||
};
|
||||
|
||||
static const struct regmap_config phy_meson_g12a_usb2_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = PHY_CTRL_R23,
|
||||
};
|
||||
|
||||
static int phy_meson_g12a_usb2_init(struct phy *phy)
|
||||
{
|
||||
struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
ret = reset_control_reset(priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
udelay(RESET_COMPLETE_TIME);
|
||||
|
||||
/* usb2_otg_aca_en == 0 */
|
||||
regmap_update_bits(priv->regmap, PHY_CTRL_R21,
|
||||
PHY_CTRL_R21_USB2_OTG_ACA_EN, 0);
|
||||
|
||||
/* PLL Setup : 24MHz * 20 / 1 = 480MHz */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R16,
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_M, 20) |
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_N, 1) |
|
||||
PHY_CTRL_R16_MPLL_LOAD |
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_LOCK_LONG, 1) |
|
||||
PHY_CTRL_R16_MPLL_FAST_LOCK |
|
||||
PHY_CTRL_R16_MPLL_EN |
|
||||
PHY_CTRL_R16_MPLL_RESET);
|
||||
|
||||
regmap_write(priv->regmap, PHY_CTRL_R17,
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_FRAC_IN, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_LAMBDA1, 7) |
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_LAMBDA0, 7) |
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT2, 2) |
|
||||
FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT1, 9));
|
||||
|
||||
regmap_write(priv->regmap, PHY_CTRL_R18,
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) |
|
||||
FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) |
|
||||
PHY_CTRL_R18_MPLL_ACG_RANGE);
|
||||
|
||||
udelay(PLL_RESET_COMPLETE_TIME);
|
||||
|
||||
/* UnReset PLL */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R16,
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_M, 20) |
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_N, 1) |
|
||||
PHY_CTRL_R16_MPLL_LOAD |
|
||||
FIELD_PREP(PHY_CTRL_R16_MPLL_LOCK_LONG, 1) |
|
||||
PHY_CTRL_R16_MPLL_FAST_LOCK |
|
||||
PHY_CTRL_R16_MPLL_EN);
|
||||
|
||||
/* PHY Tuning */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R20,
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_OTG_VBUS_TRIM_2_0, 4) |
|
||||
PHY_CTRL_R20_USB2_OTG_VBUSDET_EN |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_DMON_SEL_3_0, 15) |
|
||||
PHY_CTRL_R20_USB2_EDGE_DRV_EN |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_EDGE_DRV_TRIM_1_0, 3) |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_BGR_ADJ_4_0, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_BGR_VREF_4_0, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R20_USB2_BGR_DBG_1_0, 0));
|
||||
|
||||
regmap_write(priv->regmap, PHY_CTRL_R4,
|
||||
FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) |
|
||||
FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) |
|
||||
FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) |
|
||||
PHY_CTRL_R4_TEST_BYPASS_MODE_EN |
|
||||
FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0));
|
||||
|
||||
/* Tuning Disconnect Threshold */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R3,
|
||||
FIELD_PREP(PHY_CTRL_R3_SQUELCH_REF, 0) |
|
||||
FIELD_PREP(PHY_CTRL_R3_HSDIC_REF, 1) |
|
||||
FIELD_PREP(PHY_CTRL_R3_DISC_THRESH, 3));
|
||||
|
||||
/* Analog Settings */
|
||||
regmap_write(priv->regmap, PHY_CTRL_R14, 0);
|
||||
regmap_write(priv->regmap, PHY_CTRL_R13,
|
||||
PHY_CTRL_R13_UPDATE_PMA_SIGNALS |
|
||||
FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_meson_g12a_usb2_exit(struct phy *phy)
|
||||
{
|
||||
struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
return reset_control_reset(priv->reset);
|
||||
}
|
||||
|
||||
/* set_mode is not needed, mode setting is handled via the UTMI bus */
|
||||
static const struct phy_ops phy_meson_g12a_usb2_ops = {
|
||||
.init = phy_meson_g12a_usb2_init,
|
||||
.exit = phy_meson_g12a_usb2_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int phy_meson_g12a_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy_provider *phy_provider;
|
||||
struct resource *res;
|
||||
struct phy_meson_g12a_usb2_priv *priv;
|
||||
struct phy *phy;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&phy_meson_g12a_usb2_regmap_conf);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->clk = devm_clk_get(dev, "xtal");
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
priv->reset = devm_reset_control_get(dev, "phy");
|
||||
if (IS_ERR(priv->reset))
|
||||
return PTR_ERR(priv->reset);
|
||||
|
||||
ret = reset_control_deassert(priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &phy_meson_g12a_usb2_ops);
|
||||
if (IS_ERR(phy)) {
|
||||
ret = PTR_ERR(phy);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_set_bus_width(phy, 8);
|
||||
phy_set_drvdata(phy, priv);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id phy_meson_g12a_usb2_of_match[] = {
|
||||
{ .compatible = "amlogic,g12a-usb2-phy", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, phy_meson_g12a_usb2_of_match);
|
||||
|
||||
static struct platform_driver phy_meson_g12a_usb2_driver = {
|
||||
.probe = phy_meson_g12a_usb2_probe,
|
||||
.driver = {
|
||||
.name = "phy-meson-g12a-usb2",
|
||||
.of_match_table = phy_meson_g12a_usb2_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(phy_meson_g12a_usb2_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_DESCRIPTION("Meson G12A USB2 PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
413
drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c
Normal file
413
drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c
Normal file
@ -0,0 +1,413 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Amlogic G12A USB3 + PCIE Combo PHY driver
|
||||
*
|
||||
* Copyright (C) 2017 Amlogic, Inc. All rights reserved
|
||||
* Copyright (C) 2019 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
|
||||
#define PHY_R0 0x00
|
||||
#define PHY_R0_PCIE_POWER_STATE GENMASK(4, 0)
|
||||
#define PHY_R0_PCIE_USB3_SWITCH GENMASK(6, 5)
|
||||
|
||||
#define PHY_R1 0x04
|
||||
#define PHY_R1_PHY_TX1_TERM_OFFSET GENMASK(4, 0)
|
||||
#define PHY_R1_PHY_TX0_TERM_OFFSET GENMASK(9, 5)
|
||||
#define PHY_R1_PHY_RX1_EQ GENMASK(12, 10)
|
||||
#define PHY_R1_PHY_RX0_EQ GENMASK(15, 13)
|
||||
#define PHY_R1_PHY_LOS_LEVEL GENMASK(20, 16)
|
||||
#define PHY_R1_PHY_LOS_BIAS GENMASK(23, 21)
|
||||
#define PHY_R1_PHY_REF_CLKDIV2 BIT(24)
|
||||
#define PHY_R1_PHY_MPLL_MULTIPLIER GENMASK(31, 25)
|
||||
|
||||
#define PHY_R2 0x08
|
||||
#define PHY_R2_PCS_TX_DEEMPH_GEN2_6DB GENMASK(5, 0)
|
||||
#define PHY_R2_PCS_TX_DEEMPH_GEN2_3P5DB GENMASK(11, 6)
|
||||
#define PHY_R2_PCS_TX_DEEMPH_GEN1 GENMASK(17, 12)
|
||||
#define PHY_R2_PHY_TX_VBOOST_LVL GENMASK(20, 18)
|
||||
|
||||
#define PHY_R4 0x10
|
||||
#define PHY_R4_PHY_CR_WRITE BIT(0)
|
||||
#define PHY_R4_PHY_CR_READ BIT(1)
|
||||
#define PHY_R4_PHY_CR_DATA_IN GENMASK(17, 2)
|
||||
#define PHY_R4_PHY_CR_CAP_DATA BIT(18)
|
||||
#define PHY_R4_PHY_CR_CAP_ADDR BIT(19)
|
||||
|
||||
#define PHY_R5 0x14
|
||||
#define PHY_R5_PHY_CR_DATA_OUT GENMASK(15, 0)
|
||||
#define PHY_R5_PHY_CR_ACK BIT(16)
|
||||
#define PHY_R5_PHY_BS_OUT BIT(17)
|
||||
|
||||
struct phy_g12a_usb3_pcie_priv {
|
||||
struct regmap *regmap;
|
||||
struct regmap *regmap_cr;
|
||||
struct clk *clk_ref;
|
||||
struct reset_control *reset;
|
||||
struct phy *phy;
|
||||
unsigned int mode;
|
||||
};
|
||||
|
||||
static const struct regmap_config phy_g12a_usb3_pcie_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = PHY_R5,
|
||||
};
|
||||
|
||||
static int phy_g12a_usb3_pcie_cr_bus_addr(struct phy_g12a_usb3_pcie_priv *priv,
|
||||
unsigned int addr)
|
||||
{
|
||||
unsigned int val, reg;
|
||||
int ret;
|
||||
|
||||
reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, addr);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_ADDR);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
!(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_g12a_usb3_pcie_cr_bus_read(void *context, unsigned int addr,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct phy_g12a_usb3_pcie_priv *priv = context;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, 0);
|
||||
regmap_write(priv->regmap, PHY_R4, PHY_R4_PHY_CR_READ);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*data = FIELD_GET(PHY_R5_PHY_CR_DATA_OUT, val);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, 0);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
!(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_g12a_usb3_pcie_cr_bus_write(void *context, unsigned int addr,
|
||||
unsigned int data)
|
||||
{
|
||||
struct phy_g12a_usb3_pcie_priv *priv = context;
|
||||
unsigned int val, reg;
|
||||
int ret;
|
||||
|
||||
ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, data);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_DATA);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK) == 0,
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_WRITE);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK),
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_write(priv->regmap, PHY_R4, reg);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
|
||||
(val & PHY_R5_PHY_CR_ACK) == 0,
|
||||
5, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config phy_g12a_usb3_pcie_cr_regmap_conf = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 16,
|
||||
.reg_read = phy_g12a_usb3_pcie_cr_bus_read,
|
||||
.reg_write = phy_g12a_usb3_pcie_cr_bus_write,
|
||||
.max_register = 0xffff,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static int phy_g12a_usb3_init(struct phy *phy)
|
||||
{
|
||||
struct phy_g12a_usb3_pcie_priv *priv = phy_get_drvdata(phy);
|
||||
int data, ret;
|
||||
|
||||
/* Switch PHY to USB3 */
|
||||
/* TODO figure out how to handle when PCIe was set in the bootloader */
|
||||
regmap_update_bits(priv->regmap, PHY_R0,
|
||||
PHY_R0_PCIE_USB3_SWITCH,
|
||||
PHY_R0_PCIE_USB3_SWITCH);
|
||||
|
||||
/*
|
||||
* WORKAROUND: There is SSPHY suspend bug due to
|
||||
* which USB enumerates
|
||||
* in HS mode instead of SS mode. Workaround it by asserting
|
||||
* LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus
|
||||
* mode
|
||||
*/
|
||||
ret = regmap_update_bits(priv->regmap_cr, 0x102d, BIT(7), BIT(7));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(priv->regmap_cr, 0x1010, 0xff0, 20);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Fix RX Equalization setting as follows
|
||||
* LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
|
||||
* LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
|
||||
* LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
|
||||
* LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
|
||||
*/
|
||||
ret = regmap_read(priv->regmap_cr, 0x1006, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data &= ~BIT(6);
|
||||
data |= BIT(7);
|
||||
data &= ~(0x7 << 8);
|
||||
data |= (0x3 << 8);
|
||||
data |= (1 << 11);
|
||||
ret = regmap_write(priv->regmap_cr, 0x1006, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Set EQ and TX launch amplitudes as follows
|
||||
* LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
|
||||
* LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
|
||||
* LANE0.TX_OVRD_DRV_LO.EN set to 1.
|
||||
*/
|
||||
ret = regmap_read(priv->regmap_cr, 0x1002, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data &= ~0x3f80;
|
||||
data |= (0x16 << 7);
|
||||
data &= ~0x7f;
|
||||
data |= (0x7f | BIT(14));
|
||||
ret = regmap_write(priv->regmap_cr, 0x1002, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* MPLL_LOOP_CTL.PROP_CNTRL = 8 */
|
||||
ret = regmap_update_bits(priv->regmap_cr, 0x30, 0xf << 4, 8 << 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(priv->regmap, PHY_R2,
|
||||
PHY_R2_PHY_TX_VBOOST_LVL,
|
||||
FIELD_PREP(PHY_R2_PHY_TX_VBOOST_LVL, 0x4));
|
||||
|
||||
regmap_update_bits(priv->regmap, PHY_R1,
|
||||
PHY_R1_PHY_LOS_BIAS | PHY_R1_PHY_LOS_LEVEL,
|
||||
FIELD_PREP(PHY_R1_PHY_LOS_BIAS, 4) |
|
||||
FIELD_PREP(PHY_R1_PHY_LOS_LEVEL, 9));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_g12a_usb3_pcie_init(struct phy *phy)
|
||||
{
|
||||
struct phy_g12a_usb3_pcie_priv *priv = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
ret = reset_control_reset(priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->mode == PHY_TYPE_USB3)
|
||||
return phy_g12a_usb3_init(phy);
|
||||
|
||||
/* Power UP PCIE */
|
||||
/* TODO figure out when the bootloader has set USB3 mode before */
|
||||
regmap_update_bits(priv->regmap, PHY_R0,
|
||||
PHY_R0_PCIE_POWER_STATE,
|
||||
FIELD_PREP(PHY_R0_PCIE_POWER_STATE, 0x1c));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int phy_g12a_usb3_pcie_exit(struct phy *phy)
|
||||
{
|
||||
struct phy_g12a_usb3_pcie_priv *priv = phy_get_drvdata(phy);
|
||||
|
||||
return reset_control_reset(priv->reset);
|
||||
}
|
||||
|
||||
static struct phy *phy_g12a_usb3_pcie_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct phy_g12a_usb3_pcie_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned int mode;
|
||||
|
||||
if (args->args_count < 1) {
|
||||
dev_err(dev, "invalid number of arguments\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
mode = args->args[0];
|
||||
|
||||
if (mode != PHY_TYPE_USB3 && mode != PHY_TYPE_PCIE) {
|
||||
dev_err(dev, "invalid phy mode select argument\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
priv->mode = mode;
|
||||
|
||||
return priv->phy;
|
||||
}
|
||||
|
||||
static const struct phy_ops phy_g12a_usb3_pcie_ops = {
|
||||
.init = phy_g12a_usb3_pcie_init,
|
||||
.exit = phy_g12a_usb3_pcie_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int phy_g12a_usb3_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct phy_g12a_usb3_pcie_priv *priv;
|
||||
struct resource *res;
|
||||
struct phy_provider *phy_provider;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&phy_g12a_usb3_pcie_regmap_conf);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->regmap_cr = devm_regmap_init(dev, NULL, priv,
|
||||
&phy_g12a_usb3_pcie_cr_regmap_conf);
|
||||
if (IS_ERR(priv->regmap_cr))
|
||||
return PTR_ERR(priv->regmap_cr);
|
||||
|
||||
priv->clk_ref = devm_clk_get(dev, "ref_clk");
|
||||
if (IS_ERR(priv->clk_ref))
|
||||
return PTR_ERR(priv->clk_ref);
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_ref);
|
||||
if (ret)
|
||||
goto err_disable_clk_ref;
|
||||
|
||||
priv->reset = devm_reset_control_array_get(dev, false, false);
|
||||
if (IS_ERR(priv->reset))
|
||||
return PTR_ERR(priv->reset);
|
||||
|
||||
priv->phy = devm_phy_create(dev, np, &phy_g12a_usb3_pcie_ops);
|
||||
if (IS_ERR(priv->phy)) {
|
||||
ret = PTR_ERR(priv->phy);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
phy_set_drvdata(priv->phy, priv);
|
||||
dev_set_drvdata(dev, priv);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev,
|
||||
phy_g12a_usb3_pcie_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
|
||||
err_disable_clk_ref:
|
||||
clk_disable_unprepare(priv->clk_ref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id phy_g12a_usb3_pcie_of_match[] = {
|
||||
{ .compatible = "amlogic,g12a-usb3-pcie-phy", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, phy_g12a_usb3_pcie_of_match);
|
||||
|
||||
static struct platform_driver phy_g12a_usb3_pcie_driver = {
|
||||
.probe = phy_g12a_usb3_pcie_probe,
|
||||
.driver = {
|
||||
.name = "phy-g12a-usb3-pcie",
|
||||
.of_match_table = phy_g12a_usb3_pcie_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(phy_g12a_usb3_pcie_driver);
|
||||
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_DESCRIPTION("Amlogic G12A USB3 + PCIE Combo PHY driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -261,14 +261,9 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->clk = devm_clk_get(dev, "phy");
|
||||
if (IS_ERR(priv->clk)) {
|
||||
ret = PTR_ERR(priv->clk);
|
||||
if (ret == -ENOENT)
|
||||
priv->clk = NULL;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
priv->clk = devm_clk_get_optional(dev, "phy");
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
priv->reset = devm_reset_control_get_optional_shared(dev, "phy");
|
||||
if (IS_ERR(priv->reset))
|
||||
|
@ -10,6 +10,17 @@ config PHY_CYGNUS_PCIE
|
||||
Enable this to support the Broadcom Cygnus PCIe PHY.
|
||||
If unsure, say N.
|
||||
|
||||
config PHY_BCM_SR_USB
|
||||
tristate "Broadcom Stingray USB PHY driver"
|
||||
depends on OF && (ARCH_BCM_IPROC || COMPILE_TEST)
|
||||
select GENERIC_PHY
|
||||
default ARCH_BCM_IPROC
|
||||
help
|
||||
Enable this to support the Broadcom Stingray USB PHY
|
||||
driver. It supports all versions of Superspeed and
|
||||
Highspeed PHYs.
|
||||
If unsure, say N.
|
||||
|
||||
config BCM_KONA_USB2_PHY
|
||||
tristate "Broadcom Kona USB2 PHY Driver"
|
||||
depends on HAS_IOMEM
|
||||
|
@ -11,3 +11,4 @@ obj-$(CONFIG_PHY_BRCM_USB) += phy-brcm-usb-dvr.o
|
||||
phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o
|
||||
|
||||
obj-$(CONFIG_PHY_BCM_SR_PCIE) += phy-bcm-sr-pcie.o
|
||||
obj-$(CONFIG_PHY_BCM_SR_USB) += phy-bcm-sr-usb.o
|
||||
|
394
drivers/phy/broadcom/phy-bcm-sr-usb.c
Normal file
394
drivers/phy/broadcom/phy-bcm-sr-usb.c
Normal file
@ -0,0 +1,394 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2016-2018 Broadcom
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
enum bcm_usb_phy_version {
|
||||
BCM_SR_USB_COMBO_PHY,
|
||||
BCM_SR_USB_HS_PHY,
|
||||
};
|
||||
|
||||
enum bcm_usb_phy_reg {
|
||||
PLL_NDIV_FRAC,
|
||||
PLL_NDIV_INT,
|
||||
PLL_CTRL,
|
||||
PHY_CTRL,
|
||||
PHY_PLL_CTRL,
|
||||
};
|
||||
|
||||
/* USB PHY registers */
|
||||
|
||||
static const u8 bcm_usb_combo_phy_ss[] = {
|
||||
[PLL_CTRL] = 0x18,
|
||||
[PHY_CTRL] = 0x14,
|
||||
};
|
||||
|
||||
static const u8 bcm_usb_combo_phy_hs[] = {
|
||||
[PLL_NDIV_FRAC] = 0x04,
|
||||
[PLL_NDIV_INT] = 0x08,
|
||||
[PLL_CTRL] = 0x0c,
|
||||
[PHY_CTRL] = 0x10,
|
||||
};
|
||||
|
||||
#define HSPLL_NDIV_INT_VAL 0x13
|
||||
#define HSPLL_NDIV_FRAC_VAL 0x1005
|
||||
|
||||
static const u8 bcm_usb_hs_phy[] = {
|
||||
[PLL_NDIV_FRAC] = 0x0,
|
||||
[PLL_NDIV_INT] = 0x4,
|
||||
[PLL_CTRL] = 0x8,
|
||||
[PHY_CTRL] = 0xc,
|
||||
};
|
||||
|
||||
enum pll_ctrl_bits {
|
||||
PLL_RESETB,
|
||||
SSPLL_SUSPEND_EN,
|
||||
PLL_SEQ_START,
|
||||
PLL_LOCK,
|
||||
PLL_PDIV,
|
||||
};
|
||||
|
||||
static const u8 u3pll_ctrl[] = {
|
||||
[PLL_RESETB] = 0,
|
||||
[SSPLL_SUSPEND_EN] = 1,
|
||||
[PLL_SEQ_START] = 2,
|
||||
[PLL_LOCK] = 3,
|
||||
};
|
||||
|
||||
#define HSPLL_PDIV_MASK 0xF
|
||||
#define HSPLL_PDIV_VAL 0x1
|
||||
|
||||
static const u8 u2pll_ctrl[] = {
|
||||
[PLL_PDIV] = 1,
|
||||
[PLL_RESETB] = 5,
|
||||
[PLL_LOCK] = 6,
|
||||
};
|
||||
|
||||
enum bcm_usb_phy_ctrl_bits {
|
||||
CORERDY,
|
||||
AFE_LDO_PWRDWNB,
|
||||
AFE_PLL_PWRDWNB,
|
||||
AFE_BG_PWRDWNB,
|
||||
PHY_ISO,
|
||||
PHY_RESETB,
|
||||
PHY_PCTL,
|
||||
};
|
||||
|
||||
#define PHY_PCTL_MASK 0xffff
|
||||
/*
|
||||
* 0x0806 of PCTL_VAL has below bits set
|
||||
* BIT-8 : refclk divider 1
|
||||
* BIT-3:2: device mode; mode is not effect
|
||||
* BIT-1: soft reset active low
|
||||
*/
|
||||
#define HSPHY_PCTL_VAL 0x0806
|
||||
#define SSPHY_PCTL_VAL 0x0006
|
||||
|
||||
static const u8 u3phy_ctrl[] = {
|
||||
[PHY_RESETB] = 1,
|
||||
[PHY_PCTL] = 2,
|
||||
};
|
||||
|
||||
static const u8 u2phy_ctrl[] = {
|
||||
[CORERDY] = 0,
|
||||
[AFE_LDO_PWRDWNB] = 1,
|
||||
[AFE_PLL_PWRDWNB] = 2,
|
||||
[AFE_BG_PWRDWNB] = 3,
|
||||
[PHY_ISO] = 4,
|
||||
[PHY_RESETB] = 5,
|
||||
[PHY_PCTL] = 6,
|
||||
};
|
||||
|
||||
struct bcm_usb_phy_cfg {
|
||||
uint32_t type;
|
||||
uint32_t version;
|
||||
void __iomem *regs;
|
||||
struct phy *phy;
|
||||
const u8 *offset;
|
||||
};
|
||||
|
||||
#define PLL_LOCK_RETRY_COUNT 1000
|
||||
|
||||
enum bcm_usb_phy_type {
|
||||
USB_HS_PHY,
|
||||
USB_SS_PHY,
|
||||
};
|
||||
|
||||
#define NUM_BCM_SR_USB_COMBO_PHYS 2
|
||||
|
||||
static inline void bcm_usb_reg32_clrbits(void __iomem *addr, uint32_t clear)
|
||||
{
|
||||
writel(readl(addr) & ~clear, addr);
|
||||
}
|
||||
|
||||
static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)
|
||||
{
|
||||
writel(readl(addr) | set, addr);
|
||||
}
|
||||
|
||||
static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit)
|
||||
{
|
||||
int retry;
|
||||
u32 rd_data;
|
||||
|
||||
retry = PLL_LOCK_RETRY_COUNT;
|
||||
do {
|
||||
rd_data = readl(addr);
|
||||
if (rd_data & bit)
|
||||
return 0;
|
||||
udelay(1);
|
||||
} while (--retry > 0);
|
||||
|
||||
pr_err("%s: FAIL\n", __func__);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
void __iomem *regs = phy_cfg->regs;
|
||||
const u8 *offset;
|
||||
u32 rd_data;
|
||||
|
||||
offset = phy_cfg->offset;
|
||||
|
||||
/* Set pctl with mode and soft reset */
|
||||
rd_data = readl(regs + offset[PHY_CTRL]);
|
||||
rd_data &= ~(PHY_PCTL_MASK << u3phy_ctrl[PHY_PCTL]);
|
||||
rd_data |= (SSPHY_PCTL_VAL << u3phy_ctrl[PHY_PCTL]);
|
||||
writel(rd_data, regs + offset[PHY_CTRL]);
|
||||
|
||||
bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],
|
||||
BIT(u3pll_ctrl[SSPLL_SUSPEND_EN]));
|
||||
bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
|
||||
BIT(u3pll_ctrl[PLL_SEQ_START]));
|
||||
bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
|
||||
BIT(u3pll_ctrl[PLL_RESETB]));
|
||||
|
||||
/* Maximum timeout for PLL reset done */
|
||||
msleep(30);
|
||||
|
||||
ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],
|
||||
BIT(u3pll_ctrl[PLL_LOCK]));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
void __iomem *regs = phy_cfg->regs;
|
||||
const u8 *offset;
|
||||
u32 rd_data;
|
||||
|
||||
offset = phy_cfg->offset;
|
||||
|
||||
writel(HSPLL_NDIV_INT_VAL, regs + offset[PLL_NDIV_INT]);
|
||||
writel(HSPLL_NDIV_FRAC_VAL, regs + offset[PLL_NDIV_FRAC]);
|
||||
|
||||
rd_data = readl(regs + offset[PLL_CTRL]);
|
||||
rd_data &= ~(HSPLL_PDIV_MASK << u2pll_ctrl[PLL_PDIV]);
|
||||
rd_data |= (HSPLL_PDIV_VAL << u2pll_ctrl[PLL_PDIV]);
|
||||
writel(rd_data, regs + offset[PLL_CTRL]);
|
||||
|
||||
/* Set Core Ready high */
|
||||
bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
|
||||
BIT(u2phy_ctrl[CORERDY]));
|
||||
|
||||
/* Maximum timeout for Core Ready done */
|
||||
msleep(30);
|
||||
|
||||
bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
|
||||
BIT(u2pll_ctrl[PLL_RESETB]));
|
||||
bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
|
||||
BIT(u2phy_ctrl[PHY_RESETB]));
|
||||
|
||||
|
||||
rd_data = readl(regs + offset[PHY_CTRL]);
|
||||
rd_data &= ~(PHY_PCTL_MASK << u2phy_ctrl[PHY_PCTL]);
|
||||
rd_data |= (HSPHY_PCTL_VAL << u2phy_ctrl[PHY_PCTL]);
|
||||
writel(rd_data, regs + offset[PHY_CTRL]);
|
||||
|
||||
/* Maximum timeout for PLL reset done */
|
||||
msleep(30);
|
||||
|
||||
ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],
|
||||
BIT(u2pll_ctrl[PLL_LOCK]));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bcm_usb_phy_reset(struct phy *phy)
|
||||
{
|
||||
struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);
|
||||
void __iomem *regs = phy_cfg->regs;
|
||||
const u8 *offset;
|
||||
|
||||
offset = phy_cfg->offset;
|
||||
|
||||
if (phy_cfg->type == USB_HS_PHY) {
|
||||
bcm_usb_reg32_clrbits(regs + offset[PHY_CTRL],
|
||||
BIT(u2phy_ctrl[CORERDY]));
|
||||
bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
|
||||
BIT(u2phy_ctrl[CORERDY]));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_usb_phy_init(struct phy *phy)
|
||||
{
|
||||
struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (phy_cfg->type == USB_SS_PHY)
|
||||
ret = bcm_usb_ss_phy_init(phy_cfg);
|
||||
else if (phy_cfg->type == USB_HS_PHY)
|
||||
ret = bcm_usb_hs_phy_init(phy_cfg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct phy_ops sr_phy_ops = {
|
||||
.init = bcm_usb_phy_init,
|
||||
.reset = bcm_usb_phy_reset,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *bcm_usb_phy_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct bcm_usb_phy_cfg *phy_cfg;
|
||||
int phy_idx;
|
||||
|
||||
phy_cfg = dev_get_drvdata(dev);
|
||||
if (!phy_cfg)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (phy_cfg->version == BCM_SR_USB_COMBO_PHY) {
|
||||
phy_idx = args->args[0];
|
||||
|
||||
if (WARN_ON(phy_idx > 1))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return phy_cfg[phy_idx].phy;
|
||||
} else
|
||||
return phy_cfg->phy;
|
||||
}
|
||||
|
||||
static int bcm_usb_phy_create(struct device *dev, struct device_node *node,
|
||||
void __iomem *regs, uint32_t version)
|
||||
{
|
||||
struct bcm_usb_phy_cfg *phy_cfg;
|
||||
int idx;
|
||||
|
||||
if (version == BCM_SR_USB_COMBO_PHY) {
|
||||
phy_cfg = devm_kzalloc(dev, NUM_BCM_SR_USB_COMBO_PHYS *
|
||||
sizeof(struct bcm_usb_phy_cfg),
|
||||
GFP_KERNEL);
|
||||
if (!phy_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
for (idx = 0; idx < NUM_BCM_SR_USB_COMBO_PHYS; idx++) {
|
||||
phy_cfg[idx].regs = regs;
|
||||
phy_cfg[idx].version = version;
|
||||
if (idx == 0) {
|
||||
phy_cfg[idx].offset = bcm_usb_combo_phy_hs;
|
||||
phy_cfg[idx].type = USB_HS_PHY;
|
||||
} else {
|
||||
phy_cfg[idx].offset = bcm_usb_combo_phy_ss;
|
||||
phy_cfg[idx].type = USB_SS_PHY;
|
||||
}
|
||||
phy_cfg[idx].phy = devm_phy_create(dev, node,
|
||||
&sr_phy_ops);
|
||||
if (IS_ERR(phy_cfg[idx].phy))
|
||||
return PTR_ERR(phy_cfg[idx].phy);
|
||||
|
||||
phy_set_drvdata(phy_cfg[idx].phy, &phy_cfg[idx]);
|
||||
}
|
||||
} else if (version == BCM_SR_USB_HS_PHY) {
|
||||
phy_cfg = devm_kzalloc(dev, sizeof(struct bcm_usb_phy_cfg),
|
||||
GFP_KERNEL);
|
||||
if (!phy_cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
phy_cfg->regs = regs;
|
||||
phy_cfg->version = version;
|
||||
phy_cfg->offset = bcm_usb_hs_phy;
|
||||
phy_cfg->type = USB_HS_PHY;
|
||||
phy_cfg->phy = devm_phy_create(dev, node, &sr_phy_ops);
|
||||
if (IS_ERR(phy_cfg->phy))
|
||||
return PTR_ERR(phy_cfg->phy);
|
||||
|
||||
phy_set_drvdata(phy_cfg->phy, phy_cfg);
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
dev_set_drvdata(dev, phy_cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm_usb_phy_of_match[] = {
|
||||
{
|
||||
.compatible = "brcm,sr-usb-combo-phy",
|
||||
.data = (void *)BCM_SR_USB_COMBO_PHY,
|
||||
},
|
||||
{
|
||||
.compatible = "brcm,sr-usb-hs-phy",
|
||||
.data = (void *)BCM_SR_USB_HS_PHY,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm_usb_phy_of_match);
|
||||
|
||||
static int bcm_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dn = dev->of_node;
|
||||
const struct of_device_id *of_id;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
enum bcm_usb_phy_version version;
|
||||
struct phy_provider *phy_provider;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
of_id = of_match_node(bcm_usb_phy_of_match, dn);
|
||||
if (of_id)
|
||||
version = (enum bcm_usb_phy_version)of_id->data;
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
ret = bcm_usb_phy_create(dev, dn, regs, version);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, bcm_usb_phy_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static struct platform_driver bcm_usb_phy_driver = {
|
||||
.driver = {
|
||||
.name = "phy-bcm-sr-usb",
|
||||
.of_match_table = bcm_usb_phy_of_match,
|
||||
},
|
||||
.probe = bcm_usb_phy_probe,
|
||||
};
|
||||
module_platform_driver(bcm_usb_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Broadcom");
|
||||
MODULE_DESCRIPTION("Broadcom stingray USB Phy driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -6,6 +6,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define PHY_CTRL0 0x0
|
||||
#define PHY_CTRL0_REF_SSP_EN BIT(2)
|
||||
@ -24,6 +25,7 @@ struct imx8mq_usb_phy {
|
||||
struct phy *phy;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
struct regulator *vbus;
|
||||
};
|
||||
|
||||
static int imx8mq_usb_phy_init(struct phy *phy)
|
||||
@ -55,6 +57,11 @@ static int imx8mq_usb_phy_init(struct phy *phy)
|
||||
static int imx8mq_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(imx_phy->vbus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clk_prepare_enable(imx_phy->clk);
|
||||
}
|
||||
@ -64,6 +71,7 @@ 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);
|
||||
regulator_disable(imx_phy->vbus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -101,6 +109,10 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(imx_phy->phy))
|
||||
return PTR_ERR(imx_phy->phy);
|
||||
|
||||
imx_phy->vbus = devm_regulator_get(dev, "vbus");
|
||||
if (IS_ERR(imx_phy->vbus))
|
||||
return PTR_ERR(imx_phy->vbus);
|
||||
|
||||
phy_set_drvdata(imx_phy->phy, imx_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
@ -12,6 +12,16 @@ config PHY_HI6220_USB
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config PHY_HI3660_USB
|
||||
tristate "hi3660 USB PHY support"
|
||||
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
|
||||
select GENERIC_PHY
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Enable this to support the HISILICON HI3660 USB PHY.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config PHY_HISTB_COMBPHY
|
||||
tristate "HiSilicon STB SoCs COMBPHY support"
|
||||
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
|
||||
|
@ -1,4 +1,5 @@
|
||||
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
|
||||
obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o
|
||||
obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o
|
||||
obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
|
||||
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
|
||||
|
233
drivers/phy/hisilicon/phy-hi3660-usb3.c
Normal file
233
drivers/phy/hisilicon/phy-hi3660-usb3.c
Normal file
@ -0,0 +1,233 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Phy provider for USB 3.0 controller on HiSilicon 3660 platform
|
||||
*
|
||||
* Copyright (C) 2017-2018 Hilisicon Electronics Co., Ltd.
|
||||
* http://www.huawei.com
|
||||
*
|
||||
* Authors: Yu Chen <chenyu56@huawei.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define PERI_CRG_CLK_EN4 0x40
|
||||
#define PERI_CRG_CLK_DIS4 0x44
|
||||
#define GT_CLK_USB3OTG_REF BIT(0)
|
||||
#define GT_ACLK_USB3OTG BIT(1)
|
||||
|
||||
#define PERI_CRG_RSTEN4 0x90
|
||||
#define PERI_CRG_RSTDIS4 0x94
|
||||
#define IP_RST_USB3OTGPHY_POR BIT(3)
|
||||
#define IP_RST_USB3OTG BIT(5)
|
||||
|
||||
#define PERI_CRG_ISODIS 0x148
|
||||
#define USB_REFCLK_ISO_EN BIT(25)
|
||||
|
||||
#define PCTRL_PERI_CTRL3 0x10
|
||||
#define PCTRL_PERI_CTRL3_MSK_START 16
|
||||
#define USB_TCXO_EN BIT(1)
|
||||
|
||||
#define PCTRL_PERI_CTRL24 0x64
|
||||
#define SC_CLK_USB3PHY_3MUX1_SEL BIT(25)
|
||||
|
||||
#define USBOTG3_CTRL0 0x00
|
||||
#define SC_USB3PHY_ABB_GT_EN BIT(15)
|
||||
|
||||
#define USBOTG3_CTRL2 0x08
|
||||
#define USBOTG3CTRL2_POWERDOWN_HSP BIT(0)
|
||||
#define USBOTG3CTRL2_POWERDOWN_SSP BIT(1)
|
||||
|
||||
#define USBOTG3_CTRL3 0x0C
|
||||
#define USBOTG3_CTRL3_VBUSVLDEXT BIT(6)
|
||||
#define USBOTG3_CTRL3_VBUSVLDEXTSEL BIT(5)
|
||||
|
||||
#define USBOTG3_CTRL4 0x10
|
||||
|
||||
#define USBOTG3_CTRL7 0x1c
|
||||
#define REF_SSP_EN BIT(16)
|
||||
|
||||
/* This value config the default txtune parameter of the usb 2.0 phy */
|
||||
#define HI3660_USB_DEFAULT_PHY_PARAM 0x1c466e3
|
||||
|
||||
struct hi3660_priv {
|
||||
struct device *dev;
|
||||
struct regmap *peri_crg;
|
||||
struct regmap *pctrl;
|
||||
struct regmap *otg_bc;
|
||||
u32 eye_diagram_param;
|
||||
};
|
||||
|
||||
static int hi3660_phy_init(struct phy *phy)
|
||||
{
|
||||
struct hi3660_priv *priv = phy_get_drvdata(phy);
|
||||
u32 val, mask;
|
||||
int ret;
|
||||
|
||||
/* usb refclk iso disable */
|
||||
ret = regmap_write(priv->peri_crg, PERI_CRG_ISODIS, USB_REFCLK_ISO_EN);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* enable usb_tcxo_en */
|
||||
val = USB_TCXO_EN | (USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START);
|
||||
ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* assert phy */
|
||||
val = IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG;
|
||||
ret = regmap_write(priv->peri_crg, PERI_CRG_RSTEN4, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* enable phy ref clk */
|
||||
val = SC_USB3PHY_ABB_GT_EN;
|
||||
mask = val;
|
||||
ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL0, mask, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
val = REF_SSP_EN;
|
||||
mask = val;
|
||||
ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL7, mask, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* exit from IDDQ mode */
|
||||
mask = USBOTG3CTRL2_POWERDOWN_HSP | USBOTG3CTRL2_POWERDOWN_SSP;
|
||||
ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL2, mask, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* delay for exit from IDDQ mode */
|
||||
usleep_range(100, 120);
|
||||
|
||||
/* deassert phy */
|
||||
val = IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG;
|
||||
ret = regmap_write(priv->peri_crg, PERI_CRG_RSTDIS4, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* delay for phy deasserted */
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
/* fake vbus valid signal */
|
||||
val = USBOTG3_CTRL3_VBUSVLDEXT | USBOTG3_CTRL3_VBUSVLDEXTSEL;
|
||||
mask = val;
|
||||
ret = regmap_update_bits(priv->otg_bc, USBOTG3_CTRL3, mask, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* delay for vbus valid */
|
||||
usleep_range(100, 120);
|
||||
|
||||
ret = regmap_write(priv->otg_bc, USBOTG3_CTRL4,
|
||||
priv->eye_diagram_param);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
dev_err(priv->dev, "failed to init phy ret: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hi3660_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct hi3660_priv *priv = phy_get_drvdata(phy);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* assert phy */
|
||||
val = IP_RST_USB3OTGPHY_POR;
|
||||
ret = regmap_write(priv->peri_crg, PERI_CRG_RSTEN4, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* disable usb_tcxo_en */
|
||||
val = USB_TCXO_EN << PCTRL_PERI_CTRL3_MSK_START;
|
||||
ret = regmap_write(priv->pctrl, PCTRL_PERI_CTRL3, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
dev_err(priv->dev, "failed to exit phy ret: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct phy_ops hi3660_phy_ops = {
|
||||
.init = hi3660_phy_init,
|
||||
.exit = hi3660_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int hi3660_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *phy;
|
||||
struct hi3660_priv *priv;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->peri_crg = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"hisilicon,pericrg-syscon");
|
||||
if (IS_ERR(priv->peri_crg)) {
|
||||
dev_err(dev, "no hisilicon,pericrg-syscon\n");
|
||||
return PTR_ERR(priv->peri_crg);
|
||||
}
|
||||
|
||||
priv->pctrl = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"hisilicon,pctrl-syscon");
|
||||
if (IS_ERR(priv->pctrl)) {
|
||||
dev_err(dev, "no hisilicon,pctrl-syscon\n");
|
||||
return PTR_ERR(priv->pctrl);
|
||||
}
|
||||
|
||||
/* node of hi3660 phy is a sub-node of usb3_otg_bc */
|
||||
priv->otg_bc = syscon_node_to_regmap(dev->parent->of_node);
|
||||
if (IS_ERR(priv->otg_bc)) {
|
||||
dev_err(dev, "no hisilicon,usb3-otg-bc-syscon\n");
|
||||
return PTR_ERR(priv->otg_bc);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "hisilicon,eye-diagram-param",
|
||||
&(priv->eye_diagram_param)))
|
||||
priv->eye_diagram_param = HI3660_USB_DEFAULT_PHY_PARAM;
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &hi3660_phy_ops);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
phy_set_drvdata(phy, priv);
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id hi3660_phy_of_match[] = {
|
||||
{.compatible = "hisilicon,hi3660-usb-phy",},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hi3660_phy_of_match);
|
||||
|
||||
static struct platform_driver hi3660_phy_driver = {
|
||||
.probe = hi3660_phy_probe,
|
||||
.driver = {
|
||||
.name = "hi3660-usb-phy",
|
||||
.of_match_table = hi3660_phy_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(hi3660_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Yu Chen <chenyu56@huawei.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Hilisicon Hi3660 USB3 PHY Driver");
|
@ -13,6 +13,16 @@ config PHY_MTK_TPHY
|
||||
multi-ports is first version, otherwise is second veriosn,
|
||||
so you can easily distinguish them by banks layout.
|
||||
|
||||
config PHY_MTK_UFS
|
||||
tristate "MediaTek UFS M-PHY driver"
|
||||
depends on ARCH_MEDIATEK && OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for UFS M-PHY on MediaTek chipsets.
|
||||
Enable this to provide vendor-specific probing,
|
||||
initialization, power on and power off flow of
|
||||
specified M-PHYs.
|
||||
|
||||
config PHY_MTK_XSPHY
|
||||
tristate "MediaTek XS-PHY Driver"
|
||||
depends on ARCH_MEDIATEK && OF
|
||||
|
@ -4,4 +4,5 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
|
||||
obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
|
||||
obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o
|
||||
|
@ -1103,13 +1103,9 @@ static int mtk_tphy_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* it's deprecated, make it optional for backward compatibility */
|
||||
tphy->u3phya_ref = devm_clk_get(dev, "u3phya_ref");
|
||||
if (IS_ERR(tphy->u3phya_ref)) {
|
||||
if (PTR_ERR(tphy->u3phya_ref) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
tphy->u3phya_ref = NULL;
|
||||
}
|
||||
tphy->u3phya_ref = devm_clk_get_optional(dev, "u3phya_ref");
|
||||
if (IS_ERR(tphy->u3phya_ref))
|
||||
return PTR_ERR(tphy->u3phya_ref);
|
||||
|
||||
tphy->src_ref_clk = U3P_REF_CLK;
|
||||
tphy->src_coef = U3P_SLEW_RATE_COEF;
|
||||
|
245
drivers/phy/mediatek/phy-mtk-ufs.c
Normal file
245
drivers/phy/mediatek/phy-mtk-ufs.c
Normal file
@ -0,0 +1,245 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019 MediaTek Inc.
|
||||
* Author: Stanley Chu <stanley.chu@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* mphy register and offsets */
|
||||
#define MP_GLB_DIG_8C 0x008C
|
||||
#define FRC_PLL_ISO_EN BIT(8)
|
||||
#define PLL_ISO_EN BIT(9)
|
||||
#define FRC_FRC_PWR_ON BIT(10)
|
||||
#define PLL_PWR_ON BIT(11)
|
||||
|
||||
#define MP_LN_DIG_RX_9C 0xA09C
|
||||
#define FSM_DIFZ_FRC BIT(18)
|
||||
|
||||
#define MP_LN_DIG_RX_AC 0xA0AC
|
||||
#define FRC_RX_SQ_EN BIT(0)
|
||||
#define RX_SQ_EN BIT(1)
|
||||
|
||||
#define MP_LN_RX_44 0xB044
|
||||
#define FRC_CDR_PWR_ON BIT(17)
|
||||
#define CDR_PWR_ON BIT(18)
|
||||
#define FRC_CDR_ISO_EN BIT(19)
|
||||
#define CDR_ISO_EN BIT(20)
|
||||
|
||||
struct ufs_mtk_phy {
|
||||
struct device *dev;
|
||||
void __iomem *mmio;
|
||||
struct clk *mp_clk;
|
||||
struct clk *unipro_clk;
|
||||
};
|
||||
|
||||
static inline u32 mphy_readl(struct ufs_mtk_phy *phy, u32 reg)
|
||||
{
|
||||
return readl(phy->mmio + reg);
|
||||
}
|
||||
|
||||
static inline void mphy_writel(struct ufs_mtk_phy *phy, u32 val, u32 reg)
|
||||
{
|
||||
writel(val, phy->mmio + reg);
|
||||
}
|
||||
|
||||
static void mphy_set_bit(struct ufs_mtk_phy *phy, u32 reg, u32 bit)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = mphy_readl(phy, reg);
|
||||
val |= bit;
|
||||
mphy_writel(phy, val, reg);
|
||||
}
|
||||
|
||||
static void mphy_clr_bit(struct ufs_mtk_phy *phy, u32 reg, u32 bit)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = mphy_readl(phy, reg);
|
||||
val &= ~bit;
|
||||
mphy_writel(phy, val, reg);
|
||||
}
|
||||
|
||||
static struct ufs_mtk_phy *get_ufs_mtk_phy(struct phy *generic_phy)
|
||||
{
|
||||
return (struct ufs_mtk_phy *)phy_get_drvdata(generic_phy);
|
||||
}
|
||||
|
||||
static int ufs_mtk_phy_clk_init(struct ufs_mtk_phy *phy)
|
||||
{
|
||||
struct device *dev = phy->dev;
|
||||
|
||||
phy->unipro_clk = devm_clk_get(dev, "unipro");
|
||||
if (IS_ERR(phy->unipro_clk)) {
|
||||
dev_err(dev, "failed to get clock: unipro");
|
||||
return PTR_ERR(phy->unipro_clk);
|
||||
}
|
||||
|
||||
phy->mp_clk = devm_clk_get(dev, "mp");
|
||||
if (IS_ERR(phy->mp_clk)) {
|
||||
dev_err(dev, "failed to get clock: mp");
|
||||
return PTR_ERR(phy->mp_clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ufs_mtk_phy_set_active(struct ufs_mtk_phy *phy)
|
||||
{
|
||||
/* release DA_MP_PLL_PWR_ON */
|
||||
mphy_set_bit(phy, MP_GLB_DIG_8C, PLL_PWR_ON);
|
||||
mphy_clr_bit(phy, MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
|
||||
|
||||
/* release DA_MP_PLL_ISO_EN */
|
||||
mphy_clr_bit(phy, MP_GLB_DIG_8C, PLL_ISO_EN);
|
||||
mphy_clr_bit(phy, MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
|
||||
|
||||
/* release DA_MP_CDR_PWR_ON */
|
||||
mphy_set_bit(phy, MP_LN_RX_44, CDR_PWR_ON);
|
||||
mphy_clr_bit(phy, MP_LN_RX_44, FRC_CDR_PWR_ON);
|
||||
|
||||
/* release DA_MP_CDR_ISO_EN */
|
||||
mphy_clr_bit(phy, MP_LN_RX_44, CDR_ISO_EN);
|
||||
mphy_clr_bit(phy, MP_LN_RX_44, FRC_CDR_ISO_EN);
|
||||
|
||||
/* release DA_MP_RX0_SQ_EN */
|
||||
mphy_set_bit(phy, MP_LN_DIG_RX_AC, RX_SQ_EN);
|
||||
mphy_clr_bit(phy, MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
|
||||
|
||||
/* delay 1us to wait DIFZ stable */
|
||||
udelay(1);
|
||||
|
||||
/* release DIFZ */
|
||||
mphy_clr_bit(phy, MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
|
||||
}
|
||||
|
||||
static void ufs_mtk_phy_set_deep_hibern(struct ufs_mtk_phy *phy)
|
||||
{
|
||||
/* force DIFZ */
|
||||
mphy_set_bit(phy, MP_LN_DIG_RX_9C, FSM_DIFZ_FRC);
|
||||
|
||||
/* force DA_MP_RX0_SQ_EN */
|
||||
mphy_set_bit(phy, MP_LN_DIG_RX_AC, FRC_RX_SQ_EN);
|
||||
mphy_clr_bit(phy, MP_LN_DIG_RX_AC, RX_SQ_EN);
|
||||
|
||||
/* force DA_MP_CDR_ISO_EN */
|
||||
mphy_set_bit(phy, MP_LN_RX_44, FRC_CDR_ISO_EN);
|
||||
mphy_set_bit(phy, MP_LN_RX_44, CDR_ISO_EN);
|
||||
|
||||
/* force DA_MP_CDR_PWR_ON */
|
||||
mphy_set_bit(phy, MP_LN_RX_44, FRC_CDR_PWR_ON);
|
||||
mphy_clr_bit(phy, MP_LN_RX_44, CDR_PWR_ON);
|
||||
|
||||
/* force DA_MP_PLL_ISO_EN */
|
||||
mphy_set_bit(phy, MP_GLB_DIG_8C, FRC_PLL_ISO_EN);
|
||||
mphy_set_bit(phy, MP_GLB_DIG_8C, PLL_ISO_EN);
|
||||
|
||||
/* force DA_MP_PLL_PWR_ON */
|
||||
mphy_set_bit(phy, MP_GLB_DIG_8C, FRC_FRC_PWR_ON);
|
||||
mphy_clr_bit(phy, MP_GLB_DIG_8C, PLL_PWR_ON);
|
||||
}
|
||||
|
||||
static int ufs_mtk_phy_power_on(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_mtk_phy *phy = get_ufs_mtk_phy(generic_phy);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(phy->unipro_clk);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "unipro_clk enable failed %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(phy->mp_clk);
|
||||
if (ret) {
|
||||
dev_err(phy->dev, "mp_clk enable failed %d\n", ret);
|
||||
goto out_unprepare_unipro_clk;
|
||||
}
|
||||
|
||||
ufs_mtk_phy_set_active(phy);
|
||||
|
||||
return 0;
|
||||
|
||||
out_unprepare_unipro_clk:
|
||||
clk_disable_unprepare(phy->unipro_clk);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_mtk_phy_power_off(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_mtk_phy *phy = get_ufs_mtk_phy(generic_phy);
|
||||
|
||||
ufs_mtk_phy_set_deep_hibern(phy);
|
||||
|
||||
clk_disable_unprepare(phy->unipro_clk);
|
||||
clk_disable_unprepare(phy->mp_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops ufs_mtk_phy_ops = {
|
||||
.power_on = ufs_mtk_phy_power_on,
|
||||
.power_off = ufs_mtk_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ufs_mtk_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct resource *res;
|
||||
struct ufs_mtk_phy *phy;
|
||||
int ret;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
phy->mmio = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(phy->mmio))
|
||||
return PTR_ERR(phy->mmio);
|
||||
|
||||
phy->dev = dev;
|
||||
|
||||
ret = ufs_mtk_phy_clk_init(phy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
generic_phy = devm_phy_create(dev, NULL, &ufs_mtk_phy_ops);
|
||||
if (IS_ERR(generic_phy))
|
||||
return PTR_ERR(generic_phy);
|
||||
|
||||
phy_set_drvdata(generic_phy, phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id ufs_mtk_phy_of_match[] = {
|
||||
{.compatible = "mediatek,mt8183-ufsphy"},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ufs_mtk_phy_of_match);
|
||||
|
||||
static struct platform_driver ufs_mtk_phy_driver = {
|
||||
.probe = ufs_mtk_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = ufs_mtk_phy_of_match,
|
||||
.name = "ufs_mtk_phy",
|
||||
},
|
||||
};
|
||||
module_platform_driver(ufs_mtk_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Universal Flash Storage (UFS) MediaTek MPHY");
|
||||
MODULE_AUTHOR("Stanley Chu <stanley.chu@mediatek.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -31,6 +31,238 @@ struct serdes_macro {
|
||||
struct serdes_ctrl *ctrl;
|
||||
};
|
||||
|
||||
#define MCB_S6G_CFG_TIMEOUT 50
|
||||
|
||||
static int __serdes_write_mcb_s6g(struct regmap *regmap, u8 macro, u32 op)
|
||||
{
|
||||
unsigned int regval = 0;
|
||||
|
||||
regmap_write(regmap, HSIO_MCB_S6G_ADDR_CFG, op |
|
||||
HSIO_MCB_S6G_ADDR_CFG_SERDES6G_ADDR(BIT(macro)));
|
||||
|
||||
return regmap_read_poll_timeout(regmap, HSIO_MCB_S6G_ADDR_CFG, regval,
|
||||
(regval & op) != op, 100,
|
||||
MCB_S6G_CFG_TIMEOUT * 1000);
|
||||
}
|
||||
|
||||
static int serdes_commit_mcb_s6g(struct regmap *regmap, u8 macro)
|
||||
{
|
||||
return __serdes_write_mcb_s6g(regmap, macro,
|
||||
HSIO_MCB_S6G_ADDR_CFG_SERDES6G_WR_ONE_SHOT);
|
||||
}
|
||||
|
||||
static int serdes_update_mcb_s6g(struct regmap *regmap, u8 macro)
|
||||
{
|
||||
return __serdes_write_mcb_s6g(regmap, macro,
|
||||
HSIO_MCB_S6G_ADDR_CFG_SERDES6G_RD_ONE_SHOT);
|
||||
}
|
||||
|
||||
static int serdes_init_s6g(struct regmap *regmap, u8 serdes, int mode)
|
||||
{
|
||||
u32 pll_fsm_ctrl_data;
|
||||
u32 ob_ena1v_mode;
|
||||
u32 des_bw_ana;
|
||||
u32 ob_ena_cas;
|
||||
u32 if_mode;
|
||||
u32 ob_lev;
|
||||
u32 qrate;
|
||||
int ret;
|
||||
|
||||
if (mode == PHY_INTERFACE_MODE_QSGMII) {
|
||||
pll_fsm_ctrl_data = 120;
|
||||
ob_ena1v_mode = 0;
|
||||
ob_ena_cas = 0;
|
||||
des_bw_ana = 5;
|
||||
ob_lev = 24;
|
||||
if_mode = 3;
|
||||
qrate = 0;
|
||||
} else {
|
||||
pll_fsm_ctrl_data = 60;
|
||||
ob_ena1v_mode = 1;
|
||||
ob_ena_cas = 2;
|
||||
des_bw_ana = 3;
|
||||
ob_lev = 48;
|
||||
if_mode = 1;
|
||||
qrate = 1;
|
||||
}
|
||||
|
||||
ret = serdes_update_mcb_s6g(regmap, serdes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Test pattern */
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,
|
||||
HSIO_S6G_COMMON_CFG_SYS_RST, 0);
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
|
||||
HSIO_S6G_PLL_CFG_PLL_FSM_ENA, 0);
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
|
||||
HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |
|
||||
HSIO_S6G_IB_CFG_IB_REG_ENA |
|
||||
HSIO_S6G_IB_CFG_IB_SAM_ENA |
|
||||
HSIO_S6G_IB_CFG_IB_EQZ_ENA |
|
||||
HSIO_S6G_IB_CFG_IB_CONCUR |
|
||||
HSIO_S6G_IB_CFG_IB_CAL_ENA,
|
||||
HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |
|
||||
HSIO_S6G_IB_CFG_IB_REG_ENA |
|
||||
HSIO_S6G_IB_CFG_IB_SAM_ENA |
|
||||
HSIO_S6G_IB_CFG_IB_EQZ_ENA |
|
||||
HSIO_S6G_IB_CFG_IB_CONCUR);
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
|
||||
HSIO_S6G_IB_CFG1_IB_FRC_OFFSET |
|
||||
HSIO_S6G_IB_CFG1_IB_FRC_LP |
|
||||
HSIO_S6G_IB_CFG1_IB_FRC_MID |
|
||||
HSIO_S6G_IB_CFG1_IB_FRC_HP |
|
||||
HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |
|
||||
HSIO_S6G_IB_CFG1_IB_FILT_LP |
|
||||
HSIO_S6G_IB_CFG1_IB_FILT_MID |
|
||||
HSIO_S6G_IB_CFG1_IB_FILT_HP,
|
||||
HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |
|
||||
HSIO_S6G_IB_CFG1_IB_FILT_HP |
|
||||
HSIO_S6G_IB_CFG1_IB_FILT_LP |
|
||||
HSIO_S6G_IB_CFG1_IB_FILT_MID);
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG2,
|
||||
HSIO_S6G_IB_CFG2_IB_UREG_M,
|
||||
HSIO_S6G_IB_CFG2_IB_UREG(4));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG3,
|
||||
HSIO_S6G_IB_CFG3_IB_INI_OFFSET_M |
|
||||
HSIO_S6G_IB_CFG3_IB_INI_LP_M |
|
||||
HSIO_S6G_IB_CFG3_IB_INI_MID_M |
|
||||
HSIO_S6G_IB_CFG3_IB_INI_HP_M,
|
||||
HSIO_S6G_IB_CFG3_IB_INI_OFFSET(31) |
|
||||
HSIO_S6G_IB_CFG3_IB_INI_LP(1) |
|
||||
HSIO_S6G_IB_CFG3_IB_INI_MID(31) |
|
||||
HSIO_S6G_IB_CFG3_IB_INI_HP(0));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
|
||||
HSIO_S6G_MISC_CFG_LANE_RST,
|
||||
HSIO_S6G_MISC_CFG_LANE_RST);
|
||||
|
||||
ret = serdes_commit_mcb_s6g(regmap, serdes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* OB + DES + IB + SER CFG */
|
||||
regmap_update_bits(regmap, HSIO_S6G_OB_CFG,
|
||||
HSIO_S6G_OB_CFG_OB_IDLE |
|
||||
HSIO_S6G_OB_CFG_OB_ENA1V_MODE |
|
||||
HSIO_S6G_OB_CFG_OB_POST0_M |
|
||||
HSIO_S6G_OB_CFG_OB_PREC_M,
|
||||
(ob_ena1v_mode ? HSIO_S6G_OB_CFG_OB_ENA1V_MODE : 0) |
|
||||
HSIO_S6G_OB_CFG_OB_POST0(0) |
|
||||
HSIO_S6G_OB_CFG_OB_PREC(0));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_OB_CFG1,
|
||||
HSIO_S6G_OB_CFG1_OB_ENA_CAS_M |
|
||||
HSIO_S6G_OB_CFG1_OB_LEV_M,
|
||||
HSIO_S6G_OB_CFG1_OB_LEV(ob_lev) |
|
||||
HSIO_S6G_OB_CFG1_OB_ENA_CAS(ob_ena_cas));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_DES_CFG,
|
||||
HSIO_S6G_DES_CFG_DES_PHS_CTRL_M |
|
||||
HSIO_S6G_DES_CFG_DES_CPMD_SEL_M |
|
||||
HSIO_S6G_DES_CFG_DES_BW_ANA_M,
|
||||
HSIO_S6G_DES_CFG_DES_PHS_CTRL(2) |
|
||||
HSIO_S6G_DES_CFG_DES_CPMD_SEL(0) |
|
||||
HSIO_S6G_DES_CFG_DES_BW_ANA(des_bw_ana));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
|
||||
HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M |
|
||||
HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M,
|
||||
HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |
|
||||
HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(0));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
|
||||
HSIO_S6G_IB_CFG1_IB_TSDET_M,
|
||||
HSIO_S6G_IB_CFG1_IB_TSDET(16));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_SER_CFG,
|
||||
HSIO_S6G_SER_CFG_SER_ALISEL_M |
|
||||
HSIO_S6G_SER_CFG_SER_ENALI,
|
||||
HSIO_S6G_SER_CFG_SER_ALISEL(0));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
|
||||
HSIO_S6G_PLL_CFG_PLL_DIV4 |
|
||||
HSIO_S6G_PLL_CFG_PLL_ENA_ROT |
|
||||
HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA_M |
|
||||
HSIO_S6G_PLL_CFG_PLL_ROT_DIR |
|
||||
HSIO_S6G_PLL_CFG_PLL_ROT_FRQ,
|
||||
HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA
|
||||
(pll_fsm_ctrl_data));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,
|
||||
HSIO_S6G_COMMON_CFG_SYS_RST |
|
||||
HSIO_S6G_COMMON_CFG_ENA_LANE |
|
||||
HSIO_S6G_COMMON_CFG_PWD_RX |
|
||||
HSIO_S6G_COMMON_CFG_PWD_TX |
|
||||
HSIO_S6G_COMMON_CFG_HRATE |
|
||||
HSIO_S6G_COMMON_CFG_QRATE |
|
||||
HSIO_S6G_COMMON_CFG_ENA_ELOOP |
|
||||
HSIO_S6G_COMMON_CFG_ENA_FLOOP |
|
||||
HSIO_S6G_COMMON_CFG_IF_MODE_M,
|
||||
HSIO_S6G_COMMON_CFG_SYS_RST |
|
||||
HSIO_S6G_COMMON_CFG_ENA_LANE |
|
||||
(qrate ? HSIO_S6G_COMMON_CFG_QRATE : 0) |
|
||||
HSIO_S6G_COMMON_CFG_IF_MODE(if_mode));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
|
||||
HSIO_S6G_MISC_CFG_LANE_RST |
|
||||
HSIO_S6G_MISC_CFG_DES_100FX_CPMD_ENA |
|
||||
HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA |
|
||||
HSIO_S6G_MISC_CFG_TX_LPI_MODE_ENA,
|
||||
HSIO_S6G_MISC_CFG_LANE_RST |
|
||||
HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA);
|
||||
|
||||
|
||||
ret = serdes_commit_mcb_s6g(regmap, serdes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
|
||||
HSIO_S6G_PLL_CFG_PLL_FSM_ENA,
|
||||
HSIO_S6G_PLL_CFG_PLL_FSM_ENA);
|
||||
|
||||
ret = serdes_commit_mcb_s6g(regmap, serdes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for PLL bringup */
|
||||
msleep(20);
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
|
||||
HSIO_S6G_IB_CFG_IB_CAL_ENA,
|
||||
HSIO_S6G_IB_CFG_IB_CAL_ENA);
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
|
||||
HSIO_S6G_MISC_CFG_LANE_RST, 0);
|
||||
|
||||
ret = serdes_commit_mcb_s6g(regmap, serdes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for calibration */
|
||||
msleep(60);
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
|
||||
HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M |
|
||||
HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M,
|
||||
HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |
|
||||
HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(7));
|
||||
|
||||
regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
|
||||
HSIO_S6G_IB_CFG1_IB_TSDET_M,
|
||||
HSIO_S6G_IB_CFG1_IB_TSDET(3));
|
||||
|
||||
/* IB CFG */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MCB_S1G_CFG_TIMEOUT 50
|
||||
|
||||
static int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op)
|
||||
@ -191,8 +423,12 @@ static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||
|
||||
if (macro->idx <= SERDES1G_MAX)
|
||||
return serdes_init_s1g(macro->ctrl->regs, macro->idx);
|
||||
else if (macro->idx <= SERDES6G_MAX)
|
||||
return serdes_init_s6g(macro->ctrl->regs,
|
||||
macro->idx - (SERDES1G_MAX + 1),
|
||||
ocelot_serdes_muxes[i].submode);
|
||||
|
||||
/* SERDES6G and PCIe not supported yet */
|
||||
/* PCIe not supported yet */
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
|
@ -384,10 +384,16 @@ int phy_reset(struct phy *phy)
|
||||
if (!phy || !phy->ops->reset)
|
||||
return 0;
|
||||
|
||||
ret = phy_pm_runtime_get_sync(phy);
|
||||
if (ret < 0 && ret != -ENOTSUPP)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
ret = phy->ops->reset(phy);
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
phy_pm_runtime_put(phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(phy_reset);
|
||||
@ -564,6 +570,11 @@ void phy_put(struct phy *phy)
|
||||
if (!phy || IS_ERR(phy))
|
||||
return;
|
||||
|
||||
mutex_lock(&phy->mutex);
|
||||
if (phy->ops->release)
|
||||
phy->ops->release(phy);
|
||||
mutex_unlock(&phy->mutex);
|
||||
|
||||
module_put(phy->ops->owner);
|
||||
put_device(&phy->dev);
|
||||
}
|
||||
|
@ -242,6 +242,88 @@ static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x0f),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x03),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x55),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x55),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x08),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x34),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x07),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8998_pcie_tx_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8998_pcie_rx_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x1c),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0a),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x00),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71),
|
||||
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8998_pcie_pcs_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x99),
|
||||
QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03),
|
||||
};
|
||||
|
||||
static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = {
|
||||
QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
|
||||
QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
|
||||
@ -897,6 +979,7 @@ struct qmp_phy {
|
||||
* @init_count: phy common block initialization count
|
||||
* @phy_initialized: indicate if PHY has been initialized
|
||||
* @mode: current PHY mode
|
||||
* @ufs_reset: optional UFS PHY reset handle
|
||||
*/
|
||||
struct qcom_qmp {
|
||||
struct device *dev;
|
||||
@ -914,6 +997,8 @@ struct qcom_qmp {
|
||||
int init_count;
|
||||
bool phy_initialized;
|
||||
enum phy_mode mode;
|
||||
|
||||
struct reset_control *ufs_reset;
|
||||
};
|
||||
|
||||
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
|
||||
@ -1146,6 +1231,31 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
|
||||
.no_pcs_sw_reset = true,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg msm8998_pciephy_cfg = {
|
||||
.type = PHY_TYPE_PCIE,
|
||||
.nlanes = 1,
|
||||
|
||||
.serdes_tbl = msm8998_pcie_serdes_tbl,
|
||||
.serdes_tbl_num = ARRAY_SIZE(msm8998_pcie_serdes_tbl),
|
||||
.tx_tbl = msm8998_pcie_tx_tbl,
|
||||
.tx_tbl_num = ARRAY_SIZE(msm8998_pcie_tx_tbl),
|
||||
.rx_tbl = msm8998_pcie_rx_tbl,
|
||||
.rx_tbl_num = ARRAY_SIZE(msm8998_pcie_rx_tbl),
|
||||
.pcs_tbl = msm8998_pcie_pcs_tbl,
|
||||
.pcs_tbl_num = ARRAY_SIZE(msm8998_pcie_pcs_tbl),
|
||||
.clk_list = msm8996_phy_clk_l,
|
||||
.num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
|
||||
.reset_list = ipq8074_pciephy_reset_l,
|
||||
.num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
|
||||
.vreg_list = qmp_phy_vreg_l,
|
||||
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
|
||||
.regs = pciephy_regs_layout,
|
||||
|
||||
.start_ctrl = SERDES_START | PCS_START,
|
||||
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
|
||||
.mask_com_pcs_ready = PCS_READY,
|
||||
};
|
||||
|
||||
static const struct qmp_phy_cfg msm8998_usb3phy_cfg = {
|
||||
.type = PHY_TYPE_USB3,
|
||||
.nlanes = 1,
|
||||
@ -1314,6 +1424,7 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
reset_control_assert(qmp->ufs_reset);
|
||||
if (cfg->has_phy_com_ctrl) {
|
||||
qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
|
||||
SERDES_START | PCS_START);
|
||||
@ -1335,8 +1446,7 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PHY Initialization */
|
||||
static int qcom_qmp_phy_init(struct phy *phy)
|
||||
static int qcom_qmp_phy_enable(struct phy *phy)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
@ -1351,6 +1461,33 @@ static int qcom_qmp_phy_init(struct phy *phy)
|
||||
|
||||
dev_vdbg(qmp->dev, "Initializing QMP phy\n");
|
||||
|
||||
if (cfg->no_pcs_sw_reset) {
|
||||
/*
|
||||
* Get UFS reset, which is delayed until now to avoid a
|
||||
* circular dependency where UFS needs its PHY, but the PHY
|
||||
* needs this UFS reset.
|
||||
*/
|
||||
if (!qmp->ufs_reset) {
|
||||
qmp->ufs_reset =
|
||||
devm_reset_control_get_exclusive(qmp->dev,
|
||||
"ufsphy");
|
||||
|
||||
if (IS_ERR(qmp->ufs_reset)) {
|
||||
ret = PTR_ERR(qmp->ufs_reset);
|
||||
dev_err(qmp->dev,
|
||||
"failed to get UFS reset: %d\n",
|
||||
ret);
|
||||
|
||||
qmp->ufs_reset = NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = reset_control_assert(qmp->ufs_reset);
|
||||
if (ret)
|
||||
goto err_lane_rst;
|
||||
}
|
||||
|
||||
ret = qcom_qmp_phy_com_init(qphy);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -1383,14 +1520,9 @@ static int qcom_qmp_phy_init(struct phy *phy)
|
||||
cfg->rx_tbl, cfg->rx_tbl_num);
|
||||
|
||||
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
||||
|
||||
/*
|
||||
* UFS PHY requires the deassert of software reset before serdes start.
|
||||
* For UFS PHYs that do not have software reset control bits, defer
|
||||
* starting serdes until the power on callback.
|
||||
*/
|
||||
if ((cfg->type == PHY_TYPE_UFS) && cfg->no_pcs_sw_reset)
|
||||
goto out;
|
||||
ret = reset_control_deassert(qmp->ufs_reset);
|
||||
if (ret)
|
||||
goto err_lane_rst;
|
||||
|
||||
/*
|
||||
* Pull out PHY from POWER DOWN state.
|
||||
@ -1403,7 +1535,9 @@ static int qcom_qmp_phy_init(struct phy *phy)
|
||||
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
|
||||
|
||||
/* Pull PHY out of reset state */
|
||||
if (!cfg->no_pcs_sw_reset)
|
||||
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||
|
||||
if (cfg->has_phy_dp_com_ctrl)
|
||||
qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
|
||||
|
||||
@ -1420,11 +1554,10 @@ static int qcom_qmp_phy_init(struct phy *phy)
|
||||
goto err_pcs_ready;
|
||||
}
|
||||
qmp->phy_initialized = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
err_pcs_ready:
|
||||
reset_control_assert(qmp->ufs_reset);
|
||||
clk_disable_unprepare(qphy->pipe_clk);
|
||||
err_clk_enable:
|
||||
if (cfg->has_lane_rst)
|
||||
@ -1435,7 +1568,7 @@ err_lane_rst:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_qmp_phy_exit(struct phy *phy)
|
||||
static int qcom_qmp_phy_disable(struct phy *phy)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
@ -1463,44 +1596,6 @@ static int qcom_qmp_phy_exit(struct phy *phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_qmp_phy_poweron(struct phy *phy)
|
||||
{
|
||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||
struct qcom_qmp *qmp = qphy->qmp;
|
||||
const struct qmp_phy_cfg *cfg = qmp->cfg;
|
||||
void __iomem *pcs = qphy->pcs;
|
||||
void __iomem *status;
|
||||
unsigned int mask, val;
|
||||
int ret = 0;
|
||||
|
||||
if (cfg->type != PHY_TYPE_UFS)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* For UFS PHY that has not software reset control, serdes start
|
||||
* should only happen when UFS driver explicitly calls phy_power_on
|
||||
* after it deasserts software reset.
|
||||
*/
|
||||
if (cfg->no_pcs_sw_reset && !qmp->phy_initialized &&
|
||||
(qmp->init_count != 0)) {
|
||||
/* start SerDes and Phy-Coding-Sublayer */
|
||||
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
|
||||
|
||||
status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
|
||||
mask = cfg->mask_pcs_ready;
|
||||
|
||||
ret = readl_poll_timeout(status, val, !(val & mask), 1,
|
||||
PHY_INIT_COMPLETE_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(qmp->dev, "phy initialization timed-out\n");
|
||||
return ret;
|
||||
}
|
||||
qmp->phy_initialized = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qcom_qmp_phy_set_mode(struct phy *phy,
|
||||
enum phy_mode mode, int submode)
|
||||
{
|
||||
@ -1750,9 +1845,15 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
|
||||
}
|
||||
|
||||
static const struct phy_ops qcom_qmp_phy_gen_ops = {
|
||||
.init = qcom_qmp_phy_init,
|
||||
.exit = qcom_qmp_phy_exit,
|
||||
.power_on = qcom_qmp_phy_poweron,
|
||||
.init = qcom_qmp_phy_enable,
|
||||
.exit = qcom_qmp_phy_disable,
|
||||
.set_mode = qcom_qmp_phy_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct phy_ops qcom_qmp_ufs_ops = {
|
||||
.power_on = qcom_qmp_phy_enable,
|
||||
.power_off = qcom_qmp_phy_disable,
|
||||
.set_mode = qcom_qmp_phy_set_mode,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
@ -1763,6 +1864,7 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
|
||||
struct qcom_qmp *qmp = dev_get_drvdata(dev);
|
||||
struct phy *generic_phy;
|
||||
struct qmp_phy *qphy;
|
||||
const struct phy_ops *ops = &qcom_qmp_phy_gen_ops;
|
||||
char prop_name[MAX_PROP_NAME];
|
||||
int ret;
|
||||
|
||||
@ -1849,7 +1951,10 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
|
||||
}
|
||||
}
|
||||
|
||||
generic_phy = devm_phy_create(dev, np, &qcom_qmp_phy_gen_ops);
|
||||
if (qmp->cfg->type == PHY_TYPE_UFS)
|
||||
ops = &qcom_qmp_ufs_ops;
|
||||
|
||||
generic_phy = devm_phy_create(dev, np, ops);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
ret = PTR_ERR(generic_phy);
|
||||
dev_err(dev, "failed to create qphy %d\n", ret);
|
||||
@ -1872,6 +1977,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
|
||||
}, {
|
||||
.compatible = "qcom,msm8996-qmp-usb3-phy",
|
||||
.data = &msm8996_usb3phy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,msm8998-qmp-pcie-phy",
|
||||
.data = &msm8998_pciephy_cfg,
|
||||
}, {
|
||||
.compatible = "qcom,msm8998-qmp-ufs-phy",
|
||||
.data = &sdm845_ufsphy_cfg,
|
||||
|
@ -241,6 +241,7 @@
|
||||
#define QSERDES_V3_RX_RX_BAND 0x110
|
||||
#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c
|
||||
#define QSERDES_V3_RX_RX_MODE_00 0x164
|
||||
#define QSERDES_V3_RX_RX_MODE_01 0x168
|
||||
|
||||
/* Only for QMP V3 PHY - PCS registers */
|
||||
#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004
|
||||
@ -280,6 +281,7 @@
|
||||
#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c
|
||||
#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
|
||||
#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4
|
||||
#define QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME 0x0a8
|
||||
#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK 0x0b0
|
||||
#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8
|
||||
#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc
|
||||
@ -292,13 +294,23 @@
|
||||
#define QPHY_V3_PCS_RX_MIN_HIBERN8_TIME 0x138
|
||||
#define QPHY_V3_PCS_RX_SIGDET_CTRL1 0x13c
|
||||
#define QPHY_V3_PCS_RX_SIGDET_CTRL2 0x140
|
||||
#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1a8
|
||||
#define QPHY_V3_PCS_OSC_DTCT_ACTIONS 0x1ac
|
||||
#define QPHY_V3_PCS_SIGDET_CNTRL 0x1b0
|
||||
#define QPHY_V3_PCS_TX_MID_TERM_CTRL1 0x1bc
|
||||
#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4
|
||||
#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
|
||||
#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1dc
|
||||
#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1e0
|
||||
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c
|
||||
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210
|
||||
|
||||
/* Only for QMP V3 PHY - PCS_MISC registers */
|
||||
#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c
|
||||
#define QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2 0x2c
|
||||
#define QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1 0x44
|
||||
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2 0x54
|
||||
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c
|
||||
#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60
|
||||
|
||||
#endif
|
||||
|
@ -822,14 +822,9 @@ static int qusb2_phy_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
qphy->iface_clk = devm_clk_get(dev, "iface");
|
||||
if (IS_ERR(qphy->iface_clk)) {
|
||||
ret = PTR_ERR(qphy->iface_clk);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
qphy->iface_clk = NULL;
|
||||
dev_dbg(dev, "failed to get iface clk, %d\n", ret);
|
||||
}
|
||||
qphy->iface_clk = devm_clk_get_optional(dev, "iface");
|
||||
if (IS_ERR(qphy->iface_clk))
|
||||
return PTR_ERR(qphy->iface_clk);
|
||||
|
||||
qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0);
|
||||
if (IS_ERR(qphy->phy_reset)) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
@ -96,11 +97,10 @@ struct ufs_qcom_phy {
|
||||
char name[UFS_QCOM_PHY_NAME_LEN];
|
||||
struct ufs_qcom_phy_calibration *cached_regs;
|
||||
int cached_regs_table_size;
|
||||
bool is_powered_on;
|
||||
bool is_started;
|
||||
struct ufs_qcom_phy_specific_ops *phy_spec_ops;
|
||||
|
||||
enum phy_mode mode;
|
||||
struct reset_control *ufs_reset;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -115,6 +115,7 @@ struct ufs_qcom_phy {
|
||||
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
|
||||
*/
|
||||
struct ufs_qcom_phy_specific_ops {
|
||||
int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B);
|
||||
void (*start_serdes)(struct ufs_qcom_phy *phy);
|
||||
int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
|
||||
void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
|
||||
|
@ -42,28 +42,6 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
|
||||
UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
bool is_rate_B = false;
|
||||
int ret;
|
||||
|
||||
if (phy_common->mode == PHY_MODE_UFS_HS_B)
|
||||
is_rate_B = true;
|
||||
|
||||
ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B);
|
||||
if (!ret)
|
||||
/* phy calibrated, but yet to be started */
|
||||
phy_common->is_started = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy,
|
||||
enum phy_mode mode, int submode)
|
||||
@ -124,8 +102,6 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
|
||||
}
|
||||
|
||||
static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
|
||||
.init = ufs_qcom_phy_qmp_14nm_init,
|
||||
.exit = ufs_qcom_phy_qmp_14nm_exit,
|
||||
.power_on = ufs_qcom_phy_power_on,
|
||||
.power_off = ufs_qcom_phy_power_off,
|
||||
.set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
|
||||
@ -133,6 +109,7 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
|
||||
.calibrate = ufs_qcom_phy_qmp_14nm_phy_calibrate,
|
||||
.start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
|
||||
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
|
||||
.set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,
|
||||
|
@ -61,28 +61,6 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
|
||||
UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
bool is_rate_B = false;
|
||||
int ret;
|
||||
|
||||
if (phy_common->mode == PHY_MODE_UFS_HS_B)
|
||||
is_rate_B = true;
|
||||
|
||||
ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B);
|
||||
if (!ret)
|
||||
/* phy calibrated, but yet to be started */
|
||||
phy_common->is_started = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy,
|
||||
enum phy_mode mode, int submode)
|
||||
@ -182,8 +160,6 @@ static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
|
||||
}
|
||||
|
||||
static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
|
||||
.init = ufs_qcom_phy_qmp_20nm_init,
|
||||
.exit = ufs_qcom_phy_qmp_20nm_exit,
|
||||
.power_on = ufs_qcom_phy_power_on,
|
||||
.power_off = ufs_qcom_phy_power_off,
|
||||
.set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
|
||||
@ -191,6 +167,7 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
|
||||
};
|
||||
|
||||
static struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
|
||||
.calibrate = ufs_qcom_phy_qmp_20nm_phy_calibrate,
|
||||
.start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
|
||||
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
|
||||
.set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,
|
||||
|
@ -147,6 +147,21 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe);
|
||||
|
||||
static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common)
|
||||
{
|
||||
struct reset_control *reset;
|
||||
|
||||
if (phy_common->ufs_reset)
|
||||
return 0;
|
||||
|
||||
reset = devm_reset_control_get_exclusive_by_index(phy_common->dev, 0);
|
||||
if (IS_ERR(reset))
|
||||
return PTR_ERR(reset);
|
||||
|
||||
phy_common->ufs_reset = reset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __ufs_qcom_phy_clk_get(struct device *dev,
|
||||
const char *name, struct clk **clk_out, bool err_print)
|
||||
{
|
||||
@ -528,12 +543,30 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
struct device *dev = phy_common->dev;
|
||||
bool is_rate_B = false;
|
||||
int err;
|
||||
|
||||
if (phy_common->is_powered_on)
|
||||
return 0;
|
||||
err = ufs_qcom_phy_get_reset(phy_common);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = reset_control_assert(phy_common->ufs_reset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (phy_common->mode == PHY_MODE_UFS_HS_B)
|
||||
is_rate_B = true;
|
||||
|
||||
err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = reset_control_deassert(phy_common->ufs_reset);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to assert UFS PHY reset");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!phy_common->is_started) {
|
||||
err = ufs_qcom_phy_start_serdes(phy_common);
|
||||
if (err)
|
||||
return err;
|
||||
@ -542,9 +575,6 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
phy_common->is_started = true;
|
||||
}
|
||||
|
||||
err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy);
|
||||
if (err) {
|
||||
dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
|
||||
@ -587,7 +617,6 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
|
||||
}
|
||||
}
|
||||
|
||||
phy_common->is_powered_on = true;
|
||||
goto out;
|
||||
|
||||
out_disable_ref_clk:
|
||||
@ -607,9 +636,6 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy)
|
||||
{
|
||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||
|
||||
if (!phy_common->is_powered_on)
|
||||
return 0;
|
||||
|
||||
phy_common->phy_spec_ops->power_control(phy_common, false);
|
||||
|
||||
if (phy_common->vddp_ref_clk.reg)
|
||||
@ -620,8 +646,7 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy)
|
||||
|
||||
ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll);
|
||||
ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy);
|
||||
phy_common->is_powered_on = false;
|
||||
|
||||
reset_control_assert(phy_common->ufs_reset);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
|
||||
|
@ -19,7 +19,7 @@ config PHY_RCAR_GEN3_PCIE
|
||||
config PHY_RCAR_GEN3_USB2
|
||||
tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
|
||||
depends on ARCH_RENESAS
|
||||
depends on EXTCON
|
||||
depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in
|
||||
depends on USB_SUPPORT
|
||||
select GENERIC_PHY
|
||||
select USB_COMMON
|
||||
|
@ -4,6 +4,7 @@
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Solutions Corp.
|
||||
* Copyright (C) 2014 Cogent Embedded, Inc.
|
||||
* Copyright (C) 2019 Renesas Electronics Corp.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
@ -15,6 +16,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define USBHS_LPSTS 0x02
|
||||
#define USBHS_UGCTRL 0x80
|
||||
@ -35,6 +37,8 @@
|
||||
#define USBHS_UGCTRL2_USB0SEL 0x00000030
|
||||
#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010
|
||||
#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030
|
||||
#define USBHS_UGCTRL2_USB0SEL_USB20 0x00000010
|
||||
#define USBHS_UGCTRL2_USB0SEL_HS_USB20 0x00000020
|
||||
|
||||
/* USB General status register (UGSTS) */
|
||||
#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */
|
||||
@ -64,6 +68,11 @@ struct rcar_gen2_phy_driver {
|
||||
struct rcar_gen2_channel *channels;
|
||||
};
|
||||
|
||||
struct rcar_gen2_phy_data {
|
||||
const struct phy_ops *gen2_phy_ops;
|
||||
const u32 (*select_value)[PHYS_PER_CHANNEL];
|
||||
};
|
||||
|
||||
static int rcar_gen2_phy_init(struct phy *p)
|
||||
{
|
||||
struct rcar_gen2_phy *phy = phy_get_drvdata(p);
|
||||
@ -180,6 +189,60 @@ static int rcar_gen2_phy_power_off(struct phy *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rz_g1c_phy_power_on(struct phy *p)
|
||||
{
|
||||
struct rcar_gen2_phy *phy = phy_get_drvdata(p);
|
||||
struct rcar_gen2_phy_driver *drv = phy->channel->drv;
|
||||
void __iomem *base = drv->base;
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
|
||||
spin_lock_irqsave(&drv->lock, flags);
|
||||
|
||||
/* Power on USBHS PHY */
|
||||
value = readl(base + USBHS_UGCTRL);
|
||||
value &= ~USBHS_UGCTRL_PLLRESET;
|
||||
writel(value, base + USBHS_UGCTRL);
|
||||
|
||||
/* As per the data sheet wait 340 micro sec for power stable */
|
||||
udelay(340);
|
||||
|
||||
if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) {
|
||||
value = readw(base + USBHS_LPSTS);
|
||||
value |= USBHS_LPSTS_SUSPM;
|
||||
writew(value, base + USBHS_LPSTS);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&drv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rz_g1c_phy_power_off(struct phy *p)
|
||||
{
|
||||
struct rcar_gen2_phy *phy = phy_get_drvdata(p);
|
||||
struct rcar_gen2_phy_driver *drv = phy->channel->drv;
|
||||
void __iomem *base = drv->base;
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
|
||||
spin_lock_irqsave(&drv->lock, flags);
|
||||
/* Power off USBHS PHY */
|
||||
if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) {
|
||||
value = readw(base + USBHS_LPSTS);
|
||||
value &= ~USBHS_LPSTS_SUSPM;
|
||||
writew(value, base + USBHS_LPSTS);
|
||||
}
|
||||
|
||||
value = readl(base + USBHS_UGCTRL);
|
||||
value |= USBHS_UGCTRL_PLLRESET;
|
||||
writel(value, base + USBHS_UGCTRL);
|
||||
|
||||
spin_unlock_irqrestore(&drv->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops rcar_gen2_phy_ops = {
|
||||
.init = rcar_gen2_phy_init,
|
||||
.exit = rcar_gen2_phy_exit,
|
||||
@ -188,12 +251,55 @@ static const struct phy_ops rcar_gen2_phy_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct phy_ops rz_g1c_phy_ops = {
|
||||
.init = rcar_gen2_phy_init,
|
||||
.exit = rcar_gen2_phy_exit,
|
||||
.power_on = rz_g1c_phy_power_on,
|
||||
.power_off = rz_g1c_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const u32 pci_select_value[][PHYS_PER_CHANNEL] = {
|
||||
[0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB },
|
||||
[2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 },
|
||||
};
|
||||
|
||||
static const u32 usb20_select_value[][PHYS_PER_CHANNEL] = {
|
||||
{ USBHS_UGCTRL2_USB0SEL_USB20, USBHS_UGCTRL2_USB0SEL_HS_USB20 },
|
||||
};
|
||||
|
||||
static const struct rcar_gen2_phy_data rcar_gen2_usb_phy_data = {
|
||||
.gen2_phy_ops = &rcar_gen2_phy_ops,
|
||||
.select_value = pci_select_value,
|
||||
};
|
||||
|
||||
static const struct rcar_gen2_phy_data rz_g1c_usb_phy_data = {
|
||||
.gen2_phy_ops = &rz_g1c_phy_ops,
|
||||
.select_value = usb20_select_value,
|
||||
};
|
||||
|
||||
static const struct of_device_id rcar_gen2_phy_match_table[] = {
|
||||
{ .compatible = "renesas,usb-phy-r8a7790" },
|
||||
{ .compatible = "renesas,usb-phy-r8a7791" },
|
||||
{ .compatible = "renesas,usb-phy-r8a7794" },
|
||||
{ .compatible = "renesas,rcar-gen2-usb-phy" },
|
||||
{ }
|
||||
{
|
||||
.compatible = "renesas,usb-phy-r8a77470",
|
||||
.data = &rz_g1c_usb_phy_data,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usb-phy-r8a7790",
|
||||
.data = &rcar_gen2_usb_phy_data,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usb-phy-r8a7791",
|
||||
.data = &rcar_gen2_usb_phy_data,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usb-phy-r8a7794",
|
||||
.data = &rcar_gen2_usb_phy_data,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,rcar-gen2-usb-phy",
|
||||
.data = &rcar_gen2_usb_phy_data,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
|
||||
|
||||
@ -224,11 +330,6 @@ static const u32 select_mask[] = {
|
||||
[2] = USBHS_UGCTRL2_USB2SEL,
|
||||
};
|
||||
|
||||
static const u32 select_value[][PHYS_PER_CHANNEL] = {
|
||||
[0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB },
|
||||
[2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 },
|
||||
};
|
||||
|
||||
static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -238,6 +339,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
const struct rcar_gen2_phy_data *data;
|
||||
int i = 0;
|
||||
|
||||
if (!dev->of_node) {
|
||||
@ -266,6 +368,10 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||
drv->clk = clk;
|
||||
drv->base = base;
|
||||
|
||||
data = of_device_get_match_data(dev);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
drv->num_channels = of_get_child_count(dev->of_node);
|
||||
drv->channels = devm_kcalloc(dev, drv->num_channels,
|
||||
sizeof(struct rcar_gen2_channel),
|
||||
@ -294,10 +400,10 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||
|
||||
phy->channel = channel;
|
||||
phy->number = n;
|
||||
phy->select_value = select_value[channel_num][n];
|
||||
phy->select_value = data->select_value[channel_num][n];
|
||||
|
||||
phy->phy = devm_phy_create(dev, NULL,
|
||||
&rcar_gen2_phy_ops);
|
||||
data->gen2_phy_ops);
|
||||
if (IS_ERR(phy->phy)) {
|
||||
dev_err(dev, "Failed to create PHY\n");
|
||||
return PTR_ERR(phy->phy);
|
||||
|
@ -37,11 +37,8 @@
|
||||
|
||||
/* INT_ENABLE */
|
||||
#define USB2_INT_ENABLE_UCOM_INTEN BIT(3)
|
||||
#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2)
|
||||
#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1)
|
||||
#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \
|
||||
USB2_INT_ENABLE_USBH_INTB_EN | \
|
||||
USB2_INT_ENABLE_USBH_INTA_EN)
|
||||
#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) /* For EHCI */
|
||||
#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) /* For OHCI */
|
||||
|
||||
/* USBCTR */
|
||||
#define USB2_USBCTR_DIRPD BIT(2)
|
||||
@ -78,10 +75,35 @@
|
||||
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
|
||||
#define USB2_ADPCTRL_DRVVBUS BIT(4)
|
||||
|
||||
#define NUM_OF_PHYS 4
|
||||
enum rcar_gen3_phy_index {
|
||||
PHY_INDEX_BOTH_HC,
|
||||
PHY_INDEX_OHCI,
|
||||
PHY_INDEX_EHCI,
|
||||
PHY_INDEX_HSUSB
|
||||
};
|
||||
|
||||
static const u32 rcar_gen3_int_enable[NUM_OF_PHYS] = {
|
||||
USB2_INT_ENABLE_USBH_INTB_EN | USB2_INT_ENABLE_USBH_INTA_EN,
|
||||
USB2_INT_ENABLE_USBH_INTA_EN,
|
||||
USB2_INT_ENABLE_USBH_INTB_EN,
|
||||
0
|
||||
};
|
||||
|
||||
struct rcar_gen3_phy {
|
||||
struct phy *phy;
|
||||
struct rcar_gen3_chan *ch;
|
||||
u32 int_enable_bits;
|
||||
bool initialized;
|
||||
bool otg_initialized;
|
||||
bool powered;
|
||||
};
|
||||
|
||||
struct rcar_gen3_chan {
|
||||
void __iomem *base;
|
||||
struct device *dev; /* platform_device's device */
|
||||
struct extcon_dev *extcon;
|
||||
struct phy *phy;
|
||||
struct rcar_gen3_phy rphys[NUM_OF_PHYS];
|
||||
struct regulator *vbus;
|
||||
struct work_struct work;
|
||||
enum usb_dr_mode dr_mode;
|
||||
@ -120,7 +142,7 @@ static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 val = readl(usb2_base + USB2_COMMCTRL);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host);
|
||||
dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, host);
|
||||
if (host)
|
||||
val &= ~USB2_COMMCTRL_OTG_PERI;
|
||||
else
|
||||
@ -133,7 +155,7 @@ static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm)
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 val = readl(usb2_base + USB2_LINECTRL1);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm);
|
||||
dev_vdbg(ch->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm);
|
||||
val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD);
|
||||
if (dp)
|
||||
val |= USB2_LINECTRL1_DP_RPD;
|
||||
@ -147,7 +169,7 @@ static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
|
||||
void __iomem *usb2_base = ch->base;
|
||||
u32 val = readl(usb2_base + USB2_ADPCTRL);
|
||||
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus);
|
||||
dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, vbus);
|
||||
if (vbus)
|
||||
val |= USB2_ADPCTRL_DRVVBUS;
|
||||
else
|
||||
@ -249,6 +271,42 @@ static enum phy_mode rcar_gen3_get_phy_mode(struct rcar_gen3_chan *ch)
|
||||
return PHY_MODE_USB_DEVICE;
|
||||
}
|
||||
|
||||
static bool rcar_gen3_is_any_rphy_initialized(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_OF_PHYS; i++) {
|
||||
if (ch->rphys[i].initialized)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool rcar_gen3_needs_init_otg(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_OF_PHYS; i++) {
|
||||
if (ch->rphys[i].otg_initialized)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rcar_gen3_are_all_rphys_power_off(struct rcar_gen3_chan *ch)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_OF_PHYS; i++) {
|
||||
if (ch->rphys[i].powered)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t role_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
@ -256,7 +314,7 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
|
||||
bool is_b_device;
|
||||
enum phy_mode cur_mode, new_mode;
|
||||
|
||||
if (!ch->is_otg_channel || !ch->phy->init_count)
|
||||
if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch))
|
||||
return -EIO;
|
||||
|
||||
if (!strncmp(buf, "host", strlen("host")))
|
||||
@ -294,7 +352,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
|
||||
|
||||
if (!ch->is_otg_channel || !ch->phy->init_count)
|
||||
if (!ch->is_otg_channel || !rcar_gen3_is_any_rphy_initialized(ch))
|
||||
return -EIO;
|
||||
|
||||
return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" :
|
||||
@ -328,37 +386,62 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
|
||||
|
||||
static int rcar_gen3_phy_usb2_init(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
|
||||
struct rcar_gen3_chan *channel = rphy->ch;
|
||||
void __iomem *usb2_base = channel->base;
|
||||
u32 val;
|
||||
|
||||
/* Initialize USB2 part */
|
||||
writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE);
|
||||
val = readl(usb2_base + USB2_INT_ENABLE);
|
||||
val |= USB2_INT_ENABLE_UCOM_INTEN | rphy->int_enable_bits;
|
||||
writel(val, usb2_base + USB2_INT_ENABLE);
|
||||
writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
|
||||
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
|
||||
|
||||
/* Initialize otg part */
|
||||
if (channel->is_otg_channel)
|
||||
if (channel->is_otg_channel) {
|
||||
if (rcar_gen3_needs_init_otg(channel))
|
||||
rcar_gen3_init_otg(channel);
|
||||
rphy->otg_initialized = true;
|
||||
}
|
||||
|
||||
rphy->initialized = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_exit(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
|
||||
struct rcar_gen3_chan *channel = rphy->ch;
|
||||
void __iomem *usb2_base = channel->base;
|
||||
u32 val;
|
||||
|
||||
writel(0, channel->base + USB2_INT_ENABLE);
|
||||
rphy->initialized = false;
|
||||
|
||||
if (channel->is_otg_channel)
|
||||
rphy->otg_initialized = false;
|
||||
|
||||
val = readl(usb2_base + USB2_INT_ENABLE);
|
||||
val &= ~rphy->int_enable_bits;
|
||||
if (!rcar_gen3_is_any_rphy_initialized(channel))
|
||||
val &= ~USB2_INT_ENABLE_UCOM_INTEN;
|
||||
writel(val, usb2_base + USB2_INT_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
|
||||
struct rcar_gen3_chan *channel = rphy->ch;
|
||||
void __iomem *usb2_base = channel->base;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (!rcar_gen3_are_all_rphys_power_off(channel))
|
||||
return 0;
|
||||
|
||||
if (channel->vbus) {
|
||||
ret = regulator_enable(channel->vbus);
|
||||
if (ret)
|
||||
@ -371,14 +454,22 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
||||
val &= ~USB2_USBCTR_PLL_RST;
|
||||
writel(val, usb2_base + USB2_USBCTR);
|
||||
|
||||
rphy->powered = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_power_off(struct phy *p)
|
||||
{
|
||||
struct rcar_gen3_chan *channel = phy_get_drvdata(p);
|
||||
struct rcar_gen3_phy *rphy = phy_get_drvdata(p);
|
||||
struct rcar_gen3_chan *channel = rphy->ch;
|
||||
int ret = 0;
|
||||
|
||||
rphy->powered = false;
|
||||
|
||||
if (!rcar_gen3_are_all_rphys_power_off(channel))
|
||||
return 0;
|
||||
|
||||
if (channel->vbus)
|
||||
ret = regulator_disable(channel->vbus);
|
||||
|
||||
@ -393,6 +484,12 @@ static const struct phy_ops rcar_gen3_phy_usb2_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct phy_ops rz_g1c_phy_usb2_ops = {
|
||||
.init = rcar_gen3_phy_usb2_init,
|
||||
.exit = rcar_gen3_phy_usb2_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
|
||||
{
|
||||
struct rcar_gen3_chan *ch = _ch;
|
||||
@ -401,7 +498,7 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
if (status & USB2_OBINT_BITS) {
|
||||
dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status);
|
||||
dev_vdbg(ch->dev, "%s: %08x\n", __func__, status);
|
||||
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
|
||||
rcar_gen3_device_recognition(ch);
|
||||
ret = IRQ_HANDLED;
|
||||
@ -411,11 +508,27 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
|
||||
}
|
||||
|
||||
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
|
||||
{ .compatible = "renesas,usb2-phy-r8a7795" },
|
||||
{ .compatible = "renesas,usb2-phy-r8a7796" },
|
||||
{ .compatible = "renesas,usb2-phy-r8a77965" },
|
||||
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
|
||||
{ }
|
||||
{
|
||||
.compatible = "renesas,usb2-phy-r8a77470",
|
||||
.data = &rz_g1c_phy_usb2_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usb2-phy-r8a7795",
|
||||
.data = &rcar_gen3_phy_usb2_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usb2-phy-r8a7796",
|
||||
.data = &rcar_gen3_phy_usb2_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,usb2-phy-r8a77965",
|
||||
.data = &rcar_gen3_phy_usb2_ops,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,rcar-gen3-usb2-phy",
|
||||
.data = &rcar_gen3_phy_usb2_ops,
|
||||
},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
|
||||
|
||||
@ -425,13 +538,54 @@ static const unsigned int rcar_gen3_phy_cable[] = {
|
||||
EXTCON_NONE,
|
||||
};
|
||||
|
||||
static struct phy *rcar_gen3_phy_usb2_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
|
||||
|
||||
if (args->args_count == 0) /* For old version dts */
|
||||
return ch->rphys[PHY_INDEX_BOTH_HC].phy;
|
||||
else if (args->args_count > 1) /* Prevent invalid args count */
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
if (args->args[0] >= NUM_OF_PHYS)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return ch->rphys[args->args[0]].phy;
|
||||
}
|
||||
|
||||
static enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np)
|
||||
{
|
||||
enum usb_dr_mode candidate = USB_DR_MODE_UNKNOWN;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* If one of device nodes has other dr_mode except UNKNOWN,
|
||||
* this function returns UNKNOWN. To achieve backward compatibility,
|
||||
* this loop starts the index as 0.
|
||||
*/
|
||||
for (i = 0; i < NUM_OF_PHYS; i++) {
|
||||
enum usb_dr_mode mode = of_usb_get_dr_mode_by_phy(np, i);
|
||||
|
||||
if (mode != USB_DR_MODE_UNKNOWN) {
|
||||
if (candidate == USB_DR_MODE_UNKNOWN)
|
||||
candidate = mode;
|
||||
else if (candidate != mode)
|
||||
return USB_DR_MODE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rcar_gen3_chan *channel;
|
||||
struct phy_provider *provider;
|
||||
struct resource *res;
|
||||
int irq, ret = 0;
|
||||
const struct phy_ops *phy_usb2_ops;
|
||||
int irq, ret = 0, i;
|
||||
|
||||
if (!dev->of_node) {
|
||||
dev_err(dev, "This driver needs device tree\n");
|
||||
@ -457,7 +611,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
dev_err(dev, "No irq handler (%d)\n", irq);
|
||||
}
|
||||
|
||||
channel->dr_mode = of_usb_get_dr_mode_by_phy(dev->of_node, 0);
|
||||
channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node);
|
||||
if (channel->dr_mode != USB_DR_MODE_UNKNOWN) {
|
||||
int ret;
|
||||
|
||||
@ -481,12 +635,22 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
* And then, phy-core will manage runtime pm for this device.
|
||||
*/
|
||||
pm_runtime_enable(dev);
|
||||
channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops);
|
||||
if (IS_ERR(channel->phy)) {
|
||||
phy_usb2_ops = of_device_get_match_data(dev);
|
||||
if (!phy_usb2_ops)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < NUM_OF_PHYS; i++) {
|
||||
channel->rphys[i].phy = devm_phy_create(dev, NULL,
|
||||
phy_usb2_ops);
|
||||
if (IS_ERR(channel->rphys[i].phy)) {
|
||||
dev_err(dev, "Failed to create USB2 PHY\n");
|
||||
ret = PTR_ERR(channel->phy);
|
||||
ret = PTR_ERR(channel->rphys[i].phy);
|
||||
goto error;
|
||||
}
|
||||
channel->rphys[i].ch = channel;
|
||||
channel->rphys[i].int_enable_bits = rcar_gen3_int_enable[i];
|
||||
phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]);
|
||||
}
|
||||
|
||||
channel->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||
if (IS_ERR(channel->vbus)) {
|
||||
@ -498,9 +662,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, channel);
|
||||
phy_set_drvdata(channel->phy, channel);
|
||||
channel->dev = dev;
|
||||
|
||||
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
provider = devm_of_phy_provider_register(dev, rcar_gen3_phy_usb2_xlate);
|
||||
if (IS_ERR(provider)) {
|
||||
dev_err(dev, "Failed to register PHY provider\n");
|
||||
ret = PTR_ERR(provider);
|
||||
|
@ -87,6 +87,7 @@ struct rockchip_emmc_phy {
|
||||
unsigned int reg_offset;
|
||||
struct regmap *reg_base;
|
||||
struct clk *emmcclk;
|
||||
unsigned int drive_impedance;
|
||||
};
|
||||
|
||||
static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
|
||||
@ -281,10 +282,10 @@ static int rockchip_emmc_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||
|
||||
/* Drive impedance: 50 Ohm */
|
||||
/* Drive impedance: from DTS */
|
||||
regmap_write(rk_phy->reg_base,
|
||||
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||
HIWORD_UPDATE(PHYCTRL_DR_50OHM,
|
||||
HIWORD_UPDATE(rk_phy->drive_impedance,
|
||||
PHYCTRL_DR_MASK,
|
||||
PHYCTRL_DR_SHIFT));
|
||||
|
||||
@ -314,6 +315,26 @@ static const struct phy_ops ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static u32 convert_drive_impedance_ohm(struct platform_device *pdev, u32 dr_ohm)
|
||||
{
|
||||
switch (dr_ohm) {
|
||||
case 100:
|
||||
return PHYCTRL_DR_100OHM;
|
||||
case 66:
|
||||
return PHYCTRL_DR_66OHM;
|
||||
case 50:
|
||||
return PHYCTRL_DR_50OHM;
|
||||
case 40:
|
||||
return PHYCTRL_DR_40OHM;
|
||||
case 33:
|
||||
return PHYCTRL_DR_33OHM;
|
||||
}
|
||||
|
||||
dev_warn(&pdev->dev, "Invalid value %u for drive-impedance-ohm.\n",
|
||||
dr_ohm);
|
||||
return PHYCTRL_DR_50OHM;
|
||||
}
|
||||
|
||||
static int rockchip_emmc_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -322,6 +343,7 @@ static int rockchip_emmc_phy_probe(struct platform_device *pdev)
|
||||
struct phy_provider *phy_provider;
|
||||
struct regmap *grf;
|
||||
unsigned int reg_offset;
|
||||
u32 val;
|
||||
|
||||
if (!dev->parent || !dev->parent->of_node)
|
||||
return -ENODEV;
|
||||
@ -344,6 +366,10 @@ static int rockchip_emmc_phy_probe(struct platform_device *pdev)
|
||||
|
||||
rk_phy->reg_offset = reg_offset;
|
||||
rk_phy->reg_base = grf;
|
||||
rk_phy->drive_impedance = PHYCTRL_DR_50OHM;
|
||||
|
||||
if (!of_property_read_u32(dev->of_node, "drive-impedance-ohm", &val))
|
||||
rk_phy->drive_impedance = convert_drive_impedance_ohm(pdev, val);
|
||||
|
||||
generic_phy = devm_phy_create(dev, dev->of_node, &ops);
|
||||
if (IS_ERR(generic_phy)) {
|
||||
|
@ -335,13 +335,9 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->clk_parent))
|
||||
return PTR_ERR(priv->clk_parent);
|
||||
|
||||
priv->clk_ext = devm_clk_get(dev, "phy-ext");
|
||||
if (IS_ERR(priv->clk_ext)) {
|
||||
if (PTR_ERR(priv->clk_ext) == -ENOENT)
|
||||
priv->clk_ext = NULL;
|
||||
else
|
||||
priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
|
||||
if (IS_ERR(priv->clk_ext))
|
||||
return PTR_ERR(priv->clk_ext);
|
||||
}
|
||||
|
||||
priv->rst = devm_reset_control_get_shared(dev, "phy");
|
||||
if (IS_ERR(priv->rst))
|
||||
|
@ -238,13 +238,9 @@ static int uniphier_u3ssphy_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(priv->clk))
|
||||
return PTR_ERR(priv->clk);
|
||||
|
||||
priv->clk_ext = devm_clk_get(dev, "phy-ext");
|
||||
if (IS_ERR(priv->clk_ext)) {
|
||||
if (PTR_ERR(priv->clk_ext) == -ENOENT)
|
||||
priv->clk_ext = NULL;
|
||||
else
|
||||
priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
|
||||
if (IS_ERR(priv->clk_ext))
|
||||
return PTR_ERR(priv->clk_ext);
|
||||
}
|
||||
|
||||
priv->rst = devm_reset_control_get_shared(dev, "phy");
|
||||
if (IS_ERR(priv->rst))
|
||||
|
@ -4,3 +4,4 @@ phy-tegra-xusb-y += xusb.o
|
||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o
|
||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
|
||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o
|
||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
|
||||
|
899
drivers/phy/tegra/xusb-tegra186.c
Normal file
899
drivers/phy/tegra/xusb-tegra186.c
Normal file
@ -0,0 +1,899 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include "xusb.h"
|
||||
|
||||
/* FUSE USB_CALIB registers */
|
||||
#define HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? (11 + (x - 1) * 6) : 0)
|
||||
#define HS_CURR_LEVEL_PAD_MASK 0x3f
|
||||
#define HS_TERM_RANGE_ADJ_SHIFT 7
|
||||
#define HS_TERM_RANGE_ADJ_MASK 0xf
|
||||
#define HS_SQUELCH_SHIFT 29
|
||||
#define HS_SQUELCH_MASK 0x7
|
||||
|
||||
#define RPD_CTRL_SHIFT 0
|
||||
#define RPD_CTRL_MASK 0x1f
|
||||
|
||||
/* XUSB PADCTL registers */
|
||||
#define XUSB_PADCTL_USB2_PAD_MUX 0x4
|
||||
#define USB2_PORT_SHIFT(x) ((x) * 2)
|
||||
#define USB2_PORT_MASK 0x3
|
||||
#define PORT_XUSB 1
|
||||
#define HSIC_PORT_SHIFT(x) ((x) + 20)
|
||||
#define HSIC_PORT_MASK 0x1
|
||||
#define PORT_HSIC 0
|
||||
|
||||
#define XUSB_PADCTL_USB2_PORT_CAP 0x8
|
||||
#define XUSB_PADCTL_SS_PORT_CAP 0xc
|
||||
#define PORTX_CAP_SHIFT(x) ((x) * 4)
|
||||
#define PORT_CAP_MASK 0x3
|
||||
#define PORT_CAP_DISABLED 0x0
|
||||
#define PORT_CAP_HOST 0x1
|
||||
#define PORT_CAP_DEVICE 0x2
|
||||
#define PORT_CAP_OTG 0x3
|
||||
|
||||
#define XUSB_PADCTL_ELPG_PROGRAM 0x20
|
||||
#define USB2_PORT_WAKE_INTERRUPT_ENABLE(x) BIT(x)
|
||||
#define USB2_PORT_WAKEUP_EVENT(x) BIT((x) + 7)
|
||||
#define SS_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 14)
|
||||
#define SS_PORT_WAKEUP_EVENT(x) BIT((x) + 21)
|
||||
#define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) BIT((x) + 28)
|
||||
#define USB2_HSIC_PORT_WAKEUP_EVENT(x) BIT((x) + 30)
|
||||
#define ALL_WAKE_EVENTS \
|
||||
(USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \
|
||||
USB2_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(0) | \
|
||||
SS_PORT_WAKEUP_EVENT(1) | SS_PORT_WAKEUP_EVENT(2) | \
|
||||
USB2_HSIC_PORT_WAKEUP_EVENT(0))
|
||||
|
||||
#define XUSB_PADCTL_ELPG_PROGRAM_1 0x24
|
||||
#define SSPX_ELPG_CLAMP_EN(x) BIT(0 + (x) * 3)
|
||||
#define SSPX_ELPG_CLAMP_EN_EARLY(x) BIT(1 + (x) * 3)
|
||||
#define SSPX_ELPG_VCORE_DOWN(x) BIT(2 + (x) * 3)
|
||||
|
||||
#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x88 + (x) * 0x40)
|
||||
#define HS_CURR_LEVEL(x) ((x) & 0x3f)
|
||||
#define TERM_SEL BIT(25)
|
||||
#define USB2_OTG_PD BIT(26)
|
||||
#define USB2_OTG_PD2 BIT(27)
|
||||
#define USB2_OTG_PD2_OVRD_EN BIT(28)
|
||||
#define USB2_OTG_PD_ZI BIT(29)
|
||||
|
||||
#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x8c + (x) * 0x40)
|
||||
#define USB2_OTG_PD_DR BIT(2)
|
||||
#define TERM_RANGE_ADJ(x) (((x) & 0xf) << 3)
|
||||
#define RPD_CTRL(x) (((x) & 0x1f) << 26)
|
||||
|
||||
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
|
||||
#define BIAS_PAD_PD BIT(11)
|
||||
#define HS_SQUELCH_LEVEL(x) (((x) & 0x7) << 0)
|
||||
|
||||
#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1 0x288
|
||||
#define USB2_TRK_START_TIMER(x) (((x) & 0x7f) << 12)
|
||||
#define USB2_TRK_DONE_RESET_TIMER(x) (((x) & 0x7f) << 19)
|
||||
#define USB2_PD_TRK BIT(26)
|
||||
|
||||
#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
|
||||
#define HSIC_PD_TX_DATA0 BIT(1)
|
||||
#define HSIC_PD_TX_STROBE BIT(3)
|
||||
#define HSIC_PD_RX_DATA0 BIT(4)
|
||||
#define HSIC_PD_RX_STROBE BIT(6)
|
||||
#define HSIC_PD_ZI_DATA0 BIT(7)
|
||||
#define HSIC_PD_ZI_STROBE BIT(9)
|
||||
#define HSIC_RPD_DATA0 BIT(13)
|
||||
#define HSIC_RPD_STROBE BIT(15)
|
||||
#define HSIC_RPU_DATA0 BIT(16)
|
||||
#define HSIC_RPU_STROBE BIT(18)
|
||||
|
||||
#define XUSB_PADCTL_HSIC_PAD_TRK_CTL0 0x340
|
||||
#define HSIC_TRK_START_TIMER(x) (((x) & 0x7f) << 5)
|
||||
#define HSIC_TRK_DONE_RESET_TIMER(x) (((x) & 0x7f) << 12)
|
||||
#define HSIC_PD_TRK BIT(19)
|
||||
|
||||
#define USB2_VBUS_ID 0x360
|
||||
#define VBUS_OVERRIDE BIT(14)
|
||||
#define ID_OVERRIDE(x) (((x) & 0xf) << 18)
|
||||
#define ID_OVERRIDE_FLOATING ID_OVERRIDE(8)
|
||||
#define ID_OVERRIDE_GROUNDED ID_OVERRIDE(0)
|
||||
|
||||
#define TEGRA186_LANE(_name, _offset, _shift, _mask, _type) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.offset = _offset, \
|
||||
.shift = _shift, \
|
||||
.mask = _mask, \
|
||||
.num_funcs = ARRAY_SIZE(tegra186_##_type##_functions), \
|
||||
.funcs = tegra186_##_type##_functions, \
|
||||
}
|
||||
|
||||
struct tegra_xusb_fuse_calibration {
|
||||
u32 *hs_curr_level;
|
||||
u32 hs_squelch;
|
||||
u32 hs_term_range_adj;
|
||||
u32 rpd_ctrl;
|
||||
};
|
||||
|
||||
struct tegra186_xusb_padctl {
|
||||
struct tegra_xusb_padctl base;
|
||||
|
||||
struct tegra_xusb_fuse_calibration calib;
|
||||
|
||||
/* UTMI bias and tracking */
|
||||
struct clk *usb2_trk_clk;
|
||||
unsigned int bias_pad_enable;
|
||||
};
|
||||
|
||||
static inline struct tegra186_xusb_padctl *
|
||||
to_tegra186_xusb_padctl(struct tegra_xusb_padctl *padctl)
|
||||
{
|
||||
return container_of(padctl, struct tegra186_xusb_padctl, base);
|
||||
}
|
||||
|
||||
/* USB 2.0 UTMI PHY support */
|
||||
static struct tegra_xusb_lane *
|
||||
tegra186_usb2_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
|
||||
unsigned int index)
|
||||
{
|
||||
struct tegra_xusb_usb2_lane *usb2;
|
||||
int err;
|
||||
|
||||
usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
|
||||
if (!usb2)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&usb2->base.list);
|
||||
usb2->base.soc = &pad->soc->lanes[index];
|
||||
usb2->base.index = index;
|
||||
usb2->base.pad = pad;
|
||||
usb2->base.np = np;
|
||||
|
||||
err = tegra_xusb_lane_parse_dt(&usb2->base, np);
|
||||
if (err < 0) {
|
||||
kfree(usb2);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return &usb2->base;
|
||||
}
|
||||
|
||||
static void tegra186_usb2_lane_remove(struct tegra_xusb_lane *lane)
|
||||
{
|
||||
struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
|
||||
|
||||
kfree(usb2);
|
||||
}
|
||||
|
||||
static const struct tegra_xusb_lane_ops tegra186_usb2_lane_ops = {
|
||||
.probe = tegra186_usb2_lane_probe,
|
||||
.remove = tegra186_usb2_lane_remove,
|
||||
};
|
||||
|
||||
static void tegra186_utmi_bias_pad_power_on(struct tegra_xusb_padctl *padctl)
|
||||
{
|
||||
struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
|
||||
struct device *dev = padctl->dev;
|
||||
u32 value;
|
||||
int err;
|
||||
|
||||
mutex_lock(&padctl->lock);
|
||||
|
||||
if (priv->bias_pad_enable++ > 0) {
|
||||
mutex_unlock(&padctl->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(priv->usb2_trk_clk);
|
||||
if (err < 0)
|
||||
dev_warn(dev, "failed to enable USB2 trk clock: %d\n", err);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
value &= ~USB2_TRK_START_TIMER(~0);
|
||||
value |= USB2_TRK_START_TIMER(0x1e);
|
||||
value &= ~USB2_TRK_DONE_RESET_TIMER(~0);
|
||||
value |= USB2_TRK_DONE_RESET_TIMER(0xa);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
|
||||
value &= ~BIAS_PAD_PD;
|
||||
value &= ~HS_SQUELCH_LEVEL(~0);
|
||||
value |= HS_SQUELCH_LEVEL(priv->calib.hs_squelch);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
|
||||
|
||||
udelay(1);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
value &= ~USB2_PD_TRK;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
|
||||
mutex_unlock(&padctl->lock);
|
||||
}
|
||||
|
||||
static void tegra186_utmi_bias_pad_power_off(struct tegra_xusb_padctl *padctl)
|
||||
{
|
||||
struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
|
||||
u32 value;
|
||||
|
||||
mutex_lock(&padctl->lock);
|
||||
|
||||
if (WARN_ON(priv->bias_pad_enable == 0)) {
|
||||
mutex_unlock(&padctl->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (--priv->bias_pad_enable > 0) {
|
||||
mutex_unlock(&padctl->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
value |= USB2_PD_TRK;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
|
||||
|
||||
clk_disable_unprepare(priv->usb2_trk_clk);
|
||||
|
||||
mutex_unlock(&padctl->lock);
|
||||
}
|
||||
|
||||
static void tegra_phy_xusb_utmi_pad_power_on(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
struct tegra_xusb_usb2_port *port;
|
||||
struct device *dev = padctl->dev;
|
||||
unsigned int index = lane->index;
|
||||
u32 value;
|
||||
|
||||
if (!phy)
|
||||
return;
|
||||
|
||||
port = tegra_xusb_find_usb2_port(padctl, index);
|
||||
if (!port) {
|
||||
dev_err(dev, "no port found for USB2 lane %u\n", index);
|
||||
return;
|
||||
}
|
||||
|
||||
tegra186_utmi_bias_pad_power_on(padctl);
|
||||
|
||||
udelay(2);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
|
||||
value &= ~USB2_OTG_PD;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
|
||||
value &= ~USB2_OTG_PD_DR;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
|
||||
}
|
||||
|
||||
static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
unsigned int index = lane->index;
|
||||
u32 value;
|
||||
|
||||
if (!phy)
|
||||
return;
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
|
||||
value |= USB2_OTG_PD;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
|
||||
value |= USB2_OTG_PD_DR;
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
|
||||
|
||||
udelay(2);
|
||||
|
||||
tegra186_utmi_bias_pad_power_off(padctl);
|
||||
}
|
||||
|
||||
static int tegra186_utmi_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
|
||||
struct tegra_xusb_usb2_port *port;
|
||||
unsigned int index = lane->index;
|
||||
struct device *dev = padctl->dev;
|
||||
u32 value;
|
||||
|
||||
port = tegra_xusb_find_usb2_port(padctl, index);
|
||||
if (!port) {
|
||||
dev_err(dev, "no port found for USB2 lane %u\n", index);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
|
||||
value &= ~(USB2_PORT_MASK << USB2_PORT_SHIFT(index));
|
||||
value |= (PORT_XUSB << USB2_PORT_SHIFT(index));
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
|
||||
value &= ~(PORT_CAP_MASK << PORTX_CAP_SHIFT(index));
|
||||
|
||||
if (port->mode == USB_DR_MODE_UNKNOWN)
|
||||
value |= (PORT_CAP_DISABLED << PORTX_CAP_SHIFT(index));
|
||||
else if (port->mode == USB_DR_MODE_PERIPHERAL)
|
||||
value |= (PORT_CAP_DEVICE << PORTX_CAP_SHIFT(index));
|
||||
else if (port->mode == USB_DR_MODE_HOST)
|
||||
value |= (PORT_CAP_HOST << PORTX_CAP_SHIFT(index));
|
||||
else if (port->mode == USB_DR_MODE_OTG)
|
||||
value |= (PORT_CAP_OTG << PORTX_CAP_SHIFT(index));
|
||||
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
|
||||
value &= ~USB2_OTG_PD_ZI;
|
||||
value |= TERM_SEL;
|
||||
value &= ~HS_CURR_LEVEL(~0);
|
||||
|
||||
if (usb2->hs_curr_level_offset) {
|
||||
int hs_current_level;
|
||||
|
||||
hs_current_level = (int)priv->calib.hs_curr_level[index] +
|
||||
usb2->hs_curr_level_offset;
|
||||
|
||||
if (hs_current_level < 0)
|
||||
hs_current_level = 0;
|
||||
if (hs_current_level > 0x3f)
|
||||
hs_current_level = 0x3f;
|
||||
|
||||
value |= HS_CURR_LEVEL(hs_current_level);
|
||||
} else {
|
||||
value |= HS_CURR_LEVEL(priv->calib.hs_curr_level[index]);
|
||||
}
|
||||
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
|
||||
value &= ~TERM_RANGE_ADJ(~0);
|
||||
value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj);
|
||||
value &= ~RPD_CTRL(~0);
|
||||
value |= RPD_CTRL(priv->calib.rpd_ctrl);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
|
||||
|
||||
/* TODO: pad power saving */
|
||||
tegra_phy_xusb_utmi_pad_power_on(phy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_utmi_phy_power_off(struct phy *phy)
|
||||
{
|
||||
/* TODO: pad power saving */
|
||||
tegra_phy_xusb_utmi_pad_power_down(phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_utmi_phy_init(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
struct tegra_xusb_usb2_port *port;
|
||||
unsigned int index = lane->index;
|
||||
struct device *dev = padctl->dev;
|
||||
int err;
|
||||
|
||||
port = tegra_xusb_find_usb2_port(padctl, index);
|
||||
if (!port) {
|
||||
dev_err(dev, "no port found for USB2 lane %u\n", index);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (port->supply && port->mode == USB_DR_MODE_HOST) {
|
||||
err = regulator_enable(port->supply);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to enable port %u VBUS: %d\n",
|
||||
index, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_utmi_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
struct tegra_xusb_usb2_port *port;
|
||||
unsigned int index = lane->index;
|
||||
struct device *dev = padctl->dev;
|
||||
int err;
|
||||
|
||||
port = tegra_xusb_find_usb2_port(padctl, index);
|
||||
if (!port) {
|
||||
dev_err(dev, "no port found for USB2 lane %u\n", index);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (port->supply && port->mode == USB_DR_MODE_HOST) {
|
||||
err = regulator_disable(port->supply);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to disable port %u VBUS: %d\n",
|
||||
index, err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops utmi_phy_ops = {
|
||||
.init = tegra186_utmi_phy_init,
|
||||
.exit = tegra186_utmi_phy_exit,
|
||||
.power_on = tegra186_utmi_phy_power_on,
|
||||
.power_off = tegra186_utmi_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct tegra_xusb_pad *
|
||||
tegra186_usb2_pad_probe(struct tegra_xusb_padctl *padctl,
|
||||
const struct tegra_xusb_pad_soc *soc,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct tegra186_xusb_padctl *priv = to_tegra186_xusb_padctl(padctl);
|
||||
struct tegra_xusb_usb2_pad *usb2;
|
||||
struct tegra_xusb_pad *pad;
|
||||
int err;
|
||||
|
||||
usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
|
||||
if (!usb2)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pad = &usb2->base;
|
||||
pad->ops = &tegra186_usb2_lane_ops;
|
||||
pad->soc = soc;
|
||||
|
||||
err = tegra_xusb_pad_init(pad, padctl, np);
|
||||
if (err < 0) {
|
||||
kfree(usb2);
|
||||
goto out;
|
||||
}
|
||||
|
||||
priv->usb2_trk_clk = devm_clk_get(&pad->dev, "trk");
|
||||
if (IS_ERR(priv->usb2_trk_clk)) {
|
||||
err = PTR_ERR(priv->usb2_trk_clk);
|
||||
dev_dbg(&pad->dev, "failed to get usb2 trk clock: %d\n", err);
|
||||
goto unregister;
|
||||
}
|
||||
|
||||
err = tegra_xusb_pad_register(pad, &utmi_phy_ops);
|
||||
if (err < 0)
|
||||
goto unregister;
|
||||
|
||||
dev_set_drvdata(&pad->dev, pad);
|
||||
|
||||
return pad;
|
||||
|
||||
unregister:
|
||||
device_unregister(&pad->dev);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void tegra186_usb2_pad_remove(struct tegra_xusb_pad *pad)
|
||||
{
|
||||
struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad);
|
||||
|
||||
kfree(usb2);
|
||||
}
|
||||
|
||||
static const struct tegra_xusb_pad_ops tegra186_usb2_pad_ops = {
|
||||
.probe = tegra186_usb2_pad_probe,
|
||||
.remove = tegra186_usb2_pad_remove,
|
||||
};
|
||||
|
||||
static const char * const tegra186_usb2_functions[] = {
|
||||
"xusb",
|
||||
};
|
||||
|
||||
static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = {
|
||||
TEGRA186_LANE("usb2-0", 0, 0, 0, usb2),
|
||||
TEGRA186_LANE("usb2-1", 0, 0, 0, usb2),
|
||||
TEGRA186_LANE("usb2-2", 0, 0, 0, usb2),
|
||||
};
|
||||
|
||||
static const struct tegra_xusb_pad_soc tegra186_usb2_pad = {
|
||||
.name = "usb2",
|
||||
.num_lanes = ARRAY_SIZE(tegra186_usb2_lanes),
|
||||
.lanes = tegra186_usb2_lanes,
|
||||
.ops = &tegra186_usb2_pad_ops,
|
||||
};
|
||||
|
||||
static int tegra186_usb2_port_enable(struct tegra_xusb_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra186_usb2_port_disable(struct tegra_xusb_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static struct tegra_xusb_lane *
|
||||
tegra186_usb2_port_map(struct tegra_xusb_port *port)
|
||||
{
|
||||
return tegra_xusb_find_lane(port->padctl, "usb2", port->index);
|
||||
}
|
||||
|
||||
static const struct tegra_xusb_port_ops tegra186_usb2_port_ops = {
|
||||
.enable = tegra186_usb2_port_enable,
|
||||
.disable = tegra186_usb2_port_disable,
|
||||
.map = tegra186_usb2_port_map,
|
||||
};
|
||||
|
||||
/* SuperSpeed PHY support */
|
||||
static struct tegra_xusb_lane *
|
||||
tegra186_usb3_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
|
||||
unsigned int index)
|
||||
{
|
||||
struct tegra_xusb_usb3_lane *usb3;
|
||||
int err;
|
||||
|
||||
usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL);
|
||||
if (!usb3)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&usb3->base.list);
|
||||
usb3->base.soc = &pad->soc->lanes[index];
|
||||
usb3->base.index = index;
|
||||
usb3->base.pad = pad;
|
||||
usb3->base.np = np;
|
||||
|
||||
err = tegra_xusb_lane_parse_dt(&usb3->base, np);
|
||||
if (err < 0) {
|
||||
kfree(usb3);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return &usb3->base;
|
||||
}
|
||||
|
||||
static void tegra186_usb3_lane_remove(struct tegra_xusb_lane *lane)
|
||||
{
|
||||
struct tegra_xusb_usb3_lane *usb3 = to_usb3_lane(lane);
|
||||
|
||||
kfree(usb3);
|
||||
}
|
||||
|
||||
static const struct tegra_xusb_lane_ops tegra186_usb3_lane_ops = {
|
||||
.probe = tegra186_usb3_lane_probe,
|
||||
.remove = tegra186_usb3_lane_remove,
|
||||
};
|
||||
static int tegra186_usb3_port_enable(struct tegra_xusb_port *port)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra186_usb3_port_disable(struct tegra_xusb_port *port)
|
||||
{
|
||||
}
|
||||
|
||||
static struct tegra_xusb_lane *
|
||||
tegra186_usb3_port_map(struct tegra_xusb_port *port)
|
||||
{
|
||||
return tegra_xusb_find_lane(port->padctl, "usb3", port->index);
|
||||
}
|
||||
|
||||
static const struct tegra_xusb_port_ops tegra186_usb3_port_ops = {
|
||||
.enable = tegra186_usb3_port_enable,
|
||||
.disable = tegra186_usb3_port_disable,
|
||||
.map = tegra186_usb3_port_map,
|
||||
};
|
||||
|
||||
static int tegra186_usb3_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
struct tegra_xusb_usb3_port *port;
|
||||
struct tegra_xusb_usb2_port *usb2;
|
||||
unsigned int index = lane->index;
|
||||
struct device *dev = padctl->dev;
|
||||
u32 value;
|
||||
|
||||
port = tegra_xusb_find_usb3_port(padctl, index);
|
||||
if (!port) {
|
||||
dev_err(dev, "no port found for USB3 lane %u\n", index);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
usb2 = tegra_xusb_find_usb2_port(padctl, port->port);
|
||||
if (!usb2) {
|
||||
dev_err(dev, "no companion port found for USB3 lane %u\n",
|
||||
index);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&padctl->lock);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP);
|
||||
value &= ~(PORT_CAP_MASK << PORTX_CAP_SHIFT(index));
|
||||
|
||||
if (usb2->mode == USB_DR_MODE_UNKNOWN)
|
||||
value |= (PORT_CAP_DISABLED << PORTX_CAP_SHIFT(index));
|
||||
else if (usb2->mode == USB_DR_MODE_PERIPHERAL)
|
||||
value |= (PORT_CAP_DEVICE << PORTX_CAP_SHIFT(index));
|
||||
else if (usb2->mode == USB_DR_MODE_HOST)
|
||||
value |= (PORT_CAP_HOST << PORTX_CAP_SHIFT(index));
|
||||
else if (usb2->mode == USB_DR_MODE_OTG)
|
||||
value |= (PORT_CAP_OTG << PORTX_CAP_SHIFT(index));
|
||||
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CAP);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
value &= ~SSPX_ELPG_VCORE_DOWN(index);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
|
||||
usleep_range(100, 200);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
value &= ~SSPX_ELPG_CLAMP_EN_EARLY(index);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
|
||||
usleep_range(100, 200);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
value &= ~SSPX_ELPG_CLAMP_EN(index);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
|
||||
mutex_unlock(&padctl->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_usb3_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
|
||||
struct tegra_xusb_padctl *padctl = lane->pad->padctl;
|
||||
struct tegra_xusb_usb3_port *port;
|
||||
unsigned int index = lane->index;
|
||||
struct device *dev = padctl->dev;
|
||||
u32 value;
|
||||
|
||||
port = tegra_xusb_find_usb3_port(padctl, index);
|
||||
if (!port) {
|
||||
dev_err(dev, "no port found for USB3 lane %u\n", index);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&padctl->lock);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
value |= SSPX_ELPG_CLAMP_EN_EARLY(index);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
|
||||
usleep_range(100, 200);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
value |= SSPX_ELPG_CLAMP_EN(index);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
|
||||
usleep_range(250, 350);
|
||||
|
||||
value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
value |= SSPX_ELPG_VCORE_DOWN(index);
|
||||
padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
|
||||
|
||||
mutex_unlock(&padctl->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_usb3_phy_init(struct phy *phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_usb3_phy_exit(struct phy *phy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops usb3_phy_ops = {
|
||||
.init = tegra186_usb3_phy_init,
|
||||
.exit = tegra186_usb3_phy_exit,
|
||||
.power_on = tegra186_usb3_phy_power_on,
|
||||
.power_off = tegra186_usb3_phy_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct tegra_xusb_pad *
|
||||
tegra186_usb3_pad_probe(struct tegra_xusb_padctl *padctl,
|
||||
const struct tegra_xusb_pad_soc *soc,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct tegra_xusb_usb3_pad *usb3;
|
||||
struct tegra_xusb_pad *pad;
|
||||
int err;
|
||||
|
||||
usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL);
|
||||
if (!usb3)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pad = &usb3->base;
|
||||
pad->ops = &tegra186_usb3_lane_ops;
|
||||
pad->soc = soc;
|
||||
|
||||
err = tegra_xusb_pad_init(pad, padctl, np);
|
||||
if (err < 0) {
|
||||
kfree(usb3);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = tegra_xusb_pad_register(pad, &usb3_phy_ops);
|
||||
if (err < 0)
|
||||
goto unregister;
|
||||
|
||||
dev_set_drvdata(&pad->dev, pad);
|
||||
|
||||
return pad;
|
||||
|
||||
unregister:
|
||||
device_unregister(&pad->dev);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void tegra186_usb3_pad_remove(struct tegra_xusb_pad *pad)
|
||||
{
|
||||
struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad);
|
||||
|
||||
kfree(usb2);
|
||||
}
|
||||
|
||||
static const struct tegra_xusb_pad_ops tegra186_usb3_pad_ops = {
|
||||
.probe = tegra186_usb3_pad_probe,
|
||||
.remove = tegra186_usb3_pad_remove,
|
||||
};
|
||||
|
||||
static const char * const tegra186_usb3_functions[] = {
|
||||
"xusb",
|
||||
};
|
||||
|
||||
static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = {
|
||||
TEGRA186_LANE("usb3-0", 0, 0, 0, usb3),
|
||||
TEGRA186_LANE("usb3-1", 0, 0, 0, usb3),
|
||||
TEGRA186_LANE("usb3-2", 0, 0, 0, usb3),
|
||||
};
|
||||
|
||||
static const struct tegra_xusb_pad_soc tegra186_usb3_pad = {
|
||||
.name = "usb3",
|
||||
.num_lanes = ARRAY_SIZE(tegra186_usb3_lanes),
|
||||
.lanes = tegra186_usb3_lanes,
|
||||
.ops = &tegra186_usb3_pad_ops,
|
||||
};
|
||||
|
||||
static const struct tegra_xusb_pad_soc * const tegra186_pads[] = {
|
||||
&tegra186_usb2_pad,
|
||||
&tegra186_usb3_pad,
|
||||
#if 0 /* TODO implement */
|
||||
&tegra186_hsic_pad,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int
|
||||
tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
|
||||
{
|
||||
struct device *dev = padctl->base.dev;
|
||||
unsigned int i, count;
|
||||
u32 value, *level;
|
||||
int err;
|
||||
|
||||
count = padctl->base.soc->ports.usb2.count;
|
||||
|
||||
level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL);
|
||||
if (!level)
|
||||
return -ENOMEM;
|
||||
|
||||
err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to read calibration fuse: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "FUSE_USB_CALIB_0 %#x\n", value);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
level[i] = (value >> HS_CURR_LEVEL_PADX_SHIFT(i)) &
|
||||
HS_CURR_LEVEL_PAD_MASK;
|
||||
|
||||
padctl->calib.hs_curr_level = level;
|
||||
|
||||
padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) &
|
||||
HS_SQUELCH_MASK;
|
||||
padctl->calib.hs_term_range_adj = (value >> HS_TERM_RANGE_ADJ_SHIFT) &
|
||||
HS_TERM_RANGE_ADJ_MASK;
|
||||
|
||||
err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to read calibration fuse: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "FUSE_USB_CALIB_EXT_0 %#x\n", value);
|
||||
|
||||
padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tegra_xusb_padctl *
|
||||
tegra186_xusb_padctl_probe(struct device *dev,
|
||||
const struct tegra_xusb_padctl_soc *soc)
|
||||
{
|
||||
struct tegra186_xusb_padctl *priv;
|
||||
int err;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
priv->base.dev = dev;
|
||||
priv->base.soc = soc;
|
||||
|
||||
err = tegra186_xusb_read_fuse_calibration(priv);
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return &priv->base;
|
||||
}
|
||||
|
||||
static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
|
||||
.probe = tegra186_xusb_padctl_probe,
|
||||
.remove = tegra186_xusb_padctl_remove,
|
||||
};
|
||||
|
||||
static const char * const tegra186_xusb_padctl_supply_names[] = {
|
||||
"avdd-pll-erefeut",
|
||||
"avdd-usb",
|
||||
"vclamp-usb",
|
||||
"vddio-hsic",
|
||||
};
|
||||
|
||||
const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
|
||||
.num_pads = ARRAY_SIZE(tegra186_pads),
|
||||
.pads = tegra186_pads,
|
||||
.ports = {
|
||||
.usb2 = {
|
||||
.ops = &tegra186_usb2_port_ops,
|
||||
.count = 3,
|
||||
},
|
||||
#if 0 /* TODO implement */
|
||||
.hsic = {
|
||||
.ops = &tegra186_hsic_port_ops,
|
||||
.count = 1,
|
||||
},
|
||||
#endif
|
||||
.usb3 = {
|
||||
.ops = &tegra186_usb3_port_ops,
|
||||
.count = 3,
|
||||
},
|
||||
},
|
||||
.ops = &tegra186_xusb_padctl_ops,
|
||||
.supply_names = tegra186_xusb_padctl_supply_names,
|
||||
.num_supplies = ARRAY_SIZE(tegra186_xusb_padctl_supply_names),
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc);
|
||||
|
||||
MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>");
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra186 XUSB Pad Controller driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@ -67,6 +67,12 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
|
||||
.compatible = "nvidia,tegra210-xusb-padctl",
|
||||
.data = &tegra210_xusb_padctl_soc,
|
||||
},
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
|
||||
{
|
||||
.compatible = "nvidia,tegra186-xusb-padctl",
|
||||
.data = &tegra186_xusb_padctl_soc,
|
||||
},
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
@ -313,6 +319,10 @@ static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
|
||||
const struct tegra_xusb_lane_soc *soc = lane->soc;
|
||||
u32 value;
|
||||
|
||||
/* skip single function lanes */
|
||||
if (soc->num_funcs < 2)
|
||||
return;
|
||||
|
||||
/* choose function */
|
||||
value = padctl_readl(padctl, soc->offset);
|
||||
value &= ~(soc->mask << soc->shift);
|
||||
@ -542,13 +552,34 @@ static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
|
||||
device_unregister(&port->dev);
|
||||
}
|
||||
|
||||
static const char *const modes[] = {
|
||||
[USB_DR_MODE_UNKNOWN] = "",
|
||||
[USB_DR_MODE_HOST] = "host",
|
||||
[USB_DR_MODE_PERIPHERAL] = "peripheral",
|
||||
[USB_DR_MODE_OTG] = "otg",
|
||||
};
|
||||
|
||||
static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
|
||||
{
|
||||
struct tegra_xusb_port *port = &usb2->base;
|
||||
struct device_node *np = port->dev.of_node;
|
||||
const char *mode;
|
||||
|
||||
usb2->internal = of_property_read_bool(np, "nvidia,internal");
|
||||
|
||||
if (!of_property_read_string(np, "mode", &mode)) {
|
||||
int err = match_string(modes, ARRAY_SIZE(modes), mode);
|
||||
if (err < 0) {
|
||||
dev_err(&port->dev, "invalid value %s for \"mode\"\n",
|
||||
mode);
|
||||
usb2->mode = USB_DR_MODE_UNKNOWN;
|
||||
} else {
|
||||
usb2->mode = err;
|
||||
}
|
||||
} else {
|
||||
usb2->mode = USB_DR_MODE_HOST;
|
||||
}
|
||||
|
||||
usb2->supply = devm_regulator_get(&port->dev, "vbus");
|
||||
return PTR_ERR_OR_ZERO(usb2->supply);
|
||||
}
|
||||
@ -839,6 +870,7 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
||||
struct tegra_xusb_padctl *padctl;
|
||||
const struct of_device_id *match;
|
||||
struct resource *res;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
/* for backwards compatibility with old device trees */
|
||||
@ -876,14 +908,38 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
||||
goto remove;
|
||||
}
|
||||
|
||||
padctl->supplies = devm_kcalloc(&pdev->dev, padctl->soc->num_supplies,
|
||||
sizeof(*padctl->supplies), GFP_KERNEL);
|
||||
if (!padctl->supplies) {
|
||||
err = -ENOMEM;
|
||||
goto remove;
|
||||
}
|
||||
|
||||
for (i = 0; i < padctl->soc->num_supplies; i++)
|
||||
padctl->supplies[i].supply = padctl->soc->supply_names[i];
|
||||
|
||||
err = devm_regulator_bulk_get(&pdev->dev, padctl->soc->num_supplies,
|
||||
padctl->supplies);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
|
||||
goto remove;
|
||||
}
|
||||
|
||||
err = reset_control_deassert(padctl->rst);
|
||||
if (err < 0)
|
||||
goto remove;
|
||||
|
||||
err = regulator_bulk_enable(padctl->soc->num_supplies,
|
||||
padctl->supplies);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable supplies: %d\n", err);
|
||||
goto reset;
|
||||
}
|
||||
|
||||
err = tegra_xusb_setup_pads(padctl);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to setup pads: %d\n", err);
|
||||
goto reset;
|
||||
goto power_down;
|
||||
}
|
||||
|
||||
err = tegra_xusb_setup_ports(padctl);
|
||||
@ -896,6 +952,8 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
||||
|
||||
remove_pads:
|
||||
tegra_xusb_remove_pads(padctl);
|
||||
power_down:
|
||||
regulator_bulk_disable(padctl->soc->num_supplies, padctl->supplies);
|
||||
reset:
|
||||
reset_control_assert(padctl->rst);
|
||||
remove:
|
||||
@ -911,6 +969,11 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
|
||||
tegra_xusb_remove_ports(padctl);
|
||||
tegra_xusb_remove_pads(padctl);
|
||||
|
||||
err = regulator_bulk_disable(padctl->soc->num_supplies,
|
||||
padctl->supplies);
|
||||
if (err < 0)
|
||||
dev_err(&pdev->dev, "failed to disable supplies: %d\n", err);
|
||||
|
||||
err = reset_control_assert(padctl->rst);
|
||||
if (err < 0)
|
||||
dev_err(&pdev->dev, "failed to assert reset: %d\n", err);
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
/* legacy entry points for backwards-compatibility */
|
||||
int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
|
||||
int tegra_xusb_padctl_legacy_remove(struct platform_device *pdev);
|
||||
@ -54,10 +56,21 @@ struct tegra_xusb_lane {
|
||||
int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
|
||||
struct device_node *np);
|
||||
|
||||
struct tegra_xusb_usb3_lane {
|
||||
struct tegra_xusb_lane base;
|
||||
};
|
||||
|
||||
static inline struct tegra_xusb_usb3_lane *
|
||||
to_usb3_lane(struct tegra_xusb_lane *lane)
|
||||
{
|
||||
return container_of(lane, struct tegra_xusb_usb3_lane, base);
|
||||
}
|
||||
|
||||
struct tegra_xusb_usb2_lane {
|
||||
struct tegra_xusb_lane base;
|
||||
|
||||
u32 hs_curr_level_offset;
|
||||
bool powered_on;
|
||||
};
|
||||
|
||||
static inline struct tegra_xusb_usb2_lane *
|
||||
@ -168,6 +181,19 @@ int tegra_xusb_pad_register(struct tegra_xusb_pad *pad,
|
||||
const struct phy_ops *ops);
|
||||
void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad);
|
||||
|
||||
struct tegra_xusb_usb3_pad {
|
||||
struct tegra_xusb_pad base;
|
||||
|
||||
unsigned int enable;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static inline struct tegra_xusb_usb3_pad *
|
||||
to_usb3_pad(struct tegra_xusb_pad *pad)
|
||||
{
|
||||
return container_of(pad, struct tegra_xusb_usb3_pad, base);
|
||||
}
|
||||
|
||||
struct tegra_xusb_usb2_pad {
|
||||
struct tegra_xusb_pad base;
|
||||
|
||||
@ -271,6 +297,7 @@ struct tegra_xusb_usb2_port {
|
||||
struct tegra_xusb_port base;
|
||||
|
||||
struct regulator *supply;
|
||||
enum usb_dr_mode mode;
|
||||
bool internal;
|
||||
};
|
||||
|
||||
@ -367,6 +394,9 @@ struct tegra_xusb_padctl_soc {
|
||||
} ports;
|
||||
|
||||
const struct tegra_xusb_padctl_ops *ops;
|
||||
|
||||
const char * const *supply_names;
|
||||
unsigned int num_supplies;
|
||||
};
|
||||
|
||||
struct tegra_xusb_padctl {
|
||||
@ -390,6 +420,8 @@ struct tegra_xusb_padctl {
|
||||
unsigned int enable;
|
||||
|
||||
struct clk *clk;
|
||||
|
||||
struct regulator_bulk_data *supplies;
|
||||
};
|
||||
|
||||
static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
|
||||
@ -417,5 +449,8 @@ extern const struct tegra_xusb_padctl_soc tegra124_xusb_padctl_soc;
|
||||
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
|
||||
extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc;
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
|
||||
extern const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc;
|
||||
#endif
|
||||
|
||||
#endif /* __PHY_TEGRA_XUSB_H */
|
||||
|
@ -20,6 +20,18 @@ config PHY_DM816X_USB
|
||||
help
|
||||
Enable this for dm816x USB to work.
|
||||
|
||||
config PHY_AM654_SERDES
|
||||
tristate "TI AM654 SERDES support"
|
||||
depends on OF && ARCH_K3 || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
select GENERIC_PHY
|
||||
select MULTIPLEXER
|
||||
select REGMAP_MMIO
|
||||
select MUX_MMIO
|
||||
help
|
||||
This option enables support for TI AM654 SerDes PHY used for
|
||||
PCIe.
|
||||
|
||||
config OMAP_CONTROL_PHY
|
||||
tristate "OMAP CONTROL PHY Driver"
|
||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
||||
|
@ -6,4 +6,5 @@ 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_AM654_SERDES) += phy-am654-serdes.o
|
||||
obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o
|
||||
|
658
drivers/phy/ti/phy-am654-serdes.c
Normal file
658
drivers/phy/ti/phy-am654-serdes.c
Normal file
@ -0,0 +1,658 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/**
|
||||
* PCIe SERDES driver for AM654x SoC
|
||||
*
|
||||
* Copyright (C) 2018 - 2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Author: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
*/
|
||||
|
||||
#include <dt-bindings/phy/phy.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mux/consumer.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define CMU_R07C 0x7c
|
||||
|
||||
#define COMLANE_R138 0xb38
|
||||
#define VERSION 0x70
|
||||
|
||||
#define COMLANE_R190 0xb90
|
||||
|
||||
#define COMLANE_R194 0xb94
|
||||
|
||||
#define SERDES_CTRL 0x1fd0
|
||||
|
||||
#define WIZ_LANEXCTL_STS 0x1fe0
|
||||
#define TX0_DISABLE_STATE 0x4
|
||||
#define TX0_SLEEP_STATE 0x5
|
||||
#define TX0_SNOOZE_STATE 0x6
|
||||
#define TX0_ENABLE_STATE 0x7
|
||||
|
||||
#define RX0_DISABLE_STATE 0x4
|
||||
#define RX0_SLEEP_STATE 0x5
|
||||
#define RX0_SNOOZE_STATE 0x6
|
||||
#define RX0_ENABLE_STATE 0x7
|
||||
|
||||
#define WIZ_PLL_CTRL 0x1ff4
|
||||
#define PLL_DISABLE_STATE 0x4
|
||||
#define PLL_SLEEP_STATE 0x5
|
||||
#define PLL_SNOOZE_STATE 0x6
|
||||
#define PLL_ENABLE_STATE 0x7
|
||||
|
||||
#define PLL_LOCK_TIME 100000 /* in microseconds */
|
||||
#define SLEEP_TIME 100 /* in microseconds */
|
||||
|
||||
#define LANE_USB3 0x0
|
||||
#define LANE_PCIE0_LANE0 0x1
|
||||
|
||||
#define LANE_PCIE1_LANE0 0x0
|
||||
#define LANE_PCIE0_LANE1 0x1
|
||||
|
||||
#define SERDES_NUM_CLOCKS 3
|
||||
|
||||
#define AM654_SERDES_CTRL_CLKSEL_MASK GENMASK(7, 4)
|
||||
#define AM654_SERDES_CTRL_CLKSEL_SHIFT 4
|
||||
|
||||
struct serdes_am654_clk_mux {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
unsigned int reg;
|
||||
int clk_id;
|
||||
struct clk_init_data clk_data;
|
||||
};
|
||||
|
||||
#define to_serdes_am654_clk_mux(_hw) \
|
||||
container_of(_hw, struct serdes_am654_clk_mux, hw)
|
||||
|
||||
static struct regmap_config serdes_am654_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct reg_field cmu_master_cdn_o = REG_FIELD(CMU_R07C, 24, 24);
|
||||
static const struct reg_field config_version = REG_FIELD(COMLANE_R138, 16, 23);
|
||||
static const struct reg_field l1_master_cdn_o = REG_FIELD(COMLANE_R190, 9, 9);
|
||||
static const struct reg_field cmu_ok_i_0 = REG_FIELD(COMLANE_R194, 19, 19);
|
||||
static const struct reg_field por_en = REG_FIELD(SERDES_CTRL, 29, 29);
|
||||
static const struct reg_field tx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 29, 31);
|
||||
static const struct reg_field rx0_enable = REG_FIELD(WIZ_LANEXCTL_STS, 13, 15);
|
||||
static const struct reg_field pll_enable = REG_FIELD(WIZ_PLL_CTRL, 29, 31);
|
||||
static const struct reg_field pll_ok = REG_FIELD(WIZ_PLL_CTRL, 28, 28);
|
||||
|
||||
struct serdes_am654 {
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *cmu_master_cdn_o;
|
||||
struct regmap_field *config_version;
|
||||
struct regmap_field *l1_master_cdn_o;
|
||||
struct regmap_field *cmu_ok_i_0;
|
||||
struct regmap_field *por_en;
|
||||
struct regmap_field *tx0_enable;
|
||||
struct regmap_field *rx0_enable;
|
||||
struct regmap_field *pll_enable;
|
||||
struct regmap_field *pll_ok;
|
||||
|
||||
struct device *dev;
|
||||
struct mux_control *control;
|
||||
bool busy;
|
||||
u32 type;
|
||||
struct device_node *of_node;
|
||||
struct clk_onecell_data clk_data;
|
||||
struct clk *clks[SERDES_NUM_CLOCKS];
|
||||
};
|
||||
|
||||
static int serdes_am654_enable_pll(struct serdes_am654 *phy)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = regmap_field_write(phy->pll_enable, PLL_ENABLE_STATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_field_read_poll_timeout(phy->pll_ok, val, val, 1000,
|
||||
PLL_LOCK_TIME);
|
||||
}
|
||||
|
||||
static void serdes_am654_disable_pll(struct serdes_am654 *phy)
|
||||
{
|
||||
struct device *dev = phy->dev;
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_write(phy->pll_enable, PLL_DISABLE_STATE);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to disable PLL\n");
|
||||
}
|
||||
|
||||
static int serdes_am654_enable_txrx(struct serdes_am654 *phy)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Enable TX */
|
||||
ret = regmap_field_write(phy->tx0_enable, TX0_ENABLE_STATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable RX */
|
||||
ret = regmap_field_write(phy->rx0_enable, RX0_ENABLE_STATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_am654_disable_txrx(struct serdes_am654 *phy)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Disable TX */
|
||||
ret = regmap_field_write(phy->tx0_enable, TX0_DISABLE_STATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable RX */
|
||||
ret = regmap_field_write(phy->rx0_enable, RX0_DISABLE_STATE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_am654_power_on(struct phy *x)
|
||||
{
|
||||
struct serdes_am654 *phy = phy_get_drvdata(x);
|
||||
struct device *dev = phy->dev;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = serdes_am654_enable_pll(phy);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable PLL\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = serdes_am654_enable_txrx(phy);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable TX RX\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return regmap_field_read_poll_timeout(phy->cmu_ok_i_0, val, val,
|
||||
SLEEP_TIME, PLL_LOCK_TIME);
|
||||
}
|
||||
|
||||
static int serdes_am654_power_off(struct phy *x)
|
||||
{
|
||||
struct serdes_am654 *phy = phy_get_drvdata(x);
|
||||
|
||||
serdes_am654_disable_txrx(phy);
|
||||
serdes_am654_disable_pll(phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_am654_init(struct phy *x)
|
||||
{
|
||||
struct serdes_am654 *phy = phy_get_drvdata(x);
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_write(phy->config_version, VERSION);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_field_write(phy->cmu_master_cdn_o, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_field_write(phy->l1_master_cdn_o, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_am654_reset(struct phy *x)
|
||||
{
|
||||
struct serdes_am654 *phy = phy_get_drvdata(x);
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_write(phy->por_en, 0x1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mdelay(1);
|
||||
|
||||
ret = regmap_field_write(phy->por_en, 0x0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void serdes_am654_release(struct phy *x)
|
||||
{
|
||||
struct serdes_am654 *phy = phy_get_drvdata(x);
|
||||
|
||||
phy->type = PHY_NONE;
|
||||
phy->busy = false;
|
||||
mux_control_deselect(phy->control);
|
||||
}
|
||||
|
||||
struct phy *serdes_am654_xlate(struct device *dev, struct of_phandle_args
|
||||
*args)
|
||||
{
|
||||
struct serdes_am654 *am654_phy;
|
||||
struct phy *phy;
|
||||
int ret;
|
||||
|
||||
phy = of_phy_simple_xlate(dev, args);
|
||||
if (IS_ERR(phy))
|
||||
return phy;
|
||||
|
||||
am654_phy = phy_get_drvdata(phy);
|
||||
if (am654_phy->busy)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
ret = mux_control_select(am654_phy->control, args->args[1]);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to select SERDES Lane Function\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
am654_phy->busy = true;
|
||||
am654_phy->type = args->args[0];
|
||||
|
||||
return phy;
|
||||
}
|
||||
|
||||
static const struct phy_ops ops = {
|
||||
.reset = serdes_am654_reset,
|
||||
.init = serdes_am654_init,
|
||||
.power_on = serdes_am654_power_on,
|
||||
.power_off = serdes_am654_power_off,
|
||||
.release = serdes_am654_release,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define SERDES_NUM_MUX_COMBINATIONS 16
|
||||
|
||||
#define LICLK 0
|
||||
#define EXT_REFCLK 1
|
||||
#define RICLK 2
|
||||
|
||||
static const int
|
||||
serdes_am654_mux_table[SERDES_NUM_MUX_COMBINATIONS][SERDES_NUM_CLOCKS] = {
|
||||
/*
|
||||
* Each combination maps to one of
|
||||
* "Figure 12-1986. SerDes Reference Clock Distribution"
|
||||
* in TRM.
|
||||
*/
|
||||
/* Parent of CMU refclk, Left output, Right output
|
||||
* either of EXT_REFCLK, LICLK, RICLK
|
||||
*/
|
||||
{ EXT_REFCLK, EXT_REFCLK, EXT_REFCLK }, /* 0000 */
|
||||
{ RICLK, EXT_REFCLK, EXT_REFCLK }, /* 0001 */
|
||||
{ EXT_REFCLK, RICLK, LICLK }, /* 0010 */
|
||||
{ RICLK, RICLK, EXT_REFCLK }, /* 0011 */
|
||||
{ LICLK, EXT_REFCLK, EXT_REFCLK }, /* 0100 */
|
||||
{ EXT_REFCLK, EXT_REFCLK, EXT_REFCLK }, /* 0101 */
|
||||
{ LICLK, RICLK, LICLK }, /* 0110 */
|
||||
{ EXT_REFCLK, RICLK, LICLK }, /* 0111 */
|
||||
{ EXT_REFCLK, EXT_REFCLK, LICLK }, /* 1000 */
|
||||
{ RICLK, EXT_REFCLK, LICLK }, /* 1001 */
|
||||
{ EXT_REFCLK, RICLK, EXT_REFCLK }, /* 1010 */
|
||||
{ RICLK, RICLK, EXT_REFCLK }, /* 1011 */
|
||||
{ LICLK, EXT_REFCLK, LICLK }, /* 1100 */
|
||||
{ EXT_REFCLK, EXT_REFCLK, LICLK }, /* 1101 */
|
||||
{ LICLK, RICLK, EXT_REFCLK }, /* 1110 */
|
||||
{ EXT_REFCLK, RICLK, EXT_REFCLK }, /* 1111 */
|
||||
};
|
||||
|
||||
static u8 serdes_am654_clk_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw);
|
||||
struct regmap *regmap = mux->regmap;
|
||||
unsigned int reg = mux->reg;
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(regmap, reg, &val);
|
||||
val &= AM654_SERDES_CTRL_CLKSEL_MASK;
|
||||
val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT;
|
||||
|
||||
return serdes_am654_mux_table[val][mux->clk_id];
|
||||
}
|
||||
|
||||
static int serdes_am654_clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct serdes_am654_clk_mux *mux = to_serdes_am654_clk_mux(hw);
|
||||
struct regmap *regmap = mux->regmap;
|
||||
unsigned int reg = mux->reg;
|
||||
int clk_id = mux->clk_id;
|
||||
int parents[SERDES_NUM_CLOCKS];
|
||||
const int *p;
|
||||
u32 val;
|
||||
int found, i;
|
||||
int ret;
|
||||
|
||||
/* get existing setting */
|
||||
regmap_read(regmap, reg, &val);
|
||||
val &= AM654_SERDES_CTRL_CLKSEL_MASK;
|
||||
val >>= AM654_SERDES_CTRL_CLKSEL_SHIFT;
|
||||
|
||||
for (i = 0; i < SERDES_NUM_CLOCKS; i++)
|
||||
parents[i] = serdes_am654_mux_table[val][i];
|
||||
|
||||
/* change parent of this clock. others left intact */
|
||||
parents[clk_id] = index;
|
||||
|
||||
/* Find the match */
|
||||
for (val = 0; val < SERDES_NUM_MUX_COMBINATIONS; val++) {
|
||||
p = serdes_am654_mux_table[val];
|
||||
found = 1;
|
||||
for (i = 0; i < SERDES_NUM_CLOCKS; i++) {
|
||||
if (parents[i] != p[i]) {
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
/*
|
||||
* This can never happen, unless we missed
|
||||
* a valid combination in serdes_am654_mux_table.
|
||||
*/
|
||||
WARN(1, "Failed to find the parent of %s clock\n",
|
||||
hw->init->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val <<= AM654_SERDES_CTRL_CLKSEL_SHIFT;
|
||||
ret = regmap_update_bits(regmap, reg, AM654_SERDES_CTRL_CLKSEL_MASK,
|
||||
val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct clk_ops serdes_am654_clk_mux_ops = {
|
||||
.set_parent = serdes_am654_clk_mux_set_parent,
|
||||
.get_parent = serdes_am654_clk_mux_get_parent,
|
||||
};
|
||||
|
||||
static int serdes_am654_clk_register(struct serdes_am654 *am654_phy,
|
||||
const char *clock_name, int clock_num)
|
||||
{
|
||||
struct device_node *node = am654_phy->of_node;
|
||||
struct device *dev = am654_phy->dev;
|
||||
struct serdes_am654_clk_mux *mux;
|
||||
struct device_node *regmap_node;
|
||||
const char **parent_names;
|
||||
struct clk_init_data *init;
|
||||
unsigned int num_parents;
|
||||
struct regmap *regmap;
|
||||
const __be32 *addr;
|
||||
unsigned int reg;
|
||||
struct clk *clk;
|
||||
|
||||
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return -ENOMEM;
|
||||
|
||||
init = &mux->clk_data;
|
||||
|
||||
regmap_node = of_parse_phandle(node, "ti,serdes-clk", 0);
|
||||
of_node_put(regmap_node);
|
||||
if (!regmap_node) {
|
||||
dev_err(dev, "Fail to get serdes-clk node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
regmap = syscon_node_to_regmap(regmap_node->parent);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Fail to get Syscon regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
num_parents = of_clk_get_parent_count(node);
|
||||
if (num_parents < 2) {
|
||||
dev_err(dev, "SERDES clock must have parents\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents),
|
||||
GFP_KERNEL);
|
||||
if (!parent_names)
|
||||
return -ENOMEM;
|
||||
|
||||
of_clk_parent_fill(node, parent_names, num_parents);
|
||||
|
||||
addr = of_get_address(regmap_node, 0, NULL, NULL);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
||||
reg = be32_to_cpu(*addr);
|
||||
|
||||
init->ops = &serdes_am654_clk_mux_ops;
|
||||
init->flags = CLK_SET_RATE_NO_REPARENT;
|
||||
init->parent_names = parent_names;
|
||||
init->num_parents = num_parents;
|
||||
init->name = clock_name;
|
||||
|
||||
mux->regmap = regmap;
|
||||
mux->reg = reg;
|
||||
mux->clk_id = clock_num;
|
||||
mux->hw.init = init;
|
||||
|
||||
clk = devm_clk_register(dev, &mux->hw);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
am654_phy->clks[clock_num] = clk;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id serdes_am654_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,phy-am654-serdes",
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, serdes_am654_id_table);
|
||||
|
||||
static int serdes_am654_regfield_init(struct serdes_am654 *am654_phy)
|
||||
{
|
||||
struct regmap *regmap = am654_phy->regmap;
|
||||
struct device *dev = am654_phy->dev;
|
||||
|
||||
am654_phy->cmu_master_cdn_o = devm_regmap_field_alloc(dev, regmap,
|
||||
cmu_master_cdn_o);
|
||||
if (IS_ERR(am654_phy->cmu_master_cdn_o)) {
|
||||
dev_err(dev, "CMU_MASTER_CDN_O reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->cmu_master_cdn_o);
|
||||
}
|
||||
|
||||
am654_phy->config_version = devm_regmap_field_alloc(dev, regmap,
|
||||
config_version);
|
||||
if (IS_ERR(am654_phy->config_version)) {
|
||||
dev_err(dev, "CONFIG_VERSION reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->config_version);
|
||||
}
|
||||
|
||||
am654_phy->l1_master_cdn_o = devm_regmap_field_alloc(dev, regmap,
|
||||
l1_master_cdn_o);
|
||||
if (IS_ERR(am654_phy->l1_master_cdn_o)) {
|
||||
dev_err(dev, "L1_MASTER_CDN_O reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->l1_master_cdn_o);
|
||||
}
|
||||
|
||||
am654_phy->cmu_ok_i_0 = devm_regmap_field_alloc(dev, regmap,
|
||||
cmu_ok_i_0);
|
||||
if (IS_ERR(am654_phy->cmu_ok_i_0)) {
|
||||
dev_err(dev, "CMU_OK_I_0 reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->cmu_ok_i_0);
|
||||
}
|
||||
|
||||
am654_phy->por_en = devm_regmap_field_alloc(dev, regmap, por_en);
|
||||
if (IS_ERR(am654_phy->por_en)) {
|
||||
dev_err(dev, "POR_EN reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->por_en);
|
||||
}
|
||||
|
||||
am654_phy->tx0_enable = devm_regmap_field_alloc(dev, regmap,
|
||||
tx0_enable);
|
||||
if (IS_ERR(am654_phy->tx0_enable)) {
|
||||
dev_err(dev, "TX0_ENABLE reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->tx0_enable);
|
||||
}
|
||||
|
||||
am654_phy->rx0_enable = devm_regmap_field_alloc(dev, regmap,
|
||||
rx0_enable);
|
||||
if (IS_ERR(am654_phy->rx0_enable)) {
|
||||
dev_err(dev, "RX0_ENABLE reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->rx0_enable);
|
||||
}
|
||||
|
||||
am654_phy->pll_enable = devm_regmap_field_alloc(dev, regmap,
|
||||
pll_enable);
|
||||
if (IS_ERR(am654_phy->pll_enable)) {
|
||||
dev_err(dev, "PLL_ENABLE reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->pll_enable);
|
||||
}
|
||||
|
||||
am654_phy->pll_ok = devm_regmap_field_alloc(dev, regmap, pll_ok);
|
||||
if (IS_ERR(am654_phy->pll_ok)) {
|
||||
dev_err(dev, "PLL_OK reg field init failed\n");
|
||||
return PTR_ERR(am654_phy->pll_ok);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serdes_am654_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct clk_onecell_data *clk_data;
|
||||
struct serdes_am654 *am654_phy;
|
||||
struct mux_control *control;
|
||||
const char *clock_name;
|
||||
struct regmap *regmap;
|
||||
void __iomem *base;
|
||||
struct phy *phy;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
am654_phy = devm_kzalloc(dev, sizeof(*am654_phy), GFP_KERNEL);
|
||||
if (!am654_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, base, &serdes_am654_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to initialize regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
control = devm_mux_control_get(dev, NULL);
|
||||
if (IS_ERR(control))
|
||||
return PTR_ERR(control);
|
||||
|
||||
am654_phy->dev = dev;
|
||||
am654_phy->of_node = node;
|
||||
am654_phy->regmap = regmap;
|
||||
am654_phy->control = control;
|
||||
am654_phy->type = PHY_NONE;
|
||||
|
||||
ret = serdes_am654_regfield_init(am654_phy);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize regfields\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, am654_phy);
|
||||
|
||||
for (i = 0; i < SERDES_NUM_CLOCKS; i++) {
|
||||
ret = of_property_read_string_index(node, "clock-output-names",
|
||||
i, &clock_name);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get clock name\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = serdes_am654_clk_register(am654_phy, clock_name, i);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize clock %s\n",
|
||||
clock_name);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
clk_data = &am654_phy->clk_data;
|
||||
clk_data->clks = am654_phy->clks;
|
||||
clk_data->clk_num = SERDES_NUM_CLOCKS;
|
||||
ret = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
phy = devm_phy_create(dev, NULL, &ops);
|
||||
if (IS_ERR(phy))
|
||||
return PTR_ERR(phy);
|
||||
|
||||
phy_set_drvdata(phy, am654_phy);
|
||||
phy_provider = devm_of_phy_provider_register(dev, serdes_am654_xlate);
|
||||
if (IS_ERR(phy_provider)) {
|
||||
ret = PTR_ERR(phy_provider);
|
||||
goto clk_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
clk_err:
|
||||
of_clk_del_provider(node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int serdes_am654_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct serdes_am654 *am654_phy = platform_get_drvdata(pdev);
|
||||
struct device_node *node = am654_phy->of_node;
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
of_clk_del_provider(node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver serdes_am654_driver = {
|
||||
.probe = serdes_am654_probe,
|
||||
.remove = serdes_am654_remove,
|
||||
.driver = {
|
||||
.name = "phy-am654",
|
||||
.of_match_table = serdes_am654_id_table,
|
||||
},
|
||||
};
|
||||
module_platform_driver(serdes_am654_driver);
|
||||
|
||||
MODULE_AUTHOR("Texas Instruments Inc.");
|
||||
MODULE_DESCRIPTION("TI AM654x SERDES driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -56,51 +56,73 @@
|
||||
|
||||
#define SATA_PLL_SOFT_RESET BIT(18)
|
||||
|
||||
#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000
|
||||
#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK GENMASK(21, 14)
|
||||
#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14
|
||||
|
||||
#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000
|
||||
#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK GENMASK(31, 22)
|
||||
#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22
|
||||
|
||||
#define PIPE3_PHY_TX_RX_POWERON 0x3
|
||||
#define PIPE3_PHY_TX_RX_POWEROFF 0x0
|
||||
#define PIPE3_PHY_RX_POWERON (0x1 << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT)
|
||||
#define PIPE3_PHY_TX_POWERON (0x2 << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT)
|
||||
|
||||
#define PCIE_PCS_MASK 0xFF0000
|
||||
#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10
|
||||
|
||||
#define PCIEPHYRX_ANA_PROGRAMMABILITY 0x0000000C
|
||||
#define PIPE3_PHY_RX_ANA_PROGRAMMABILITY 0x0000000C
|
||||
#define INTERFACE_MASK GENMASK(31, 27)
|
||||
#define INTERFACE_SHIFT 27
|
||||
#define INTERFACE_MODE_USBSS BIT(4)
|
||||
#define INTERFACE_MODE_SATA_1P5 BIT(3)
|
||||
#define INTERFACE_MODE_SATA_3P0 BIT(2)
|
||||
#define INTERFACE_MODE_PCIE BIT(0)
|
||||
|
||||
#define LOSD_MASK GENMASK(17, 14)
|
||||
#define LOSD_SHIFT 14
|
||||
#define MEM_PLLDIV GENMASK(6, 5)
|
||||
|
||||
#define PCIEPHYRX_TRIM 0x0000001C
|
||||
#define MEM_DLL_TRIM_SEL GENMASK(31, 30)
|
||||
#define PIPE3_PHY_RX_TRIM 0x0000001C
|
||||
#define MEM_DLL_TRIM_SEL_MASK GENMASK(31, 30)
|
||||
#define MEM_DLL_TRIM_SHIFT 30
|
||||
|
||||
#define PCIEPHYRX_DLL 0x00000024
|
||||
#define MEM_DLL_PHINT_RATE GENMASK(31, 30)
|
||||
#define PIPE3_PHY_RX_DLL 0x00000024
|
||||
#define MEM_DLL_PHINT_RATE_MASK GENMASK(31, 30)
|
||||
#define MEM_DLL_PHINT_RATE_SHIFT 30
|
||||
|
||||
#define PCIEPHYRX_DIGITAL_MODES 0x00000028
|
||||
#define PIPE3_PHY_RX_DIGITAL_MODES 0x00000028
|
||||
#define MEM_HS_RATE_MASK GENMASK(28, 27)
|
||||
#define MEM_HS_RATE_SHIFT 27
|
||||
#define MEM_OVRD_HS_RATE BIT(26)
|
||||
#define MEM_OVRD_HS_RATE_SHIFT 26
|
||||
#define MEM_CDR_FASTLOCK BIT(23)
|
||||
#define MEM_CDR_LBW GENMASK(22, 21)
|
||||
#define MEM_CDR_STEPCNT GENMASK(20, 19)
|
||||
#define MEM_CDR_FASTLOCK_SHIFT 23
|
||||
#define MEM_CDR_LBW_MASK GENMASK(22, 21)
|
||||
#define MEM_CDR_LBW_SHIFT 21
|
||||
#define MEM_CDR_STEPCNT_MASK GENMASK(20, 19)
|
||||
#define MEM_CDR_STEPCNT_SHIFT 19
|
||||
#define MEM_CDR_STL_MASK GENMASK(18, 16)
|
||||
#define MEM_CDR_STL_SHIFT 16
|
||||
#define MEM_CDR_THR_MASK GENMASK(15, 13)
|
||||
#define MEM_CDR_THR_SHIFT 13
|
||||
#define MEM_CDR_THR_MODE BIT(12)
|
||||
#define MEM_CDR_CDR_2NDO_SDM_MODE BIT(11)
|
||||
#define MEM_OVRD_HS_RATE BIT(26)
|
||||
#define MEM_CDR_THR_MODE_SHIFT 12
|
||||
#define MEM_CDR_2NDO_SDM_MODE BIT(11)
|
||||
#define MEM_CDR_2NDO_SDM_MODE_SHIFT 11
|
||||
|
||||
#define PCIEPHYRX_EQUALIZER 0x00000038
|
||||
#define MEM_EQLEV GENMASK(31, 16)
|
||||
#define MEM_EQFTC GENMASK(15, 11)
|
||||
#define MEM_EQCTL GENMASK(10, 7)
|
||||
#define PIPE3_PHY_RX_EQUALIZER 0x00000038
|
||||
#define MEM_EQLEV_MASK GENMASK(31, 16)
|
||||
#define MEM_EQLEV_SHIFT 16
|
||||
#define MEM_EQFTC_MASK GENMASK(15, 11)
|
||||
#define MEM_EQFTC_SHIFT 11
|
||||
#define MEM_EQCTL_MASK GENMASK(10, 7)
|
||||
#define MEM_EQCTL_SHIFT 7
|
||||
#define MEM_OVRD_EQLEV BIT(2)
|
||||
#define MEM_OVRD_EQLEV_SHIFT 2
|
||||
#define MEM_OVRD_EQFTC BIT(1)
|
||||
#define MEM_OVRD_EQFTC_SHIFT 1
|
||||
|
||||
#define SATA_PHY_RX_IO_AND_A2D_OVERRIDES 0x44
|
||||
#define MEM_CDR_LOS_SOURCE_MASK GENMASK(10, 9)
|
||||
#define MEM_CDR_LOS_SOURCE_SHIFT 9
|
||||
|
||||
/*
|
||||
* This is an Empirical value that works, need to confirm the actual
|
||||
@ -110,6 +132,10 @@
|
||||
#define PLL_IDLE_TIME 100 /* in milliseconds */
|
||||
#define PLL_LOCK_TIME 100 /* in milliseconds */
|
||||
|
||||
enum pipe3_mode { PIPE3_MODE_PCIE = 1,
|
||||
PIPE3_MODE_SATA,
|
||||
PIPE3_MODE_USBSS };
|
||||
|
||||
struct pipe3_dpll_params {
|
||||
u16 m;
|
||||
u8 n;
|
||||
@ -123,6 +149,27 @@ struct pipe3_dpll_map {
|
||||
struct pipe3_dpll_params params;
|
||||
};
|
||||
|
||||
struct pipe3_settings {
|
||||
u8 ana_interface;
|
||||
u8 ana_losd;
|
||||
u8 dig_fastlock;
|
||||
u8 dig_lbw;
|
||||
u8 dig_stepcnt;
|
||||
u8 dig_stl;
|
||||
u8 dig_thr;
|
||||
u8 dig_thr_mode;
|
||||
u8 dig_2ndo_sdm_mode;
|
||||
u8 dig_hs_rate;
|
||||
u8 dig_ovrd_hs_rate;
|
||||
u8 dll_trim_sel;
|
||||
u8 dll_phint_rate;
|
||||
u8 eq_lev;
|
||||
u8 eq_ftc;
|
||||
u8 eq_ctl;
|
||||
u8 eq_ovrd_lev;
|
||||
u8 eq_ovrd_ftc;
|
||||
};
|
||||
|
||||
struct ti_pipe3 {
|
||||
void __iomem *pll_ctrl_base;
|
||||
void __iomem *phy_rx;
|
||||
@ -141,6 +188,8 @@ struct ti_pipe3 {
|
||||
unsigned int power_reg; /* power reg. index within syscon */
|
||||
unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */
|
||||
bool sata_refclk_enabled;
|
||||
enum pipe3_mode mode;
|
||||
struct pipe3_settings settings;
|
||||
};
|
||||
|
||||
static struct pipe3_dpll_map dpll_map_usb[] = {
|
||||
@ -163,6 +212,89 @@ static struct pipe3_dpll_map dpll_map_sata[] = {
|
||||
{ }, /* Terminator */
|
||||
};
|
||||
|
||||
struct pipe3_data {
|
||||
enum pipe3_mode mode;
|
||||
struct pipe3_dpll_map *dpll_map;
|
||||
struct pipe3_settings settings;
|
||||
};
|
||||
|
||||
static struct pipe3_data data_usb = {
|
||||
.mode = PIPE3_MODE_USBSS,
|
||||
.dpll_map = dpll_map_usb,
|
||||
.settings = {
|
||||
/* DRA75x TRM Table 26-17 Preferred USB3_PHY_RX SCP Register Settings */
|
||||
.ana_interface = INTERFACE_MODE_USBSS,
|
||||
.ana_losd = 0xa,
|
||||
.dig_fastlock = 1,
|
||||
.dig_lbw = 3,
|
||||
.dig_stepcnt = 0,
|
||||
.dig_stl = 0x3,
|
||||
.dig_thr = 1,
|
||||
.dig_thr_mode = 1,
|
||||
.dig_2ndo_sdm_mode = 0,
|
||||
.dig_hs_rate = 0,
|
||||
.dig_ovrd_hs_rate = 1,
|
||||
.dll_trim_sel = 0x2,
|
||||
.dll_phint_rate = 0x3,
|
||||
.eq_lev = 0,
|
||||
.eq_ftc = 0,
|
||||
.eq_ctl = 0x9,
|
||||
.eq_ovrd_lev = 0,
|
||||
.eq_ovrd_ftc = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static struct pipe3_data data_sata = {
|
||||
.mode = PIPE3_MODE_SATA,
|
||||
.dpll_map = dpll_map_sata,
|
||||
.settings = {
|
||||
/* DRA75x TRM Table 26-9 Preferred SATA_PHY_RX SCP Register Settings */
|
||||
.ana_interface = INTERFACE_MODE_SATA_3P0,
|
||||
.ana_losd = 0x5,
|
||||
.dig_fastlock = 1,
|
||||
.dig_lbw = 3,
|
||||
.dig_stepcnt = 0,
|
||||
.dig_stl = 0x3,
|
||||
.dig_thr = 1,
|
||||
.dig_thr_mode = 1,
|
||||
.dig_2ndo_sdm_mode = 0,
|
||||
.dig_hs_rate = 0, /* Not in TRM preferred settings */
|
||||
.dig_ovrd_hs_rate = 0, /* Not in TRM preferred settings */
|
||||
.dll_trim_sel = 0x1,
|
||||
.dll_phint_rate = 0x2, /* for 1.5 GHz DPLL clock */
|
||||
.eq_lev = 0,
|
||||
.eq_ftc = 0x1f,
|
||||
.eq_ctl = 0,
|
||||
.eq_ovrd_lev = 1,
|
||||
.eq_ovrd_ftc = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static struct pipe3_data data_pcie = {
|
||||
.mode = PIPE3_MODE_PCIE,
|
||||
.settings = {
|
||||
/* DRA75x TRM Table 26-62 Preferred PCIe_PHY_RX SCP Register Settings */
|
||||
.ana_interface = INTERFACE_MODE_PCIE,
|
||||
.ana_losd = 0xa,
|
||||
.dig_fastlock = 1,
|
||||
.dig_lbw = 3,
|
||||
.dig_stepcnt = 0,
|
||||
.dig_stl = 0x3,
|
||||
.dig_thr = 1,
|
||||
.dig_thr_mode = 1,
|
||||
.dig_2ndo_sdm_mode = 0,
|
||||
.dig_hs_rate = 0,
|
||||
.dig_ovrd_hs_rate = 0,
|
||||
.dll_trim_sel = 0x2,
|
||||
.dll_phint_rate = 0x3,
|
||||
.eq_lev = 0,
|
||||
.eq_ftc = 0x1f,
|
||||
.eq_ctl = 1,
|
||||
.eq_ovrd_lev = 0,
|
||||
.eq_ovrd_ftc = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset)
|
||||
{
|
||||
return __raw_readl(addr + offset);
|
||||
@ -196,7 +328,6 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy);
|
||||
|
||||
static int ti_pipe3_power_off(struct phy *x)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
|
||||
@ -205,13 +336,13 @@ static int ti_pipe3_power_off(struct phy *x)
|
||||
return 0;
|
||||
}
|
||||
|
||||
val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
||||
|
||||
ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
|
||||
PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val);
|
||||
PIPE3_PHY_PWRCTL_CLK_CMD_MASK, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ti_pipe3_calibrate(struct ti_pipe3 *phy);
|
||||
|
||||
static int ti_pipe3_power_on(struct phy *x)
|
||||
{
|
||||
u32 val;
|
||||
@ -219,6 +350,7 @@ static int ti_pipe3_power_on(struct phy *x)
|
||||
int ret;
|
||||
unsigned long rate;
|
||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||
bool rx_pending = false;
|
||||
|
||||
if (!phy->phy_power_syscon) {
|
||||
omap_control_phy_power(phy->control_dev, 1);
|
||||
@ -231,14 +363,35 @@ static int ti_pipe3_power_on(struct phy *x)
|
||||
return -EINVAL;
|
||||
}
|
||||
rate = rate / 1000000;
|
||||
mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
|
||||
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK;
|
||||
val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
||||
val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
|
||||
|
||||
mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK;
|
||||
val = rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
|
||||
ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
|
||||
mask, val);
|
||||
return ret;
|
||||
/*
|
||||
* For PCIe, TX and RX must be powered on simultaneously.
|
||||
* For USB and SATA, TX must be powered on before RX
|
||||
*/
|
||||
mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
|
||||
if (phy->mode == PIPE3_MODE_SATA || phy->mode == PIPE3_MODE_USBSS) {
|
||||
val = PIPE3_PHY_TX_POWERON;
|
||||
rx_pending = true;
|
||||
} else {
|
||||
val = PIPE3_PHY_TX_POWERON | PIPE3_PHY_RX_POWERON;
|
||||
}
|
||||
|
||||
regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
|
||||
mask, val);
|
||||
|
||||
if (rx_pending) {
|
||||
val = PIPE3_PHY_TX_POWERON | PIPE3_PHY_RX_POWERON;
|
||||
regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
|
||||
mask, val);
|
||||
}
|
||||
|
||||
if (phy->mode == PIPE3_MODE_PCIE)
|
||||
ti_pipe3_calibrate(phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy)
|
||||
@ -300,32 +453,55 @@ static int ti_pipe3_dpll_program(struct ti_pipe3 *phy)
|
||||
static void ti_pipe3_calibrate(struct ti_pipe3 *phy)
|
||||
{
|
||||
u32 val;
|
||||
struct pipe3_settings *s = &phy->settings;
|
||||
|
||||
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY);
|
||||
val = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_ANA_PROGRAMMABILITY);
|
||||
val &= ~(INTERFACE_MASK | LOSD_MASK | MEM_PLLDIV);
|
||||
val = (0x1 << INTERFACE_SHIFT | 0xA << LOSD_SHIFT);
|
||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY, val);
|
||||
val |= (s->ana_interface << INTERFACE_SHIFT | s->ana_losd << LOSD_SHIFT);
|
||||
ti_pipe3_writel(phy->phy_rx, PIPE3_PHY_RX_ANA_PROGRAMMABILITY, val);
|
||||
|
||||
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES);
|
||||
val &= ~(MEM_CDR_STEPCNT | MEM_CDR_STL_MASK | MEM_CDR_THR_MASK |
|
||||
MEM_CDR_CDR_2NDO_SDM_MODE | MEM_OVRD_HS_RATE);
|
||||
val |= (MEM_CDR_FASTLOCK | MEM_CDR_LBW | 0x3 << MEM_CDR_STL_SHIFT |
|
||||
0x1 << MEM_CDR_THR_SHIFT | MEM_CDR_THR_MODE);
|
||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES, val);
|
||||
val = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_DIGITAL_MODES);
|
||||
val &= ~(MEM_HS_RATE_MASK | MEM_OVRD_HS_RATE | MEM_CDR_FASTLOCK |
|
||||
MEM_CDR_LBW_MASK | MEM_CDR_STEPCNT_MASK | MEM_CDR_STL_MASK |
|
||||
MEM_CDR_THR_MASK | MEM_CDR_THR_MODE | MEM_CDR_2NDO_SDM_MODE);
|
||||
val |= s->dig_hs_rate << MEM_HS_RATE_SHIFT |
|
||||
s->dig_ovrd_hs_rate << MEM_OVRD_HS_RATE_SHIFT |
|
||||
s->dig_fastlock << MEM_CDR_FASTLOCK_SHIFT |
|
||||
s->dig_lbw << MEM_CDR_LBW_SHIFT |
|
||||
s->dig_stepcnt << MEM_CDR_STEPCNT_SHIFT |
|
||||
s->dig_stl << MEM_CDR_STL_SHIFT |
|
||||
s->dig_thr << MEM_CDR_THR_SHIFT |
|
||||
s->dig_thr_mode << MEM_CDR_THR_MODE_SHIFT |
|
||||
s->dig_2ndo_sdm_mode << MEM_CDR_2NDO_SDM_MODE_SHIFT;
|
||||
ti_pipe3_writel(phy->phy_rx, PIPE3_PHY_RX_DIGITAL_MODES, val);
|
||||
|
||||
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_TRIM);
|
||||
val &= ~MEM_DLL_TRIM_SEL;
|
||||
val |= 0x2 << MEM_DLL_TRIM_SHIFT;
|
||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_TRIM, val);
|
||||
val = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_TRIM);
|
||||
val &= ~MEM_DLL_TRIM_SEL_MASK;
|
||||
val |= s->dll_trim_sel << MEM_DLL_TRIM_SHIFT;
|
||||
ti_pipe3_writel(phy->phy_rx, PIPE3_PHY_RX_TRIM, val);
|
||||
|
||||
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DLL);
|
||||
val |= MEM_DLL_PHINT_RATE;
|
||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DLL, val);
|
||||
val = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_DLL);
|
||||
val &= ~MEM_DLL_PHINT_RATE_MASK;
|
||||
val |= s->dll_phint_rate << MEM_DLL_PHINT_RATE_SHIFT;
|
||||
ti_pipe3_writel(phy->phy_rx, PIPE3_PHY_RX_DLL, val);
|
||||
|
||||
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_EQUALIZER);
|
||||
val &= ~(MEM_EQLEV | MEM_EQCTL | MEM_OVRD_EQLEV | MEM_OVRD_EQFTC);
|
||||
val |= MEM_EQFTC | 0x1 << MEM_EQCTL_SHIFT;
|
||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_EQUALIZER, val);
|
||||
val = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_EQUALIZER);
|
||||
val &= ~(MEM_EQLEV_MASK | MEM_EQFTC_MASK | MEM_EQCTL_MASK |
|
||||
MEM_OVRD_EQLEV | MEM_OVRD_EQFTC);
|
||||
val |= s->eq_lev << MEM_EQLEV_SHIFT |
|
||||
s->eq_ftc << MEM_EQFTC_SHIFT |
|
||||
s->eq_ctl << MEM_EQCTL_SHIFT |
|
||||
s->eq_ovrd_lev << MEM_OVRD_EQLEV_SHIFT |
|
||||
s->eq_ovrd_ftc << MEM_OVRD_EQFTC_SHIFT;
|
||||
ti_pipe3_writel(phy->phy_rx, PIPE3_PHY_RX_EQUALIZER, val);
|
||||
|
||||
if (phy->mode == PIPE3_MODE_SATA) {
|
||||
val = ti_pipe3_readl(phy->phy_rx,
|
||||
SATA_PHY_RX_IO_AND_A2D_OVERRIDES);
|
||||
val &= ~MEM_CDR_LOS_SOURCE_MASK;
|
||||
ti_pipe3_writel(phy->phy_rx, SATA_PHY_RX_IO_AND_A2D_OVERRIDES,
|
||||
val);
|
||||
}
|
||||
}
|
||||
|
||||
static int ti_pipe3_init(struct phy *x)
|
||||
@ -340,7 +516,7 @@ static int ti_pipe3_init(struct phy *x)
|
||||
* as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table
|
||||
* 18-1804.
|
||||
*/
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
|
||||
if (phy->mode == PIPE3_MODE_PCIE) {
|
||||
if (!phy->pcs_syscon) {
|
||||
omap_control_pcie_pcs(phy->control_dev, 0x96);
|
||||
return 0;
|
||||
@ -349,12 +525,7 @@ static int ti_pipe3_init(struct phy *x)
|
||||
val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
|
||||
ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
|
||||
PCIE_PCS_MASK, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ti_pipe3_calibrate(phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bring it out of IDLE if it is IDLE */
|
||||
@ -367,8 +538,7 @@ static int ti_pipe3_init(struct phy *x)
|
||||
|
||||
/* SATA has issues if re-programmed when locked */
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
|
||||
if ((val & PLL_LOCK) && of_device_is_compatible(phy->dev->of_node,
|
||||
"ti,phy-pipe3-sata"))
|
||||
if ((val & PLL_LOCK) && phy->mode == PIPE3_MODE_SATA)
|
||||
return ret;
|
||||
|
||||
/* Program the DPLL */
|
||||
@ -378,6 +548,8 @@ static int ti_pipe3_init(struct phy *x)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ti_pipe3_calibrate(phy);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -390,12 +562,11 @@ static int ti_pipe3_exit(struct phy *x)
|
||||
/* If dpll_reset_syscon is not present we wont power down SATA DPLL
|
||||
* due to Errata i783
|
||||
*/
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") &&
|
||||
!phy->dpll_reset_syscon)
|
||||
if (phy->mode == PIPE3_MODE_SATA && !phy->dpll_reset_syscon)
|
||||
return 0;
|
||||
|
||||
/* PCIe doesn't have internal DPLL */
|
||||
if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
|
||||
if (phy->mode != PIPE3_MODE_PCIE) {
|
||||
/* Put DPLL in IDLE mode */
|
||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
||||
val |= PLL_IDLE;
|
||||
@ -418,7 +589,7 @@ static int ti_pipe3_exit(struct phy *x)
|
||||
}
|
||||
|
||||
/* i783: SATA needs control bit toggle after PLL unlock */
|
||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) {
|
||||
if (phy->mode == PIPE3_MODE_SATA) {
|
||||
regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg,
|
||||
SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET);
|
||||
regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg,
|
||||
@ -443,7 +614,6 @@ static int ti_pipe3_get_clk(struct ti_pipe3 *phy)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct device *dev = phy->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
phy->refclk = devm_clk_get(dev, "refclk");
|
||||
if (IS_ERR(phy->refclk)) {
|
||||
@ -451,11 +621,11 @@ static int ti_pipe3_get_clk(struct ti_pipe3 *phy)
|
||||
/* older DTBs have missing refclk in SATA PHY
|
||||
* so don't bail out in case of SATA PHY.
|
||||
*/
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata"))
|
||||
if (phy->mode != PIPE3_MODE_SATA)
|
||||
return PTR_ERR(phy->refclk);
|
||||
}
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
|
||||
if (phy->mode != PIPE3_MODE_SATA) {
|
||||
phy->wkupclk = devm_clk_get(dev, "wkupclk");
|
||||
if (IS_ERR(phy->wkupclk)) {
|
||||
dev_err(dev, "unable to get wkupclk\n");
|
||||
@ -465,8 +635,7 @@ static int ti_pipe3_get_clk(struct ti_pipe3 *phy)
|
||||
phy->wkupclk = ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") ||
|
||||
phy->phy_power_syscon) {
|
||||
if (phy->mode != PIPE3_MODE_PCIE || phy->phy_power_syscon) {
|
||||
phy->sys_clk = devm_clk_get(dev, "sysclk");
|
||||
if (IS_ERR(phy->sys_clk)) {
|
||||
dev_err(dev, "unable to get sysclk\n");
|
||||
@ -474,7 +643,7 @@ static int ti_pipe3_get_clk(struct ti_pipe3 *phy)
|
||||
}
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
if (phy->mode == PIPE3_MODE_PCIE) {
|
||||
clk = devm_clk_get(dev, "dpll_ref");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "unable to get dpll ref clk\n");
|
||||
@ -546,7 +715,7 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
|
||||
phy->control_dev = &control_pdev->dev;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
|
||||
if (phy->mode == PIPE3_MODE_PCIE) {
|
||||
phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node,
|
||||
"syscon-pcs");
|
||||
if (IS_ERR(phy->pcs_syscon)) {
|
||||
@ -564,7 +733,7 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy)
|
||||
}
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
|
||||
if (phy->mode == PIPE3_MODE_SATA) {
|
||||
phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
|
||||
"syscon-pllreset");
|
||||
if (IS_ERR(phy->dpll_reset_syscon)) {
|
||||
@ -589,12 +758,8 @@ static int ti_pipe3_get_tx_rx_base(struct ti_pipe3 *phy)
|
||||
{
|
||||
struct resource *res;
|
||||
struct device *dev = phy->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie"))
|
||||
return 0;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"phy_rx");
|
||||
phy->phy_rx = devm_ioremap_resource(dev, res);
|
||||
@ -611,24 +776,12 @@ static int ti_pipe3_get_tx_rx_base(struct ti_pipe3 *phy)
|
||||
static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy)
|
||||
{
|
||||
struct resource *res;
|
||||
const struct of_device_id *match;
|
||||
struct device *dev = phy->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-pcie"))
|
||||
if (phy->mode == PIPE3_MODE_PCIE)
|
||||
return 0;
|
||||
|
||||
match = of_match_device(ti_pipe3_id_table, dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
phy->dpll_map = (struct pipe3_dpll_map *)match->data;
|
||||
if (!phy->dpll_map) {
|
||||
dev_err(dev, "no DPLL data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||
"pll_ctrl");
|
||||
phy->pll_ctrl_base = devm_ioremap_resource(dev, res);
|
||||
@ -640,15 +793,29 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
struct ti_pipe3 *phy;
|
||||
struct phy *generic_phy;
|
||||
struct phy_provider *phy_provider;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
const struct of_device_id *match;
|
||||
struct pipe3_data *data;
|
||||
|
||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
match = of_match_device(ti_pipe3_id_table, dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
data = (struct pipe3_data *)match->data;
|
||||
if (!data) {
|
||||
dev_err(dev, "no driver data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
phy->dev = dev;
|
||||
phy->mode = data->mode;
|
||||
phy->dpll_map = data->dpll_map;
|
||||
phy->settings = data->settings;
|
||||
|
||||
ret = ti_pipe3_get_pll_base(phy);
|
||||
if (ret)
|
||||
@ -672,7 +839,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
|
||||
/*
|
||||
* Prevent auto-disable of refclk for SATA PHY due to Errata i783
|
||||
*/
|
||||
if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
|
||||
if (phy->mode == PIPE3_MODE_SATA) {
|
||||
if (!IS_ERR(phy->refclk)) {
|
||||
clk_prepare_enable(phy->refclk);
|
||||
phy->sata_refclk_enabled = true;
|
||||
@ -762,18 +929,19 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy)
|
||||
static const struct of_device_id ti_pipe3_id_table[] = {
|
||||
{
|
||||
.compatible = "ti,phy-usb3",
|
||||
.data = dpll_map_usb,
|
||||
.data = &data_usb,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,omap-usb3",
|
||||
.data = dpll_map_usb,
|
||||
.data = &data_usb,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,phy-pipe3-sata",
|
||||
.data = dpll_map_sata,
|
||||
.data = &data_sata,
|
||||
},
|
||||
{
|
||||
.compatible = "ti,phy-pipe3-pcie",
|
||||
.data = &data_pcie,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
@ -99,6 +99,7 @@ config SCSI_UFS_DWC_TC_PLATFORM
|
||||
config SCSI_UFS_QCOM
|
||||
tristate "QCOM specific hooks to UFS controller platform driver"
|
||||
depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
|
||||
select RESET_CONTROLLER
|
||||
help
|
||||
This selects the QCOM specific additions to UFSHCD platform driver.
|
||||
UFS host on QCOM needs some vendor specific configuration before
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include "ufshcd.h"
|
||||
#include "ufshcd-pltfrm.h"
|
||||
@ -49,6 +50,11 @@ static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
|
||||
static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
|
||||
u32 clk_cycles);
|
||||
|
||||
static struct ufs_qcom_host *rcdev_to_ufs_host(struct reset_controller_dev *rcd)
|
||||
{
|
||||
return container_of(rcd, struct ufs_qcom_host, rcdev);
|
||||
}
|
||||
|
||||
static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int len,
|
||||
const char *prefix, void *priv)
|
||||
{
|
||||
@ -255,11 +261,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
|
||||
if (is_rate_B)
|
||||
phy_set_mode(phy, PHY_MODE_UFS_HS_B);
|
||||
|
||||
/* Assert PHY reset and apply PHY calibration values */
|
||||
ufs_qcom_assert_reset(hba);
|
||||
/* provide 1ms delay to let the reset pulse propagate */
|
||||
usleep_range(1000, 1100);
|
||||
|
||||
/* phy initialization - calibrate the phy */
|
||||
ret = phy_init(phy);
|
||||
if (ret) {
|
||||
@ -268,15 +269,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* De-assert PHY reset and start serdes */
|
||||
ufs_qcom_deassert_reset(hba);
|
||||
|
||||
/*
|
||||
* after reset deassertion, phy will need all ref clocks,
|
||||
* voltage, current to settle down before starting serdes.
|
||||
*/
|
||||
usleep_range(1000, 1100);
|
||||
|
||||
/* power on phy - start serdes and phy's power and clocks */
|
||||
ret = phy_power_on(phy);
|
||||
if (ret) {
|
||||
@ -290,7 +282,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
|
||||
return 0;
|
||||
|
||||
out_disable_phy:
|
||||
ufs_qcom_assert_reset(hba);
|
||||
phy_exit(phy);
|
||||
out:
|
||||
return ret;
|
||||
@ -554,21 +545,10 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
|
||||
ufs_qcom_disable_lane_clks(host);
|
||||
phy_power_off(phy);
|
||||
|
||||
/* Assert PHY soft reset */
|
||||
ufs_qcom_assert_reset(hba);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If UniPro link is not active, PHY ref_clk, main PHY analog power
|
||||
* rail and low noise analog power rail for PLL can be switched off.
|
||||
*/
|
||||
if (!ufs_qcom_is_link_active(hba)) {
|
||||
} else if (!ufs_qcom_is_link_active(hba)) {
|
||||
ufs_qcom_disable_lane_clks(host);
|
||||
phy_power_off(phy);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -578,21 +558,26 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
|
||||
struct phy *phy = host->generic_phy;
|
||||
int err;
|
||||
|
||||
if (ufs_qcom_is_link_off(hba)) {
|
||||
err = phy_power_on(phy);
|
||||
if (err) {
|
||||
dev_err(hba->dev, "%s: failed enabling regs, err = %d\n",
|
||||
dev_err(hba->dev, "%s: failed PHY power on: %d\n",
|
||||
__func__, err);
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ufs_qcom_enable_lane_clks(host);
|
||||
if (err)
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
} else if (!ufs_qcom_is_link_active(hba)) {
|
||||
err = ufs_qcom_enable_lane_clks(host);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
hba->is_sys_suspended = false;
|
||||
|
||||
out:
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ufs_qcom_dev_params {
|
||||
@ -1118,8 +1103,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
|
||||
return 0;
|
||||
|
||||
if (on && (status == POST_CHANGE)) {
|
||||
phy_power_on(host->generic_phy);
|
||||
|
||||
/* enable the device ref clock for HS mode*/
|
||||
if (ufshcd_is_hs_mode(&hba->pwr_info))
|
||||
ufs_qcom_dev_ref_clk_ctrl(host, true);
|
||||
@ -1131,9 +1114,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
|
||||
if (!ufs_qcom_is_link_active(hba)) {
|
||||
/* disable device ref_clk */
|
||||
ufs_qcom_dev_ref_clk_ctrl(host, false);
|
||||
|
||||
/* powering off PHY during aggressive clk gating */
|
||||
phy_power_off(host->generic_phy);
|
||||
}
|
||||
|
||||
vote = host->bus_vote.min_bw_vote;
|
||||
@ -1147,6 +1127,41 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
ufs_qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev);
|
||||
|
||||
/* Currently this code only knows about a single reset. */
|
||||
WARN_ON(id);
|
||||
ufs_qcom_assert_reset(host->hba);
|
||||
/* provide 1ms delay to let the reset pulse propagate. */
|
||||
usleep_range(1000, 1100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ufs_qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id)
|
||||
{
|
||||
struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev);
|
||||
|
||||
/* Currently this code only knows about a single reset. */
|
||||
WARN_ON(id);
|
||||
ufs_qcom_deassert_reset(host->hba);
|
||||
|
||||
/*
|
||||
* after reset deassertion, phy will need all ref clocks,
|
||||
* voltage, current to settle down before starting serdes.
|
||||
*/
|
||||
usleep_range(1000, 1100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct reset_control_ops ufs_qcom_reset_ops = {
|
||||
.assert = ufs_qcom_reset_assert,
|
||||
.deassert = ufs_qcom_reset_deassert,
|
||||
};
|
||||
|
||||
#define ANDROID_BOOT_DEV_MAX 30
|
||||
static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
|
||||
|
||||
@ -1191,6 +1206,17 @@ static int ufs_qcom_init(struct ufs_hba *hba)
|
||||
host->hba = hba;
|
||||
ufshcd_set_variant(hba, host);
|
||||
|
||||
/* Fire up the reset controller. Failure here is non-fatal. */
|
||||
host->rcdev.of_node = dev->of_node;
|
||||
host->rcdev.ops = &ufs_qcom_reset_ops;
|
||||
host->rcdev.owner = dev->driver->owner;
|
||||
host->rcdev.nr_resets = 1;
|
||||
err = devm_reset_controller_register(dev, &host->rcdev);
|
||||
if (err) {
|
||||
dev_warn(dev, "Failed to register reset controller\n");
|
||||
err = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* voting/devoting device ref_clk source is time consuming hence
|
||||
* skip devoting it during aggressive clock gating. This clock
|
||||
|
@ -14,6 +14,8 @@
|
||||
#ifndef UFS_QCOM_H_
|
||||
#define UFS_QCOM_H_
|
||||
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#define MAX_UFS_QCOM_HOSTS 1
|
||||
#define MAX_U32 (~(u32)0)
|
||||
#define MPHY_TX_FSM_STATE 0x41
|
||||
@ -237,6 +239,8 @@ struct ufs_qcom_host {
|
||||
/* Bitmask for enabling debug prints */
|
||||
u32 dbg_print_en;
|
||||
struct ufs_qcom_testbus testbus;
|
||||
|
||||
struct reset_controller_dev rcdev;
|
||||
};
|
||||
|
||||
static inline u32
|
||||
|
@ -4,6 +4,7 @@
|
||||
config SUNXI_SRAM
|
||||
bool
|
||||
default ARCH_SUNXI
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Say y here to enable the SRAM controller support. This
|
||||
device is responsible on mapping the SRAM in the sunXi SoCs
|
||||
|
@ -205,12 +205,9 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
ci->fs_clk = NULL;
|
||||
}
|
||||
ci->fs_clk = clk = devm_clk_get_optional(&pdev->dev, "fs");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
ci->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -468,14 +468,13 @@ static void acm_read_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct acm_rb *rb = urb->context;
|
||||
struct acm *acm = rb->instance;
|
||||
unsigned long flags;
|
||||
int status = urb->status;
|
||||
bool stopped = false;
|
||||
bool stalled = false;
|
||||
|
||||
dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
|
||||
rb->index, urb->actual_length, status);
|
||||
|
||||
set_bit(rb->index, &acm->read_urbs_free);
|
||||
|
||||
if (!acm->dev) {
|
||||
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
|
||||
return;
|
||||
@ -488,15 +487,16 @@ static void acm_read_bulk_callback(struct urb *urb)
|
||||
break;
|
||||
case -EPIPE:
|
||||
set_bit(EVENT_RX_STALL, &acm->flags);
|
||||
schedule_work(&acm->work);
|
||||
return;
|
||||
stalled = true;
|
||||
break;
|
||||
case -ENOENT:
|
||||
case -ECONNRESET:
|
||||
case -ESHUTDOWN:
|
||||
dev_dbg(&acm->data->dev,
|
||||
"%s - urb shutting down with status: %d\n",
|
||||
__func__, status);
|
||||
return;
|
||||
stopped = true;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&acm->data->dev,
|
||||
"%s - nonzero urb status received: %d\n",
|
||||
@ -505,20 +505,29 @@ static void acm_read_bulk_callback(struct urb *urb)
|
||||
}
|
||||
|
||||
/*
|
||||
* Unthrottle may run on another CPU which needs to see events
|
||||
* in the same order. Submission has an implict barrier
|
||||
* Make sure URB processing is done before marking as free to avoid
|
||||
* racing with unthrottle() on another CPU. Matches the barriers
|
||||
* implied by the test_and_clear_bit() in acm_submit_read_urb().
|
||||
*/
|
||||
smp_mb__before_atomic();
|
||||
set_bit(rb->index, &acm->read_urbs_free);
|
||||
/*
|
||||
* Make sure URB is marked as free before checking the throttled flag
|
||||
* to avoid racing with unthrottle() on another CPU. Matches the
|
||||
* smp_mb() in unthrottle().
|
||||
*/
|
||||
smp_mb__after_atomic();
|
||||
|
||||
/* throttle device if requested by tty */
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
acm->throttled = acm->throttle_req;
|
||||
if (!acm->throttled) {
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&acm->read_lock, flags);
|
||||
if (stopped || stalled) {
|
||||
if (stalled)
|
||||
schedule_work(&acm->work);
|
||||
return;
|
||||
}
|
||||
|
||||
if (test_bit(ACM_THROTTLED, &acm->flags))
|
||||
return;
|
||||
|
||||
acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
/* data interface wrote those outgoing bytes */
|
||||
@ -655,10 +664,7 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
|
||||
/*
|
||||
* Unthrottle device in case the TTY was closed while throttled.
|
||||
*/
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
acm->throttled = 0;
|
||||
acm->throttle_req = 0;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
clear_bit(ACM_THROTTLED, &acm->flags);
|
||||
|
||||
retval = acm_submit_read_urbs(acm, GFP_KERNEL);
|
||||
if (retval)
|
||||
@ -826,23 +832,18 @@ static void acm_tty_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
acm->throttle_req = 1;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
set_bit(ACM_THROTTLED, &acm->flags);
|
||||
}
|
||||
|
||||
static void acm_tty_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
unsigned int was_throttled;
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
was_throttled = acm->throttled;
|
||||
acm->throttled = 0;
|
||||
acm->throttle_req = 0;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
clear_bit(ACM_THROTTLED, &acm->flags);
|
||||
|
||||
/* Matches the smp_mb__after_atomic() in acm_read_bulk_callback(). */
|
||||
smp_mb();
|
||||
|
||||
if (was_throttled)
|
||||
acm_submit_read_urbs(acm, GFP_KERNEL);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user