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
|
@ -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.
|
|
@ -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
|
- clocks: phandles to the clocks for each clock listed in clock-names
|
||||||
- clock-names: must contain "phy"
|
- clock-names: must contain "phy"
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- vbus-supply: A phandle to the regulator for USB VBUS.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
usb3_phy0: phy@381f0040 {
|
usb3_phy0: phy@381f0040 {
|
||||||
compatible = "fsl,imx8mq-usb-phy";
|
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"
|
- Tegra124: "nvidia,tegra124-xusb-padctl"
|
||||||
- Tegra132: "nvidia,tegra132-xusb-padctl", "nvidia,tegra124-xusb-padctl"
|
- Tegra132: "nvidia,tegra132-xusb-padctl", "nvidia,tegra124-xusb-padctl"
|
||||||
- Tegra210: "nvidia,tegra210-xusb-padctl"
|
- Tegra210: "nvidia,tegra210-xusb-padctl"
|
||||||
|
- Tegra186: "nvidia,tegra186-xusb-padctl"
|
||||||
- reg: Physical base address and length of the controller's registers.
|
- reg: Physical base address and length of the controller's registers.
|
||||||
- resets: Must contain an entry for each entry in reset-names.
|
- resets: Must contain an entry for each entry in reset-names.
|
||||||
- reset-names: Must include the following entries:
|
- reset-names: Must include the following entries:
|
||||||
- "padctl"
|
- "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:
|
Pad nodes:
|
||||||
==========
|
==========
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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,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-usb3-phy" for USB3 QMP V3 phy on msm8998,
|
||||||
"qcom,msm8998-qmp-ufs-phy" for UFS QMP 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-phy" for USB3 QMP V3 phy on sdm845,
|
||||||
"qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845,
|
"qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845,
|
||||||
"qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845.
|
"qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845.
|
||||||
|
@ -48,6 +49,8 @@ Required properties:
|
||||||
"aux", "cfg_ahb", "ref".
|
"aux", "cfg_ahb", "ref".
|
||||||
For "qcom,msm8998-qmp-ufs-phy" must contain:
|
For "qcom,msm8998-qmp-ufs-phy" must contain:
|
||||||
"ref", "ref_aux".
|
"ref", "ref_aux".
|
||||||
|
For "qcom,msm8998-qmp-pcie-phy" must contain:
|
||||||
|
"aux", "cfg_ahb", "ref".
|
||||||
For "qcom,sdm845-qmp-usb3-phy" must contain:
|
For "qcom,sdm845-qmp-usb3-phy" must contain:
|
||||||
"aux", "cfg_ahb", "ref", "com_aux".
|
"aux", "cfg_ahb", "ref", "com_aux".
|
||||||
For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
|
For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
|
||||||
|
@ -59,7 +62,8 @@ Required properties:
|
||||||
one for each entry in reset-names.
|
one for each entry in reset-names.
|
||||||
- reset-names: "phy" for reset of phy block,
|
- reset-names: "phy" for reset of phy block,
|
||||||
"common" for phy common block reset,
|
"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:
|
For "qcom,ipq8074-qmp-pcie-phy" must contain:
|
||||||
"phy", "common".
|
"phy", "common".
|
||||||
|
@ -69,12 +73,16 @@ Required properties:
|
||||||
"phy", "common".
|
"phy", "common".
|
||||||
For "qcom,msm8998-qmp-usb3-phy" must contain
|
For "qcom,msm8998-qmp-usb3-phy" must contain
|
||||||
"phy", "common".
|
"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:
|
For "qcom,sdm845-qmp-usb3-phy" must contain:
|
||||||
"phy", "common".
|
"phy", "common".
|
||||||
For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
|
For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
|
||||||
"phy", "common".
|
"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-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.
|
- 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.
|
- 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-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-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-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-r8a7791" if the device is a part of R8A7791 SoC.
|
||||||
"renesas,usb-phy-r8a7794" if the device is a part of R8A7794 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>.
|
- #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 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 | | |
|
|\ Selector | | |
|
||||||
|
@ -41,6 +42,16 @@ the USB channel; see the selector meanings below:
|
||||||
| 2 | PCI EHCI/OHCI | xHCI |
|
| 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):
|
Example (Lager board):
|
||||||
|
|
||||||
usb-phy@e6590100 {
|
usb-phy@e6590100 {
|
||||||
|
@ -48,15 +59,53 @@ Example (Lager board):
|
||||||
reg = <0 0xe6590100 0 0x100>;
|
reg = <0 0xe6590100 0 0x100>;
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
clocks = <&mstp7_clks R8A7790_CLK_HSUSB>;
|
clocks = <&cpg CPG_MOD 704>;
|
||||||
clock-names = "usbhs";
|
clock-names = "usbhs";
|
||||||
|
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
|
||||||
|
resets = <&cpg 704>;
|
||||||
|
|
||||||
usb-channel@0 {
|
usb0: usb-channel@0 {
|
||||||
reg = <0>;
|
reg = <0>;
|
||||||
#phy-cells = <1>;
|
#phy-cells = <1>;
|
||||||
};
|
};
|
||||||
usb-channel@2 {
|
usb2: usb-channel@2 {
|
||||||
reg = <2>;
|
reg = <2>;
|
||||||
#phy-cells = <1>;
|
#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
|
* Renesas R-Car generation 3 USB 2.0 PHY
|
||||||
|
|
||||||
This file provides information on what the device node for the R-Car generation
|
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:
|
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.
|
SoC.
|
||||||
"renesas,usb2-phy-r8a774c0" if the device is a part of an R8A774C0
|
"renesas,usb2-phy-r8a774c0" if the device is a part of an R8A774C0
|
||||||
SoC.
|
SoC.
|
||||||
|
@ -27,7 +29,13 @@ Required properties:
|
||||||
|
|
||||||
- reg: offset and length of the partial USB 2.0 Host register block.
|
- reg: offset and length of the partial USB 2.0 Host register block.
|
||||||
- clocks: clock phandle and specifier pair(s).
|
- 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:
|
Optional properties:
|
||||||
To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
|
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
|
- reg: PHY register address offset and length in "general
|
||||||
register files"
|
register files"
|
||||||
|
|
||||||
Optional clocks using the clock bindings (see ../clock/clock-bindings.txt),
|
Optional properties:
|
||||||
specified by name:
|
|
||||||
- clock-names: Should contain "emmcclk". Although this is listed as optional
|
- clock-names: Should contain "emmcclk". Although this is listed as optional
|
||||||
(because most boards can get basic functionality without having
|
(because most boards can get basic functionality without having
|
||||||
access to it), it is strongly suggested.
|
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.
|
- 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:
|
Example:
|
||||||
|
|
||||||
|
@ -29,6 +32,7 @@ grf: syscon@ff770000 {
|
||||||
reg = <0xf780 0x20>;
|
reg = <0xf780 0x20>;
|
||||||
clocks = <&sdhci>;
|
clocks = <&sdhci>;
|
||||||
clock-names = "emmcclk";
|
clock-names = "emmcclk";
|
||||||
|
drive-impedance-ohm = <50>;
|
||||||
#phy-cells = <0>;
|
#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
|
- 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-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
|
- 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:
|
Example:
|
||||||
|
|
||||||
|
@ -51,9 +52,11 @@ Example:
|
||||||
<&clock_gcc clk_ufs_phy_ldo>,
|
<&clock_gcc clk_ufs_phy_ldo>,
|
||||||
<&clock_gcc clk_gcc_ufs_tx_cfg_clk>,
|
<&clock_gcc clk_gcc_ufs_tx_cfg_clk>,
|
||||||
<&clock_gcc clk_gcc_ufs_rx_cfg_clk>;
|
<&clock_gcc clk_gcc_ufs_rx_cfg_clk>;
|
||||||
|
resets = <&ufshc 0>;
|
||||||
};
|
};
|
||||||
|
|
||||||
ufshc@fc598000 {
|
ufshc: ufshc@fc598000 {
|
||||||
|
#reset-cells = <1>;
|
||||||
...
|
...
|
||||||
phys = <&ufsphy1>;
|
phys = <&ufsphy1>;
|
||||||
phy-names = "ufsphy";
|
phy-names = "ufsphy";
|
||||||
|
|
|
@ -50,6 +50,8 @@ Optional properties:
|
||||||
-lanes-per-direction : number of lanes available per direction - either 1 or 2.
|
-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
|
Note that it is assume same number of lanes is used both
|
||||||
directions at once. If not specified, default is 2 lanes per direction.
|
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
|
- resets : reset node register
|
||||||
- reset-names : describe reset node register, the "rst" corresponds to reset the whole UFS IP.
|
- reset-names : describe reset node register, the "rst" corresponds to reset the whole UFS IP.
|
||||||
|
|
||||||
|
@ -79,4 +81,5 @@ Example:
|
||||||
reset-names = "rst";
|
reset-names = "rst";
|
||||||
phys = <&ufsphy1>;
|
phys = <&ufsphy1>;
|
||||||
phy-names = "ufsphy";
|
phy-names = "ufsphy";
|
||||||
|
#reset-cells = <1>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,3 +40,91 @@ Example device nodes:
|
||||||
phy-names = "usb2-phy", "usb3-phy";
|
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,meson8-usb": The DWC2 USB controller instance in Amlogic Meson8 SoCs;
|
||||||
- "amlogic,meson8b-usb": The DWC2 USB controller instance in Amlogic Meson8b 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-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;
|
- "amcc,dwc-otg": The DWC2 USB controller instance in AMCC Canyonlands 460EX SoCs;
|
||||||
- snps,dwc2: A generic DWC2 USB controller with default parameters.
|
- snps,dwc2: A generic DWC2 USB controller with default parameters.
|
||||||
- "st,stm32f4x9-fsotg": The DWC2 USB FS/HS controller instance in STM32F4x9 SoCs
|
- "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:
|
Optional properties:
|
||||||
- phys: phy provider specifier
|
- phys: phy provider specifier
|
||||||
- phy-names: shall be "usb2-phy"
|
- 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
|
Refer to phy/phy-bindings.txt for generic phy consumer properties
|
||||||
- dr_mode: shall be one of "host", "peripheral" and "otg"
|
- dr_mode: shall be one of "host", "peripheral" and "otg"
|
||||||
Refer to usb/generic.txt
|
Refer to usb/generic.txt
|
||||||
- g-rx-fifo-size: size of rx fifo size in gadget mode.
|
- 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-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.
|
- 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:
|
Deprecated properties:
|
||||||
- g-use-dma: gadget DMA mode is automatically detected
|
- g-use-dma: gadget DMA mode is automatically detected
|
||||||
|
|
|
@ -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";
|
||||||
|
};
|
||||||
|
|
||||||
|
...
|
|
@ -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"
|
- interrupt-names: must be "mc"
|
||||||
- clocks: phandle to the "udc" clock
|
- clocks: phandle to the "udc" clock
|
||||||
- clock-names: must be "udc"
|
- clock-names: must be "udc"
|
||||||
|
- phys: phandle to the USB PHY
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
usb_phy: usb-phy@0 {
|
||||||
|
compatible = "usb-nop-xceiv";
|
||||||
|
#phy-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
udc: usb@13040000 {
|
udc: usb@13040000 {
|
||||||
compatible = "ingenic,jz4740-musb";
|
compatible = "ingenic,jz4740-musb";
|
||||||
reg = <0x13040000 0x10000>;
|
reg = <0x13040000 0x10000>;
|
||||||
|
@ -21,4 +27,6 @@ udc: usb@13040000 {
|
||||||
|
|
||||||
clocks = <&cgu JZ4740_CLK_UDC>;
|
clocks = <&cgu JZ4740_CLK_UDC>;
|
||||||
clock-names = "udc";
|
clock-names = "udc";
|
||||||
|
|
||||||
|
phys = <&usb_phy>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@ Required properties:
|
||||||
- Tegra124: "nvidia,tegra124-xusb"
|
- Tegra124: "nvidia,tegra124-xusb"
|
||||||
- Tegra132: "nvidia,tegra132-xusb", "nvidia,tegra124-xusb"
|
- Tegra132: "nvidia,tegra132-xusb", "nvidia,tegra124-xusb"
|
||||||
- Tegra210: "nvidia,tegra210-xusb"
|
- Tegra210: "nvidia,tegra210-xusb"
|
||||||
|
- Tegra186: "nvidia,tegra186-xusb"
|
||||||
- reg: Must contain the base and length of the xHCI host registers, XUSB FPCI
|
- reg: Must contain the base and length of the xHCI host registers, XUSB FPCI
|
||||||
registers and XUSB IPFS registers.
|
registers and XUSB IPFS registers.
|
||||||
- reg-names: Must contain the following entries:
|
- 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.
|
- 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.
|
- 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.
|
- 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
|
- 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
|
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
|
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
|
- 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,
|
- Tegra210: usb2-0, usb2-1, usb2-2, usb2-3, hsic-0, usb3-0, usb3-1, usb3-2,
|
||||||
usb3-3
|
usb3-3
|
||||||
|
- Tegra186: usb2-0, usb2-1, usb2-2, hsic-0, usb3-0, usb3-1, usb3-2
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
--------
|
--------
|
||||||
|
|
|
@ -6,6 +6,7 @@ Required properties:
|
||||||
- "renesas,usbhs-r8a7743" for r8a7743 (RZ/G1M) compatible device
|
- "renesas,usbhs-r8a7743" for r8a7743 (RZ/G1M) compatible device
|
||||||
- "renesas,usbhs-r8a7744" for r8a7744 (RZ/G1N) compatible device
|
- "renesas,usbhs-r8a7744" for r8a7744 (RZ/G1N) compatible device
|
||||||
- "renesas,usbhs-r8a7745" for r8a7745 (RZ/G1E) compatible device
|
- "renesas,usbhs-r8a7745" for r8a7745 (RZ/G1E) compatible device
|
||||||
|
- "renesas,usbhs-r8a77470" for r8a77470 (RZ/G1C) compatible device
|
||||||
- "renesas,usbhs-r8a774a1" for r8a774a1 (RZ/G2M) compatible device
|
- "renesas,usbhs-r8a774a1" for r8a774a1 (RZ/G2M) compatible device
|
||||||
- "renesas,usbhs-r8a774c0" for r8a774c0 (RZ/G2E) compatible device
|
- "renesas,usbhs-r8a774c0" for r8a774c0 (RZ/G2E) compatible device
|
||||||
- "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
|
- "renesas,usbhs-r8a7790" for r8a7790 (R-Car H2) compatible device
|
||||||
|
|
|
@ -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>;
|
|
||||||
};
|
|
|
@ -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-r8a7743" for r8a7743 SoC
|
||||||
- "renesas,xhci-r8a7744" for r8a7744 SoC
|
- "renesas,xhci-r8a7744" for r8a7744 SoC
|
||||||
- "renesas,xhci-r8a774a1" for r8a774a1 SoC
|
- "renesas,xhci-r8a774a1" for r8a774a1 SoC
|
||||||
|
- "renesas,xhci-r8a774c0" for r8a774c0 SoC
|
||||||
- "renesas,xhci-r8a7790" for r8a7790 SoC
|
- "renesas,xhci-r8a7790" for r8a7790 SoC
|
||||||
- "renesas,xhci-r8a7791" for r8a7791 SoC
|
- "renesas,xhci-r8a7791" for r8a7791 SoC
|
||||||
- "renesas,xhci-r8a7793" for r8a7793 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
|
- power-on-time-ms : Specifies the time it takes from the time the host
|
||||||
initiates the power-on sequence to a port until the port has adequate
|
initiates the power-on sequence to a port until the port has adequate
|
||||||
power. The value is given in ms in a 0 - 510 range (default is 100ms).
|
power. The value is given in ms in a 0 - 510 range (default is 100ms).
|
||||||
- swap-dx-lanes : Specifies the ports which will swap the differential-pair
|
- swap-dx-lanes : Specifies the downstream ports which will swap the
|
||||||
(D+/D-), default is not-swapped.
|
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:
|
Examples:
|
||||||
usb2512b@2c {
|
usb2512b@2c {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
================================
|
||||||
Linux UWB + Wireless USB + WiNET
|
Linux UWB + Wireless USB + WiNET
|
||||||
|
================================
|
||||||
|
|
||||||
|
Copyright (C) 2005-2006 Intel Corporation
|
||||||
|
|
||||||
(C) 2005-2006 Intel Corporation
|
|
||||||
Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
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
|
Wireless USB 1.0 specification (including Wireless USB host controller
|
||||||
and an Intel WiNET controller).
|
and an Intel WiNET controller).
|
||||||
|
|
||||||
|
.. Contents
|
||||||
1. Introduction
|
1. Introduction
|
||||||
1. HWA: Host Wire adapters, your Wireless USB dongle
|
1. HWA: Host Wire adapters, your Wireless USB dongle
|
||||||
|
|
||||||
|
@ -51,7 +54,8 @@ and an Intel WiNET controller).
|
||||||
4. Glossary
|
4. Glossary
|
||||||
|
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
UWB is a wide-band communication protocol that is to serve also as the
|
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
|
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.
|
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
|
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
|
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.
|
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
|
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
|
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.
|
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
|
This is your usual PCI device that implements WHCI. Similar in concept
|
||||||
to EHCI, it allows your wireless USB devices (including DWAs) to connect
|
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.
|
releases.
|
||||||
|
|
||||||
|
|
||||||
The UWB stack
|
The UWB stack
|
||||||
|
=============
|
||||||
|
|
||||||
The main mission of the UWB stack is to keep a tally of which devices
|
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
|
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.
|
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
|
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
|
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.
|
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
|
So let's say we connect a dongle to the system: it is detected and
|
||||||
firmware uploaded if needed [for Intel's i1480
|
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...).
|
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
|
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:
|
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].
|
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.
|
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
|
The UWB stack maintains a local copy of DRP availability through
|
||||||
processing of incoming *DRP Availability Change* notifications. This
|
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.]
|
subject to change.]
|
||||||
|
|
||||||
|
|
||||||
Wireless USB Host Controller drivers
|
Wireless USB Host Controller drivers
|
||||||
|
====================================
|
||||||
|
|
||||||
*WARNING* This section needs a lot of work!
|
*WARNING* This section needs a lot of work!
|
||||||
|
|
||||||
|
@ -296,7 +310,8 @@ starts sending MMCs.
|
||||||
|
|
||||||
Now it all depends on external stimuli.
|
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
|
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,
|
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
|
start enumerating and doing transfers through usb_hcd->urb_enqueue() to
|
||||||
read descriptors and move our data.
|
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
|
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
|
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
|
If the device wants to disconnect, it will either die (ugly) or send a
|
||||||
/DN_Disconnect/ that will prompt a disconnection from the system.
|
/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
|
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
|
/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
|
For IN xfers, we only issue URBs for the segments we want to read and
|
||||||
then wait for the xfer result data.
|
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
|
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
|
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.
|
called--this will call the URB callback.
|
||||||
|
|
||||||
|
|
||||||
Glossary
|
Glossary
|
||||||
|
========
|
||||||
|
|
||||||
*DWA* -- Device Wire Adapter
|
*DWA* -- Device Wire Adapter
|
||||||
|
|
||||||
|
@ -436,4 +455,3 @@ the host.
|
||||||
|
|
||||||
Design-overview.txt-1.8 (last edited 2006-11-04 12:22:24 by
|
Design-overview.txt-1.8 (last edited 2006-11-04 12:22:24 by
|
||||||
InakyPerezGonzalez)
|
InakyPerezGonzalez)
|
||||||
|
|
||||||
|
|
|
@ -1,127 +1,131 @@
|
||||||
Linux ACM driver v0.16
|
======================
|
||||||
(c) 1999 Vojtech Pavlik <vojtech@suse.cz>
|
Linux ACM driver v0.16
|
||||||
Sponsored by SuSE
|
======================
|
||||||
----------------------------------------------------------------------------
|
|
||||||
|
Copyright (c) 1999 Vojtech Pavlik <vojtech@suse.cz>
|
||||||
|
|
||||||
|
Sponsored by SuSE
|
||||||
|
|
||||||
0. Disclaimer
|
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
|
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)
|
Software Foundation; either version 2 of the License, or (at your option)
|
||||||
any later version.
|
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
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
more details.
|
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
|
with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
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
|
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,
|
mail your message to <vojtech@suse.cz>, or by paper mail: Vojtech Pavlik,
|
||||||
Ucitelska 1576, Prague 8, 182 00 Czech Republic
|
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.
|
in the package: See the file COPYING.
|
||||||
|
|
||||||
1. Usage
|
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
|
adapters that conform to the Universal Serial Bus Communication Device Class
|
||||||
Abstract Control Model (USB CDC ACM) specification.
|
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 OfficeConnect 56k
|
||||||
3Com Voice FaxModem Pro
|
- 3Com Voice FaxModem Pro
|
||||||
3Com Sportster
|
- 3Com Sportster
|
||||||
MultiTech MultiModem 56k
|
- MultiTech MultiModem 56k
|
||||||
Zoom 2986L FaxModem
|
- Zoom 2986L FaxModem
|
||||||
Compaq 56k FaxModem
|
- Compaq 56k FaxModem
|
||||||
ELSA Microlink 56k
|
- 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.
|
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
|
usbcore.ko
|
||||||
uhci-hcd.ko ohci-hcd.ko or ehci-hcd.ko
|
uhci-hcd.ko ohci-hcd.ko or ehci-hcd.ko
|
||||||
cdc-acm.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.
|
minicom, ppp and mgetty with them.
|
||||||
|
|
||||||
2. Verifying that it works
|
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
|
The first step would be to check /sys/kernel/debug/usb/devices, it should look
|
||||||
B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
|
like this::
|
||||||
D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
|
||||||
P: Vendor=0000 ProdID=0000 Rev= 0.00
|
T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2
|
||||||
S: Product=USB UHCI Root Hub
|
B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0
|
||||||
S: SerialNumber=6800
|
D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
||||||
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA
|
P: Vendor=0000 ProdID=0000 Rev= 0.00
|
||||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
|
S: Product=USB UHCI Root Hub
|
||||||
E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms
|
S: SerialNumber=6800
|
||||||
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
|
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA
|
||||||
D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
|
I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
|
||||||
P: Vendor=04c1 ProdID=008f Rev= 2.07
|
E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms
|
||||||
S: Manufacturer=3Com Inc.
|
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
|
||||||
S: Product=3Com U.S. Robotics Pro ISDN TA
|
D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2
|
||||||
S: SerialNumber=UFT53A49BVT7
|
P: Vendor=04c1 ProdID=008f Rev= 2.07
|
||||||
C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA
|
S: Manufacturer=3Com Inc.
|
||||||
I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm
|
S: Product=3Com U.S. Robotics Pro ISDN TA
|
||||||
E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
S: SerialNumber=UFT53A49BVT7
|
||||||
E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA
|
||||||
E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
|
I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm
|
||||||
C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA
|
E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||||
I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
|
E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
||||||
E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms
|
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
|
C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA
|
||||||
E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm
|
||||||
E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms
|
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)
|
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
|
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
|
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
|
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#= 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
|
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: USB new device connect, assigned device number 2
|
||||||
usb.c: kmalloc IF c7691fa0, numif 1
|
usb.c: kmalloc IF c7691fa0, numif 1
|
||||||
usb.c: kmalloc IF c7b5f3e0, numif 2
|
usb.c: kmalloc IF c7b5f3e0, numif 2
|
||||||
usb.c: skipped 4 class/vendor specific interface descriptors
|
usb.c: skipped 4 class/vendor specific interface descriptors
|
||||||
usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3
|
usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3
|
||||||
usb.c: USB device number 2 default language ID 0x409
|
usb.c: USB device number 2 default language ID 0x409
|
||||||
Manufacturer: 3Com Inc.
|
Manufacturer: 3Com Inc.
|
||||||
Product: 3Com U.S. Robotics Pro ISDN TA
|
Product: 3Com U.S. Robotics Pro ISDN TA
|
||||||
SerialNumber: UFT53A49BVT7
|
SerialNumber: UFT53A49BVT7
|
||||||
acm.c: probing config 1
|
acm.c: probing config 1
|
||||||
acm.c: probing config 2
|
acm.c: probing config 2
|
||||||
ttyACM0: USB ACM device
|
ttyACM0: USB ACM device
|
||||||
acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0
|
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
|
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 c7b5f3e0
|
||||||
usb.c: acm driver claimed interface c7b5f3f8
|
usb.c: acm driver claimed interface c7b5f3f8
|
||||||
usb.c: acm driver claimed interface c7691fa0
|
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
|
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
|
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
|
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
|
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
|
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
|
modification, only if root authorizes the device to be configured will
|
||||||
then it be possible to use it.
|
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:
|
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
|
By default, Wired USB devices are authorized by default to
|
||||||
connect. Wireless USB hosts deauthorize by default all new connected
|
connect. Wireless USB hosts deauthorize by default all new connected
|
||||||
|
@ -40,21 +42,21 @@ USB ports.
|
||||||
|
|
||||||
|
|
||||||
Example system lockdown (lame)
|
Example system lockdown (lame)
|
||||||
-----------------------
|
------------------------------
|
||||||
|
|
||||||
Imagine you want to implement a lockdown so only devices of type XYZ
|
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
|
can be connected (for example, it is a kiosk machine with a visible
|
||||||
USB port):
|
USB port)::
|
||||||
|
|
||||||
boot up
|
boot up
|
||||||
rc.local ->
|
rc.local ->
|
||||||
|
|
||||||
for host in /sys/bus/usb/devices/usb*
|
for host in /sys/bus/usb/devices/usb*
|
||||||
do
|
do
|
||||||
echo 0 > $host/authorized_default
|
echo 0 > $host/authorized_default
|
||||||
done
|
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
|
if device_is_my_type $DEV
|
||||||
then
|
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
|
security verification you can make (or the best, for someone willing
|
||||||
to break it). If you need something secure, use crypto and Certificate
|
to break it). If you need something secure, use crypto and Certificate
|
||||||
Authentication or stuff like that. Something simple for an storage key
|
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
|
echo 1 > authorized # temporarily authorize it
|
||||||
# FIXME: make sure none can mount it
|
# FIXME: make sure none can mount it
|
||||||
mount DEVICENODE /mntpoint
|
mount DEVICENODE /mntpoint
|
||||||
|
@ -83,7 +85,7 @@ function device_is_my_type()
|
||||||
else
|
else
|
||||||
echo 0 > authorized
|
echo 0 > authorized
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Of course, this is lame, you'd want to do a real certificate
|
Of course, this is lame, you'd want to do a real certificate
|
||||||
|
@ -95,30 +97,35 @@ welcome.
|
||||||
|
|
||||||
Interface authorization
|
Interface authorization
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
There is a similar approach to allow or deny specific USB interfaces.
|
There is a similar approach to allow or deny specific USB interfaces.
|
||||||
That allows to block only a subset of an USB device.
|
That allows to block only a subset of an USB device.
|
||||||
|
|
||||||
Authorize an interface:
|
Authorize an interface::
|
||||||
$ echo 1 > /sys/bus/usb/devices/INTERFACE/authorized
|
|
||||||
|
|
||||||
Deauthorize an interface:
|
$ echo 1 > /sys/bus/usb/devices/INTERFACE/authorized
|
||||||
$ echo 0 > /sys/bus/usb/devices/INTERFACE/authorized
|
|
||||||
|
Deauthorize an interface::
|
||||||
|
|
||||||
|
$ echo 0 > /sys/bus/usb/devices/INTERFACE/authorized
|
||||||
|
|
||||||
The default value for new interfaces
|
The default value for new interfaces
|
||||||
on a particular USB bus can be changed, too.
|
on a particular USB bus can be changed, too.
|
||||||
|
|
||||||
Allow interfaces per default:
|
Allow interfaces per default::
|
||||||
$ echo 1 > /sys/bus/usb/devices/usbX/interface_authorized_default
|
|
||||||
|
|
||||||
Deny interfaces per default:
|
$ echo 1 > /sys/bus/usb/devices/usbX/interface_authorized_default
|
||||||
$ echo 0 > /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.
|
Per default the interface_authorized_default bit is 1.
|
||||||
So all interfaces would authorized per default.
|
So all interfaces would authorized per default.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
If a deauthorized interface will be authorized so the driver probing must
|
If a deauthorized interface will be authorized so the driver probing must
|
||||||
be triggered manually by writing INTERFACE to /sys/bus/usb/drivers_probe
|
be triggered manually by writing INTERFACE to /sys/bus/usb/drivers_probe
|
||||||
|
|
||||||
For drivers that need multiple interfaces all needed interfaces should be
|
For drivers that need multiple interfaces all needed interfaces should be
|
||||||
authorized first. After that the drivers should be probed.
|
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)
|
1. How to test OTG FSM(HNP and SRP)
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
To show how to demo OTG HNP and SRP functions via sys input files
|
To show how to demo OTG HNP and SRP functions via sys input files
|
||||||
with 2 Freescale i.MX6Q sabre SD boards.
|
with 2 Freescale i.MX6Q sabre SD boards.
|
||||||
|
|
||||||
1.1 How to enable OTG FSM
|
1.1 How to enable OTG FSM
|
||||||
---------------------------------------
|
-------------------------
|
||||||
|
|
||||||
1.1.1 Select CONFIG_USB_OTG_FSM in menuconfig, rebuild kernel
|
1.1.1 Select CONFIG_USB_OTG_FSM in menuconfig, rebuild kernel
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Image and modules. If you want to check some internal
|
Image and modules. If you want to check some internal
|
||||||
variables for otg fsm, mount debugfs, there are 2 files
|
variables for otg fsm, mount debugfs, there are 2 files
|
||||||
which can show otg fsm variables and some controller registers value:
|
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
|
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
|
1.1.2 Add below entries in your dts file for your controller node
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
otg-rev = <0x0200>;
|
otg-rev = <0x0200>;
|
||||||
adp-disable;
|
adp-disable;
|
||||||
|
|
||||||
1.2 Test operations
|
1.2 Test operations
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
1) Power up 2 Freescale i.MX6Q sabre SD boards with gadget class driver loaded
|
1) Power up 2 Freescale i.MX6Q sabre SD boards with gadget class driver loaded
|
||||||
(e.g. g_mass_storage).
|
(e.g. g_mass_storage).
|
||||||
|
|
||||||
|
@ -26,19 +41,24 @@ cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||||
The A-device(with micro A plug inserted) should enumerate B-device.
|
The A-device(with micro A plug inserted) should enumerate B-device.
|
||||||
|
|
||||||
3) Role switch
|
3) Role switch
|
||||||
On B-device:
|
|
||||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
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.
|
B-device should take host role and enumerate A-device.
|
||||||
|
|
||||||
4) A-device switch back to host.
|
4) A-device switch back to host.
|
||||||
On B-device:
|
|
||||||
echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
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
|
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
|
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
|
|
||||||
|
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
|
||||||
|
|
||||||
A-device should switch back to host and enumerate B-device.
|
A-device should switch back to host and enumerate B-device.
|
||||||
|
|
||||||
|
@ -49,23 +69,31 @@ cat /sys/kernel/debug/ci_hdrc.0/registers
|
||||||
A-device should NOT enumerate B-device.
|
A-device should NOT enumerate B-device.
|
||||||
|
|
||||||
if A-device wants to use bus:
|
if A-device wants to use bus:
|
||||||
On A-device:
|
|
||||||
echo 0 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_drop
|
On A-device::
|
||||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_req
|
|
||||||
|
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:
|
if B-device wants to use bus:
|
||||||
On B-device:
|
|
||||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
On B-device::
|
||||||
|
|
||||||
|
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
||||||
|
|
||||||
7) A-device power down the bus.
|
7) A-device power down the bus.
|
||||||
On A-device:
|
|
||||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/a_bus_drop
|
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.
|
A-device should disconnect with B-device and power down the bus.
|
||||||
|
|
||||||
8) B-device does data pulse for SRP.
|
8) B-device does data pulse for SRP.
|
||||||
On B-device:
|
|
||||||
echo 1 > /sys/bus/platform/devices/ci_hdrc.0/inputs/b_bus_req
|
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.
|
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"
|
July 27, 2012 Revision 2.0 version 1.1a"
|
||||||
|
|
||||||
2. How to enable USB as system wakeup source
|
2. How to enable USB as system wakeup source
|
||||||
-----------------------------------
|
--------------------------------------------
|
||||||
Below is the example for how to enable USB as system wakeup source
|
Below is the example for how to enable USB as system wakeup source
|
||||||
at imx6 platform.
|
at imx6 platform.
|
||||||
|
|
||||||
2.1 Enable core's 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/ci_hdrc.0/power/wakeup
|
||||||
echo enabled > /sys/bus/platform/devices/2184000.usb/power/wakeup
|
|
||||||
2.3 Enable PHY's wakeup (optional)
|
2.2 Enable glue layer's wakeup::
|
||||||
echo enabled > /sys/bus/platform/devices/20c9000.usbphy/power/wakeup
|
|
||||||
2.4 Enable roothub's wakeup
|
echo enabled > /sys/bus/platform/devices/2184000.usb/power/wakeup
|
||||||
echo enabled > /sys/bus/usb/devices/usb1/power/wakeup
|
|
||||||
2.5 Enable related device's wakeup
|
2.3 Enable PHY's wakeup (optional)::
|
||||||
echo enabled > /sys/bus/usb/devices/1-1/power/wakeup
|
|
||||||
|
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
|
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.
|
can use below script to enable usb wakeup::
|
||||||
for i in $(find /sys -name wakeup | grep usb);do echo enabled > $i;done;
|
|
||||||
|
|
||||||
|
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 :)
|
Please pick something while reading :)
|
||||||
|
|
||||||
- Convert interrupt handler to per-ep-thread-irq
|
- Convert interrupt handler to per-ep-thread-irq
|
||||||
|
@ -9,6 +14,7 @@ Please pick something while reading :)
|
||||||
until the command completes which is bad.
|
until the command completes which is bad.
|
||||||
|
|
||||||
Implementation idea:
|
Implementation idea:
|
||||||
|
|
||||||
- dwc core implements a demultiplexing irq chip for interrupts per
|
- dwc core implements a demultiplexing irq chip for interrupts per
|
||||||
endpoint. The interrupt numbers are allocated during probe and belong
|
endpoint. The interrupt numbers are allocated during probe and belong
|
||||||
to the device. If MSI provides per-endpoint interrupt this dummy
|
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()
|
- dwc3_send_gadget_ep_cmd() will sleep in wait_for_completion_timeout()
|
||||||
until the command completes.
|
until the command completes.
|
||||||
- the interrupt handler is split into the following pieces:
|
- the interrupt handler is split into the following pieces:
|
||||||
|
|
||||||
- primary handler of the device
|
- primary handler of the device
|
||||||
goes through every event and calls generic_handle_irq() for event
|
goes through every event and calls generic_handle_irq() for event
|
||||||
it. On return from generic_handle_irq() in acknowledges the event
|
it. On return from generic_handle_irq() in acknowledges the event
|
||||||
|
@ -40,6 +47,7 @@ Please pick something while reading :)
|
||||||
for command completion.
|
for command completion.
|
||||||
|
|
||||||
Latency:
|
Latency:
|
||||||
|
|
||||||
There should be no increase in latency since the interrupt-thread has a
|
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
|
high priority and will be run before an average task in user land
|
||||||
(except the user changed priorities).
|
(except the user changed priorities).
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
===========
|
||||||
|
EHCI driver
|
||||||
|
===========
|
||||||
|
|
||||||
27-Dec-2002
|
27-Dec-2002
|
||||||
|
|
||||||
The EHCI driver is used to talk to high speed USB 2.0 devices using
|
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>
|
<dbrownell@users.sourceforge.net>
|
||||||
|
|
||||||
|
|
||||||
FUNCTIONALITY
|
Functionality
|
||||||
|
=============
|
||||||
|
|
||||||
This driver is regularly tested on x86 hardware, and has also been
|
This driver is regularly tested on x86 hardware, and has also been
|
||||||
used on PPC hardware so big/little endianness issues should be gone.
|
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.
|
systems with interesting DMA mapping issues.
|
||||||
|
|
||||||
Transfer Types
|
Transfer Types
|
||||||
|
--------------
|
||||||
|
|
||||||
At this writing the driver should comfortably handle all control, bulk,
|
At this writing the driver should comfortably handle all control, bulk,
|
||||||
and interrupt transfers, including requests to USB 1.1 devices through
|
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.
|
most USB audio and video devices can't be connected to high speed buses.
|
||||||
|
|
||||||
Driver Behavior
|
Driver Behavior
|
||||||
|
---------------
|
||||||
|
|
||||||
Transfers of all types can be queued. This means that control transfers
|
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
|
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.
|
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)
|
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
|
# modprobe ehci-hcd
|
||||||
|
|
||||||
and remove it by:
|
and remove it by::
|
||||||
|
|
||||||
# rmmod ehci-hcd
|
# 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
|
debugging support, you'll see three files in the "sysfs" directory for
|
||||||
any EHCI controller:
|
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
|
and bulk transfers. Shows each active qh and the qtds
|
||||||
pending, usually one qtd per urb. (Look at it with
|
pending, usually one qtd per urb. (Look at it with
|
||||||
usb-storage doing disk I/O; watch the request queues!)
|
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.
|
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.
|
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.
|
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
|
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
|
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.
|
approach the quoted 480 MBit/sec transfer rate.
|
||||||
|
|
||||||
Hardware Performance
|
Hardware Performance
|
||||||
|
--------------------
|
||||||
|
|
||||||
At this writing, individual USB 2.0 devices tend to max out at around
|
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;
|
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).
|
it completed in less than 250 usec (depending on transfer size).
|
||||||
|
|
||||||
Software Performance
|
Software Performance
|
||||||
|
--------------------
|
||||||
|
|
||||||
To get even 20 MByte/sec transfer rates, Linux-USB device drivers will
|
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,
|
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.
|
help make high speed transfers run as fast as they can.
|
||||||
|
|
||||||
|
|
||||||
TBD: Interrupt and ISO transfer performance issues. Those periodic
|
TBD:
|
||||||
transfers are fully scheduled, so the main issue is likely to be how
|
Interrupt and ISO transfer performance issues. Those periodic
|
||||||
to trigger "high bandwidth" modes.
|
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
|
TBD:
|
||||||
through sysfs uframe_periodic_max parameter. Describe that.
|
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
|
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
|
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
|
One can imagine a gadget that has an Ethernet, MTP and HID interfaces
|
||||||
where the last two are implemented via FunctionFS. On user space
|
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
|
$ insmod g_ffs.ko idVendor=<ID> iSerialNumber=<string> functions=mtp,hid
|
||||||
$ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
|
$ mkdir /dev/ffs-mtp && mount -t functionfs mtp /dev/ffs-mtp
|
||||||
$ ( cd /dev/ffs-mtp && mtp-daemon ) &
|
$ ( cd /dev/ffs-mtp && mtp-daemon ) &
|
||||||
$ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
|
$ mkdir /dev/ffs-hid && mount -t functionfs hid /dev/ffs-hid
|
||||||
$ ( cd /dev/ffs-hid && hid-daemon ) &
|
$ ( cd /dev/ffs-hid && hid-daemon ) &
|
||||||
|
|
||||||
On kernel level the gadget checks ffs_data->dev_name to identify
|
On kernel level the gadget checks ffs_data->dev_name to identify
|
||||||
whether it's FunctionFS designed for MTP ("mtp") or HID ("hid").
|
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
|
Conversely, the gadget is unregistered after the first USB function
|
||||||
closes its endpoints.
|
closes its endpoints.
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,32 @@
|
||||||
|
==============
|
||||||
|
Gadget Testing
|
||||||
|
==============
|
||||||
|
|
||||||
This file summarizes information on basic testing of USB functions
|
This file summarizes information on basic testing of USB functions
|
||||||
provided by gadgets.
|
provided by gadgets.
|
||||||
|
|
||||||
1. ACM function
|
.. contents
|
||||||
2. ECM function
|
|
||||||
3. ECM subset function
|
1. ACM function
|
||||||
4. EEM function
|
2. ECM function
|
||||||
5. FFS function
|
3. ECM subset function
|
||||||
6. HID function
|
4. EEM function
|
||||||
7. LOOPBACK function
|
5. FFS function
|
||||||
8. MASS STORAGE function
|
6. HID function
|
||||||
9. MIDI function
|
7. LOOPBACK function
|
||||||
10. NCM function
|
8. MASS STORAGE function
|
||||||
11. OBEX function
|
9. MIDI function
|
||||||
12. PHONET function
|
10. NCM function
|
||||||
13. RNDIS function
|
11. OBEX function
|
||||||
14. SERIAL function
|
12. PHONET function
|
||||||
15. SOURCESINK function
|
13. RNDIS function
|
||||||
16. UAC1 function (legacy implementation)
|
14. SERIAL function
|
||||||
17. UAC2 function
|
15. SOURCESINK function
|
||||||
18. UVC function
|
16. UAC1 function (legacy implementation)
|
||||||
19. PRINTER function
|
17. UAC2 function
|
||||||
20. UAC1 function (new API)
|
18. UVC function
|
||||||
|
19. PRINTER function
|
||||||
|
20. UAC1 function (new API)
|
||||||
|
|
||||||
|
|
||||||
1. ACM function
|
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
|
Testing the ACM function
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
On the host: cat > /dev/ttyACM<X>
|
On the host::
|
||||||
On the device : cat /dev/ttyGS<Y>
|
|
||||||
|
cat > /dev/ttyACM<X>
|
||||||
|
|
||||||
|
On the device::
|
||||||
|
|
||||||
|
cat /dev/ttyGS<Y>
|
||||||
|
|
||||||
then the other way round
|
then the other way round
|
||||||
|
|
||||||
On the device: cat > /dev/ttyGS<Y>
|
On the device::
|
||||||
On the host: cat /dev/ttyACM<X>
|
|
||||||
|
cat > /dev/ttyGS<Y>
|
||||||
|
|
||||||
|
On the host::
|
||||||
|
|
||||||
|
cat /dev/ttyACM<X>
|
||||||
|
|
||||||
2. ECM function
|
2. ECM function
|
||||||
===============
|
===============
|
||||||
|
@ -63,13 +79,15 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "ecm".
|
The function name to use when creating the function directory is "ecm".
|
||||||
The ECM function provides these attributes in its function directory:
|
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
|
function instance
|
||||||
qmult - queue length multiplier for high and super speed
|
qmult queue length multiplier for high and super speed
|
||||||
host_addr - MAC address of host's end of this
|
host_addr MAC address of host's end of this
|
||||||
Ethernet over USB link
|
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
|
Ethernet over USB link
|
||||||
|
=============== ==================================================
|
||||||
|
|
||||||
and after creating the functions/ecm.<instance name> they contain default
|
and after creating the functions/ecm.<instance name> they contain default
|
||||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
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:
|
Configure IP addresses of the device and the host. Then:
|
||||||
|
|
||||||
On the device: ping <host's IP>
|
On the device::
|
||||||
On the host: ping <device's IP>
|
|
||||||
|
ping <host's IP>
|
||||||
|
|
||||||
|
On the host::
|
||||||
|
|
||||||
|
ping <device's IP>
|
||||||
|
|
||||||
3. ECM subset function
|
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 function name to use when creating the function directory is "geth".
|
||||||
The ECM subset function provides these attributes in its function directory:
|
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
|
function instance
|
||||||
qmult - queue length multiplier for high and super speed
|
qmult queue length multiplier for high and super speed
|
||||||
host_addr - MAC address of host's end of this
|
host_addr MAC address of host's end of this
|
||||||
Ethernet over USB link
|
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
|
Ethernet over USB link
|
||||||
|
=============== ==================================================
|
||||||
|
|
||||||
and after creating the functions/ecm.<instance name> they contain default
|
and after creating the functions/ecm.<instance name> they contain default
|
||||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
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:
|
Configure IP addresses of the device and the host. Then:
|
||||||
|
|
||||||
On the device: ping <host's IP>
|
On the device::
|
||||||
On the host: ping <device's IP>
|
|
||||||
|
ping <host's IP>
|
||||||
|
|
||||||
|
On the host::
|
||||||
|
|
||||||
|
ping <device's IP>
|
||||||
|
|
||||||
4. EEM function
|
4. EEM function
|
||||||
===============
|
===============
|
||||||
|
@ -129,13 +159,15 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "eem".
|
The function name to use when creating the function directory is "eem".
|
||||||
The EEM function provides these attributes in its function directory:
|
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
|
function instance
|
||||||
qmult - queue length multiplier for high and super speed
|
qmult queue length multiplier for high and super speed
|
||||||
host_addr - MAC address of host's end of this
|
host_addr MAC address of host's end of this
|
||||||
Ethernet over USB link
|
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
|
Ethernet over USB link
|
||||||
|
=============== ==================================================
|
||||||
|
|
||||||
and after creating the functions/eem.<instance name> they contain default
|
and after creating the functions/eem.<instance name> they contain default
|
||||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
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:
|
Configure IP addresses of the device and the host. Then:
|
||||||
|
|
||||||
On the device: ping <host's IP>
|
On the device::
|
||||||
On the host: ping <device's IP>
|
|
||||||
|
ping <host's IP>
|
||||||
|
|
||||||
|
On the host::
|
||||||
|
|
||||||
|
ping <device's IP>
|
||||||
|
|
||||||
5. FFS function
|
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 device: start the function's userspace daemon, enable the gadget
|
||||||
|
|
||||||
On the host: use the USB function provided by the device
|
On the host: use the USB function provided by the device
|
||||||
|
|
||||||
6. HID function
|
6. HID function
|
||||||
|
@ -185,39 +223,43 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "hid".
|
The function name to use when creating the function directory is "hid".
|
||||||
The HID function provides these attributes in its function directory:
|
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>
|
passed with /dev/hidg<X>
|
||||||
report_length - HID report length
|
report_length HID report length
|
||||||
subclass - HID subclass to use
|
subclass HID subclass to use
|
||||||
|
=============== ===========================================
|
||||||
|
|
||||||
For a keyboard the protocol and the subclass are 1, the report_length is 8,
|
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
|
$ hd my_report_desc
|
||||||
00000000 05 01 09 06 a1 01 05 07 19 e0 29 e7 15 00 25 01 |..........)...%.|
|
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.|
|
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.....|
|
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...|
|
00000030 75 08 15 00 25 65 05 07 19 00 29 65 81 00 c0 |u...%e....)e...|
|
||||||
0000003f
|
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
|
Testing the HID function
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
Device:
|
Device:
|
||||||
|
|
||||||
- create the gadget
|
- create the gadget
|
||||||
- connect the gadget to a host, preferably not the one used
|
- 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.
|
- 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:
|
Host:
|
||||||
|
|
||||||
- observe the keystrokes from the gadget
|
- observe the keystrokes from the gadget
|
||||||
|
|
||||||
7. LOOPBACK function
|
7. LOOPBACK function
|
||||||
|
@ -231,13 +273,16 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "Loopback".
|
The function name to use when creating the function directory is "Loopback".
|
||||||
The LOOPBACK function provides these attributes in its function directory:
|
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
|
Testing the LOOPBACK function
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
device: run the gadget
|
device: run the gadget
|
||||||
|
|
||||||
host: test-usb (tools/usb/testusb.c)
|
host: test-usb (tools/usb/testusb.c)
|
||||||
|
|
||||||
8. MASS STORAGE function
|
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:
|
The MASS STORAGE function provides these attributes in its directory:
|
||||||
files:
|
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
|
Disabled on some USB devices known not to work
|
||||||
correctly. You should set it to true.
|
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
|
are 2..4. Available only if
|
||||||
CONFIG_USB_GADGET_DEBUG_FILES is set.
|
CONFIG_USB_GADGET_DEBUG_FILES is set.
|
||||||
|
=============== ==============================================
|
||||||
|
|
||||||
and a default lun.0 directory corresponding to SCSI LUN #0.
|
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
|
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
|
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:
|
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.
|
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
|
read-only. This is implied if CD-ROM emulation
|
||||||
is enabled as well as when it was impossible
|
is enabled as well as when it was impossible
|
||||||
to open "filename" in R/W mode.
|
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.
|
being removable.
|
||||||
cdrom - Flag specifying that LUN shall be reported as
|
cdrom Flag specifying that LUN shall be reported as
|
||||||
being a CD-ROM.
|
being a CD-ROM.
|
||||||
nofua - Flag specifying that FUA flag
|
nofua Flag specifying that FUA flag
|
||||||
in SCSI WRITE(10,12)
|
in SCSI WRITE(10,12)
|
||||||
|
=============== ==============================================
|
||||||
|
|
||||||
Testing the MASS STORAGE function
|
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 function name to use when creating the function directory is "midi".
|
||||||
The MIDI function provides these attributes in its function directory:
|
The MIDI function provides these attributes in its function directory:
|
||||||
|
|
||||||
buflen - MIDI buffer length
|
=============== ====================================
|
||||||
id - ID string for the USB MIDI adapter
|
buflen MIDI buffer length
|
||||||
in_ports - number of MIDI input ports
|
id ID string for the USB MIDI adapter
|
||||||
index - index value for the USB MIDI adapter
|
in_ports number of MIDI input ports
|
||||||
out_ports - number of MIDI output ports
|
index index value for the USB MIDI adapter
|
||||||
qlen - USB read request queue length
|
out_ports number of MIDI output ports
|
||||||
|
qlen USB read request queue length
|
||||||
|
=============== ====================================
|
||||||
|
|
||||||
Testing the MIDI function
|
Testing the MIDI function
|
||||||
-------------------------
|
-------------------------
|
||||||
|
@ -317,60 +368,63 @@ Testing the MIDI function
|
||||||
There are two cases: playing a mid from the gadget to
|
There are two cases: playing a mid from the gadget to
|
||||||
the host and playing a mid from the host to the gadget.
|
the host and playing a mid from the host to the gadget.
|
||||||
|
|
||||||
1) Playing a mid from the gadget to the host
|
1) Playing a mid from the gadget to the host:
|
||||||
host)
|
|
||||||
|
|
||||||
$ arecordmidi -l
|
host::
|
||||||
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
|
|
||||||
|
|
||||||
gadget)
|
$ 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
|
||||||
|
|
||||||
$ aplaymidi -l
|
gadget::
|
||||||
Port Client name Port name
|
|
||||||
20:0 f_midi f_midi
|
|
||||||
|
|
||||||
$ aplaymidi -p 20:0 to_host.mid
|
$ aplaymidi -l
|
||||||
|
Port Client name Port name
|
||||||
|
20:0 f_midi f_midi
|
||||||
|
|
||||||
|
$ aplaymidi -p 20:0 to_host.mid
|
||||||
|
|
||||||
2) Playing a mid from the host to the gadget
|
2) Playing a mid from the host to the gadget
|
||||||
gadget)
|
|
||||||
|
|
||||||
$ arecordmidi -l
|
gadget::
|
||||||
Port Client name Port name
|
|
||||||
20:0 f_midi f_midi
|
|
||||||
|
|
||||||
$ arecordmidi -p 20:0 from_host.mid
|
$ arecordmidi -l
|
||||||
|
Port Client name Port name
|
||||||
|
20:0 f_midi f_midi
|
||||||
|
|
||||||
host)
|
$ arecordmidi -p 20:0 from_host.mid
|
||||||
|
|
||||||
$ aplaymidi -l
|
host::
|
||||||
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 -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
|
||||||
|
|
||||||
The from_gadget.mid should sound identical to the to_host.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.
|
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
|
Port Client name Port name
|
||||||
14:0 Midi Through Midi Through Port-0
|
14:0 Midi Through Midi Through Port-0
|
||||||
24:0 MIDI Gadget MIDI Gadget MIDI 1
|
24:0 MIDI Gadget MIDI Gadget MIDI 1
|
||||||
128:0 TiMidity TiMidity port 0
|
128:0 TiMidity TiMidity port 0
|
||||||
128:1 TiMidity TiMidity port 1
|
128:1 TiMidity TiMidity port 1
|
||||||
128:2 TiMidity TiMidity port 2
|
128:2 TiMidity TiMidity port 2
|
||||||
128:3 TiMidity TiMidity port 3
|
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,
|
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
|
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 function name to use when creating the function directory is "ncm".
|
||||||
The NCM function provides these attributes in its function directory:
|
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
|
function instance
|
||||||
qmult - queue length multiplier for high and super speed
|
qmult queue length multiplier for high and super speed
|
||||||
host_addr - MAC address of host's end of this
|
host_addr MAC address of host's end of this
|
||||||
Ethernet over USB link
|
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
|
Ethernet over USB link
|
||||||
|
=============== ==================================================
|
||||||
|
|
||||||
and after creating the functions/ncm.<instance name> they contain default
|
and after creating the functions/ncm.<instance name> they contain default
|
||||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
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:
|
Configure IP addresses of the device and the host. Then:
|
||||||
|
|
||||||
On the device: ping <host's IP>
|
On the device::
|
||||||
On the host: ping <device's IP>
|
|
||||||
|
ping <host's IP>
|
||||||
|
|
||||||
|
On the host::
|
||||||
|
|
||||||
|
ping <device's IP>
|
||||||
|
|
||||||
11. OBEX function
|
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
|
Testing the OBEX function
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
On device: seriald -f /dev/ttyGS<Y> -s 1024
|
On device::
|
||||||
On host: serialc -v <vendorID> -p <productID> -i<interface#> -a1 -s1024 \
|
|
||||||
-t<out endpoint addr> -r<in endpoint addr>
|
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:
|
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
|
12. PHONET function
|
||||||
===================
|
===================
|
||||||
|
@ -448,8 +514,10 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "phonet".
|
The function name to use when creating the function directory is "phonet".
|
||||||
The PHONET function provides just one attribute in its function directory:
|
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
|
function instance
|
||||||
|
=============== ==================================================
|
||||||
|
|
||||||
Testing the PHONET function
|
Testing the PHONET function
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -464,41 +532,41 @@ These tools are required:
|
||||||
|
|
||||||
git://git.gitorious.org/meego-cellular/phonet-utils.git
|
git://git.gitorious.org/meego-cellular/phonet-utils.git
|
||||||
|
|
||||||
On the host:
|
On the host::
|
||||||
|
|
||||||
$ ./phonet -a 0x10 -i usbpn0
|
$ ./phonet -a 0x10 -i usbpn0
|
||||||
$ ./pnroute add 0x6c usbpn0
|
$ ./pnroute add 0x6c usbpn0
|
||||||
$./pnroute add 0x10 usbpn0
|
$./pnroute add 0x10 usbpn0
|
||||||
$ ifconfig usbpn0 up
|
$ ifconfig usbpn0 up
|
||||||
|
|
||||||
On the device:
|
On the device::
|
||||||
|
|
||||||
$ ./phonet -a 0x6c -i upnlink0
|
$ ./phonet -a 0x6c -i upnlink0
|
||||||
$ ./pnroute add 0x10 upnlink0
|
$ ./pnroute add 0x10 upnlink0
|
||||||
$ ifconfig upnlink0 up
|
$ 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.
|
As a result some data should be sent from host to device.
|
||||||
Then the other way round:
|
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
|
13. RNDIS function
|
||||||
==================
|
==================
|
||||||
|
@ -511,13 +579,15 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "rndis".
|
The function name to use when creating the function directory is "rndis".
|
||||||
The RNDIS function provides these attributes in its function directory:
|
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
|
function instance
|
||||||
qmult - queue length multiplier for high and super speed
|
qmult queue length multiplier for high and super speed
|
||||||
host_addr - MAC address of host's end of this
|
host_addr MAC address of host's end of this
|
||||||
Ethernet over USB link
|
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
|
Ethernet over USB link
|
||||||
|
=============== ==================================================
|
||||||
|
|
||||||
and after creating the functions/rndis.<instance name> they contain default
|
and after creating the functions/rndis.<instance name> they contain default
|
||||||
values: qmult is 5, dev_addr and host_addr are randomly selected.
|
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:
|
Configure IP addresses of the device and the host. Then:
|
||||||
|
|
||||||
On the device: ping <host's IP>
|
On the device::
|
||||||
On the host: ping <device's IP>
|
|
||||||
|
ping <host's IP>
|
||||||
|
|
||||||
|
On the host::
|
||||||
|
|
||||||
|
ping <device's IP>
|
||||||
|
|
||||||
14. SERIAL function
|
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
|
Testing the SERIAL function
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
On host: insmod usbserial
|
On host::
|
||||||
echo VID PID >/sys/bus/usb-serial/drivers/generic/new_id
|
|
||||||
On host: cat > /dev/ttyUSB<X>
|
insmod usbserial
|
||||||
On target: cat /dev/ttyGS<Y>
|
echo VID PID >/sys/bus/usb-serial/drivers/generic/new_id
|
||||||
|
|
||||||
|
On host::
|
||||||
|
|
||||||
|
cat > /dev/ttyUSB<X>
|
||||||
|
|
||||||
|
On target::
|
||||||
|
|
||||||
|
cat /dev/ttyGS<Y>
|
||||||
|
|
||||||
then the other way round
|
then the other way round
|
||||||
|
|
||||||
On target: cat > /dev/ttyGS<Y>
|
On target::
|
||||||
On host: cat /dev/ttyUSB<X>
|
|
||||||
|
cat > /dev/ttyGS<Y>
|
||||||
|
|
||||||
|
On host::
|
||||||
|
|
||||||
|
cat /dev/ttyUSB<X>
|
||||||
|
|
||||||
15. SOURCESINK function
|
15. SOURCESINK function
|
||||||
=======================
|
=======================
|
||||||
|
@ -574,24 +662,27 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "SourceSink".
|
The function name to use when creating the function directory is "SourceSink".
|
||||||
The SOURCESINK function provides these attributes in its function directory:
|
The SOURCESINK function provides these attributes in its function directory:
|
||||||
|
|
||||||
pattern - 0 (all zeros), 1 (mod63), 2 (none)
|
=============== ==================================
|
||||||
isoc_interval - 1..16
|
pattern 0 (all zeros), 1 (mod63), 2 (none)
|
||||||
isoc_maxpacket - 0 - 1023 (fs), 0 - 1024 (hs/ss)
|
isoc_interval 1..16
|
||||||
isoc_mult - 0..2 (hs/ss only)
|
isoc_maxpacket 0 - 1023 (fs), 0 - 1024 (hs/ss)
|
||||||
isoc_maxburst - 0..15 (ss only)
|
isoc_mult 0..2 (hs/ss only)
|
||||||
bulk_buflen - buffer length
|
isoc_maxburst 0..15 (ss only)
|
||||||
bulk_qlen - depth of queue for bulk
|
bulk_buflen buffer length
|
||||||
iso_qlen - depth of queue for iso
|
bulk_qlen depth of queue for bulk
|
||||||
|
iso_qlen depth of queue for iso
|
||||||
|
=============== ==================================
|
||||||
|
|
||||||
Testing the SOURCESINK function
|
Testing the SOURCESINK function
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
device: run the gadget
|
device: run the gadget
|
||||||
|
|
||||||
host: test-usb (tools/usb/testusb.c)
|
host: test-usb (tools/usb/testusb.c)
|
||||||
|
|
||||||
|
|
||||||
16. UAC1 function (legacy implementation)
|
16. UAC1 function (legacy implementation)
|
||||||
=================
|
=========================================
|
||||||
|
|
||||||
The function is provided by usb_f_uac1_legacy.ko module.
|
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".
|
is "uac1_legacy".
|
||||||
The uac1 function provides these attributes in its function directory:
|
The uac1 function provides these attributes in its function directory:
|
||||||
|
|
||||||
audio_buf_size - audio buffer size
|
=============== ====================================
|
||||||
fn_cap - capture pcm device file name
|
audio_buf_size audio buffer size
|
||||||
fn_cntl - control device file name
|
fn_cap capture pcm device file name
|
||||||
fn_play - playback pcm device file name
|
fn_cntl control device file name
|
||||||
req_buf_size - ISO OUT endpoint request buffer size
|
fn_play playback pcm device file name
|
||||||
req_count - ISO OUT endpoint request count
|
req_buf_size ISO OUT endpoint request buffer size
|
||||||
|
req_count ISO OUT endpoint request count
|
||||||
|
=============== ====================================
|
||||||
|
|
||||||
The attributes have sane default values.
|
The attributes have sane default values.
|
||||||
|
|
||||||
|
@ -615,7 +708,10 @@ Testing the UAC1 function
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
device: run the gadget
|
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
|
17. UAC2 function
|
||||||
=================
|
=================
|
||||||
|
@ -628,14 +724,16 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "uac2".
|
The function name to use when creating the function directory is "uac2".
|
||||||
The uac2 function provides these attributes in its function directory:
|
The uac2 function provides these attributes in its function directory:
|
||||||
|
|
||||||
c_chmask - capture channel mask
|
=============== ====================================================
|
||||||
c_srate - capture sampling rate
|
c_chmask capture channel mask
|
||||||
c_ssize - capture sample size (bytes)
|
c_srate capture sampling rate
|
||||||
p_chmask - playback channel mask
|
c_ssize capture sample size (bytes)
|
||||||
p_srate - playback sampling rate
|
p_chmask playback channel mask
|
||||||
p_ssize - playback sample size (bytes)
|
p_srate playback sampling rate
|
||||||
req_number - the number of pre-allocated request for both capture
|
p_ssize playback sample size (bytes)
|
||||||
and playback
|
req_number the number of pre-allocated request for both capture
|
||||||
|
and playback
|
||||||
|
=============== ====================================================
|
||||||
|
|
||||||
The attributes have sane default values.
|
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
|
This function does not require real hardware support, it just
|
||||||
sends a stream of audio data to/from the host. In order to
|
sends a stream of audio data to/from the host. In order to
|
||||||
actually hear something at the device side, a command similar
|
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 | \
|
$ arecord -f dat -t wav -D hw:CARD=UAC2Gadget,DEV=0 | \
|
||||||
aplay -D default:CARD=OdroidU3
|
aplay -D default:CARD=OdroidU3
|
||||||
|
|
||||||
18. UVC function
|
18. UVC function
|
||||||
================
|
================
|
||||||
|
@ -668,66 +766,73 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "uvc".
|
The function name to use when creating the function directory is "uvc".
|
||||||
The uvc function provides these attributes in its function directory:
|
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_interval interval for polling endpoint for data transfers
|
||||||
streaming_maxpacket - maximum packet size this endpoint is capable of
|
streaming_maxburst bMaxBurst for super speed companion descriptor
|
||||||
sending or receiving when this configuration is
|
streaming_maxpacket maximum packet size this endpoint is capable of
|
||||||
selected
|
sending or receiving when this configuration is
|
||||||
|
selected
|
||||||
|
=================== ================================================
|
||||||
|
|
||||||
There are also "control" and "streaming" subdirectories, each of which contain
|
There are also "control" and "streaming" subdirectories, each of which contain
|
||||||
a number of their subdirectories. There are some sane defaults provided, but
|
a number of their subdirectories. There are some sane defaults provided, but
|
||||||
the user must provide the following:
|
the user must provide the following:
|
||||||
|
|
||||||
control header - create in control/header, link from control/class/fs
|
================== ====================================================
|
||||||
and/or control/class/ss
|
control header create in control/header, link from control/class/fs
|
||||||
streaming header - create in streaming/header, link from
|
and/or control/class/ss
|
||||||
streaming/class/fs and/or streaming/class/hs and/or
|
streaming header create in streaming/header, link from
|
||||||
streaming/class/ss
|
streaming/class/fs and/or streaming/class/hs and/or
|
||||||
format description - create in streaming/mjpeg and/or
|
streaming/class/ss
|
||||||
streaming/uncompressed
|
format description create in streaming/mjpeg and/or
|
||||||
frame description - create in streaming/mjpeg/<format> and/or in
|
streaming/uncompressed
|
||||||
streaming/uncompressed/<format>
|
frame description create in streaming/mjpeg/<format> and/or in
|
||||||
|
streaming/uncompressed/<format>
|
||||||
|
================== ====================================================
|
||||||
|
|
||||||
Each frame description contains frame interval specification, and each
|
Each frame description contains frame interval specification, and each
|
||||||
such specification consists of a number of lines with an inverval value
|
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
|
# mkdir functions/uvc.usb0/control/header/h
|
||||||
# cd functions/uvc.usb0/control/
|
# cd functions/uvc.usb0/control/
|
||||||
# ln -s header/h class/fs
|
# ln -s header/h class/fs
|
||||||
# ln -s header/h class/ss
|
# ln -s header/h class/ss
|
||||||
# mkdir -p functions/uvc.usb0/streaming/uncompressed/u/360p
|
# mkdir -p functions/uvc.usb0/streaming/uncompressed/u/360p
|
||||||
# cat <<EOF > functions/uvc.usb0/streaming/uncompressed/u/360p/dwFrameInterval
|
# cat <<EOF > functions/uvc.usb0/streaming/uncompressed/u/360p/dwFrameInterval
|
||||||
666666
|
666666
|
||||||
1000000
|
1000000
|
||||||
5000000
|
5000000
|
||||||
EOF
|
EOF
|
||||||
# cd $GADGET_CONFIGFS_ROOT
|
# cd $GADGET_CONFIGFS_ROOT
|
||||||
# mkdir functions/uvc.usb0/streaming/header/h
|
# mkdir functions/uvc.usb0/streaming/header/h
|
||||||
# cd functions/uvc.usb0/streaming/header/h
|
# cd functions/uvc.usb0/streaming/header/h
|
||||||
# ln -s ../../uncompressed/u
|
# ln -s ../../uncompressed/u
|
||||||
# cd ../../class/fs
|
# cd ../../class/fs
|
||||||
# ln -s ../../header/h
|
# ln -s ../../header/h
|
||||||
# cd ../../class/hs
|
# cd ../../class/hs
|
||||||
# ln -s ../../header/h
|
# ln -s ../../header/h
|
||||||
# cd ../../class/ss
|
# cd ../../class/ss
|
||||||
# ln -s ../../header/h
|
# ln -s ../../header/h
|
||||||
|
|
||||||
|
|
||||||
Testing the UVC function
|
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:
|
where uvc-gadget is this program:
|
||||||
http://git.ideasonboard.org/uvc-gadget.git
|
http://git.ideasonboard.org/uvc-gadget.git
|
||||||
|
|
||||||
with these patches:
|
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
|
19. PRINTER function
|
||||||
====================
|
====================
|
||||||
|
@ -740,16 +845,19 @@ Function-specific configfs interface
|
||||||
The function name to use when creating the function directory is "printer".
|
The function name to use when creating the function directory is "printer".
|
||||||
The printer function provides these attributes in its function directory:
|
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
|
Testing the PRINTER function
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
The most basic testing:
|
The most basic testing:
|
||||||
|
|
||||||
device: run the gadget
|
device: run the gadget::
|
||||||
# ls -l /devices/virtual/usb_printer_gadget/
|
|
||||||
|
# ls -l /devices/virtual/usb_printer_gadget/
|
||||||
|
|
||||||
should show g_printer<number>.
|
should show g_printer<number>.
|
||||||
|
|
||||||
|
@ -761,23 +869,28 @@ If udev is active, then e.g. /dev/usb/lp0 should appear.
|
||||||
|
|
||||||
host->device transmission:
|
host->device transmission:
|
||||||
|
|
||||||
device:
|
device::
|
||||||
# cat /dev/g_printer<number>
|
|
||||||
host:
|
|
||||||
# cat > /dev/usb/lp0
|
|
||||||
|
|
||||||
device->host transmission:
|
# cat /dev/g_printer<number>
|
||||||
|
|
||||||
# cat > /dev/g_printer<number>
|
host::
|
||||||
host:
|
|
||||||
# cat /dev/usb/lp0
|
# 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
|
More advanced testing can be done with the prn_example
|
||||||
described in Documentation/usb/gadget_printer.txt.
|
described in Documentation/usb/gadget_printer.txt.
|
||||||
|
|
||||||
|
|
||||||
20. UAC1 function (virtual ALSA card, using u_audio API)
|
20. UAC1 function (virtual ALSA card, using u_audio API)
|
||||||
=================
|
========================================================
|
||||||
|
|
||||||
The function is provided by usb_f_uac1.ko module.
|
The function is provided by usb_f_uac1.ko module.
|
||||||
It will create a virtual ALSA card and the audio streams are simply
|
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 function name to use when creating the function directory is "uac1".
|
||||||
The uac1 function provides these attributes in its function directory:
|
The uac1 function provides these attributes in its function directory:
|
||||||
|
|
||||||
c_chmask - capture channel mask
|
========== ====================================================
|
||||||
c_srate - capture sampling rate
|
c_chmask capture channel mask
|
||||||
c_ssize - capture sample size (bytes)
|
c_srate capture sampling rate
|
||||||
p_chmask - playback channel mask
|
c_ssize capture sample size (bytes)
|
||||||
p_srate - playback sampling rate
|
p_chmask playback channel mask
|
||||||
p_ssize - playback sample size (bytes)
|
p_srate playback sampling rate
|
||||||
req_number - the number of pre-allocated request for both capture
|
p_ssize playback sample size (bytes)
|
||||||
and playback
|
req_number the number of pre-allocated request for both capture
|
||||||
|
and playback
|
||||||
|
========== ====================================================
|
||||||
|
|
||||||
The attributes have sane default values.
|
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
|
This function does not require real hardware support, it just
|
||||||
sends a stream of audio data to/from the host. In order to
|
sends a stream of audio data to/from the host. In order to
|
||||||
actually hear something at the device side, a command similar
|
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 | \
|
$ arecord -f dat -t wav -D hw:CARD=UAC1Gadget,DEV=0 | \
|
||||||
aplay -D default:CARD=OdroidU3
|
aplay -D default:CARD=OdroidU3
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
|
============================================
|
||||||
|
Linux USB gadget configured through configfs
|
||||||
|
============================================
|
||||||
|
|
||||||
|
|
||||||
|
25th April 2013
|
||||||
|
|
||||||
Linux USB gadget configured through configfs
|
|
||||||
|
|
||||||
|
|
||||||
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
|
Creating a gadget means deciding what configurations there will be
|
||||||
and which functions each configuration will provide.
|
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.
|
for the purpose of telling the kernel about the above mentioned decision.
|
||||||
This document is about how to do it.
|
This document is about how to do it.
|
||||||
|
|
||||||
|
@ -51,44 +49,46 @@ Usage
|
||||||
made available through configfs can be seen here:
|
made available through configfs can be seen here:
|
||||||
http://www.spinics.net/lists/linux-usb/msg76388.html)
|
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
|
where CONFIGFS_HOME is the mount point for configfs
|
||||||
|
|
||||||
1. Creating the gadgets
|
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 <VID> > idVendor
|
||||||
$ echo <PID> > idProduct
|
$ echo <PID> > idProduct
|
||||||
|
|
||||||
A gadget also needs its serial number, manufacturer and product strings.
|
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
|
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 <serial number> > strings/0x409/serialnumber
|
||||||
$ echo <manufacturer> > strings/0x409/manufacturer
|
$ echo <manufacturer> > strings/0x409/manufacturer
|
||||||
$ echo <product> > strings/0x409/product
|
$ echo <product> > strings/0x409/product
|
||||||
|
|
||||||
2. Creating the configurations
|
2. Creating the configurations
|
||||||
------------------------------
|
------------------------------
|
||||||
|
@ -99,43 +99,43 @@ directories must be created:
|
||||||
$ mkdir configs/<name>.<number>
|
$ mkdir configs/<name>.<number>
|
||||||
|
|
||||||
where <name> can be any string which is legal in a filesystem and the
|
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
|
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
|
3. Creating the functions
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
The gadget will provide some functions, for each function its corresponding
|
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
|
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
|
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
|
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
|
configurations specified and a number of functions available. What remains
|
||||||
is specifying which function is available in which configuration (the same
|
is specifying which function is available in which configuration (the same
|
||||||
function can be used in multiple configurations). This is achieved with
|
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
|
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
|
All the above steps serve the purpose of composing the gadget of
|
||||||
configurations and functions.
|
configurations and functions.
|
||||||
|
|
||||||
An example directory structure might look like this:
|
An example directory structure might look like this::
|
||||||
|
|
||||||
.
|
.
|
||||||
./strings
|
./strings
|
||||||
./strings/0x409
|
./strings/0x409
|
||||||
./strings/0x409/serialnumber
|
./strings/0x409/serialnumber
|
||||||
./strings/0x409/product
|
./strings/0x409/product
|
||||||
./strings/0x409/manufacturer
|
./strings/0x409/manufacturer
|
||||||
./configs
|
./configs
|
||||||
./configs/c.1
|
./configs/c.1
|
||||||
./configs/c.1/ncm.usb0 -> ../../../../usb_gadget/g1/functions/ncm.usb0
|
./configs/c.1/ncm.usb0 -> ../../../../usb_gadget/g1/functions/ncm.usb0
|
||||||
./configs/c.1/strings
|
./configs/c.1/strings
|
||||||
./configs/c.1/strings/0x409
|
./configs/c.1/strings/0x409
|
||||||
./configs/c.1/strings/0x409/configuration
|
./configs/c.1/strings/0x409/configuration
|
||||||
./configs/c.1/bmAttributes
|
./configs/c.1/bmAttributes
|
||||||
./configs/c.1/MaxPower
|
./configs/c.1/MaxPower
|
||||||
./functions
|
./functions
|
||||||
./functions/ncm.usb0
|
./functions/ncm.usb0
|
||||||
./functions/ncm.usb0/ifname
|
./functions/ncm.usb0/ifname
|
||||||
./functions/ncm.usb0/qmult
|
./functions/ncm.usb0/qmult
|
||||||
./functions/ncm.usb0/host_addr
|
./functions/ncm.usb0/host_addr
|
||||||
./functions/ncm.usb0/dev_addr
|
./functions/ncm.usb0/dev_addr
|
||||||
./UDC
|
./UDC
|
||||||
./bcdUSB
|
./bcdUSB
|
||||||
./bcdDevice
|
./bcdDevice
|
||||||
./idProduct
|
./idProduct
|
||||||
./idVendor
|
./idVendor
|
||||||
./bMaxPacketSize0
|
./bMaxPacketSize0
|
||||||
./bDeviceProtocol
|
./bDeviceProtocol
|
||||||
./bDeviceSubClass
|
./bDeviceSubClass
|
||||||
./bDeviceClass
|
./bDeviceClass
|
||||||
|
|
||||||
|
|
||||||
Such a gadget must be finally enabled so that the USB host can enumerate it.
|
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/*
|
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
|
6. Disabling the gadget
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
$ echo "" > UDC
|
::
|
||||||
|
|
||||||
|
$ echo "" > UDC
|
||||||
|
|
||||||
7. Cleaning up
|
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
|
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 ..
|
$ cd ..
|
||||||
$ rmdir <gadget name>
|
$ rmdir <gadget name>
|
||||||
|
|
||||||
e.g.:
|
e.g.::
|
||||||
|
|
||||||
$ rmdir g1
|
$ rmdir g1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -305,16 +309,16 @@ configured elements. However, they are embedded in usage-specific
|
||||||
larger structures. In the picture below there is a "cs" which contains
|
larger structures. In the picture below there is a "cs" which contains
|
||||||
a config_item and an "sa" which contains a configfs_attribute.
|
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)
|
+--sa (file)
|
||||||
|
|
|
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
|
|
||||||
Whenever a user reads/writes the "sa" file, a function is called
|
Whenever a user reads/writes the "sa" file, a function is called
|
||||||
which accepts a struct config_item and a struct configfs_attribute.
|
which accepts a struct config_item and a struct configfs_attribute.
|
||||||
|
@ -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
|
from the buffer to the cs), but it is up to the implementer of the
|
||||||
two functions to decide what they actually do.
|
two functions to decide what they actually do.
|
||||||
|
|
||||||
typedef struct configured_structure cs;
|
::
|
||||||
typedef struct specific_attribute sa;
|
|
||||||
|
|
||||||
sa
|
typedef struct configured_structure cs;
|
||||||
+----------------------------------+
|
typedef struct specific_attribute sa;
|
||||||
cs | (*show)(cs *, buffer); |
|
|
||||||
+-----------------+ | (*store)(cs *, buffer, length); |
|
sa
|
||||||
| | | |
|
+----------------------------------+
|
||||||
| +-------------+ | | +------------------+ |
|
cs | (*show)(cs *, buffer); |
|
||||||
| | struct |-|----|------>|struct | |
|
+-----------------+ | (*store)(cs *, buffer, length); |
|
||||||
| | config_item | | | |configfs_attribute| |
|
| | | |
|
||||||
| +-------------+ | | +------------------+ |
|
| +-------------+ | | +------------------+ |
|
||||||
| | +----------------------------------+
|
| | struct |-|----|------>|struct | |
|
||||||
| data to be set | .
|
| | config_item | | | |configfs_attribute| |
|
||||||
| | .
|
| +-------------+ | | +------------------+ |
|
||||||
+-----------------+ .
|
| | +----------------------------------+
|
||||||
|
| data to be set | .
|
||||||
|
| | .
|
||||||
|
+-----------------+ .
|
||||||
|
|
||||||
The file names are decided by the config item/group designer, while
|
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
|
the directories in general can be named at will. A group can have
|
||||||
a number of its default sub-groups created automatically.
|
a number of its default sub-groups created automatically.
|
||||||
|
|
||||||
For more information on configfs please see
|
For more information on configfs please see
|
||||||
Documentation/filesystems/configfs/*.
|
`Documentation/filesystems/configfs/*`.
|
||||||
|
|
||||||
The concepts described above translate to USB gadgets like this:
|
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
|
Introduction
|
||||||
|
============
|
||||||
|
|
||||||
The HID Gadget driver provides emulation of USB Human Interface
|
The HID Gadget driver provides emulation of USB Human Interface
|
||||||
Devices (HID). The basic HID handling is done in the kernel,
|
Devices (HID). The basic HID handling is done in the kernel,
|
||||||
and HID reports can be sent/received through I/O on the
|
and HID reports can be sent/received through I/O on the
|
||||||
/dev/hidgX character devices.
|
/dev/hidgX character devices.
|
||||||
|
|
||||||
For more details about HID, see the developer page on
|
For more details about HID, see the developer page on
|
||||||
http://www.usb.org/developers/hidpage/
|
http://www.usb.org/developers/hidpage/
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
g_hid is a platform driver, so to use it you need to add
|
g_hid is a platform driver, so to use it you need to add
|
||||||
struct platform_device(s) to your platform code defining the
|
struct platform_device(s) to your platform code defining the
|
||||||
HID function descriptors you want to use - E.G. something
|
HID function descriptors you want to use - E.G. something
|
||||||
like:
|
like::
|
||||||
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/usb/g_hid.h>
|
#include <linux/usb/g_hid.h>
|
||||||
|
|
||||||
/* hid descriptor for a keyboard */
|
/* hid descriptor for a keyboard */
|
||||||
static struct hidg_func_descriptor my_hid_data = {
|
static struct hidg_func_descriptor my_hid_data = {
|
||||||
.subclass = 0, /* No subclass */
|
.subclass = 0, /* No subclass */
|
||||||
.protocol = 1, /* Keyboard */
|
.protocol = 1, /* Keyboard */
|
||||||
.report_length = 8,
|
.report_length = 8,
|
||||||
|
@ -61,85 +64,87 @@ static struct hidg_func_descriptor my_hid_data = {
|
||||||
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
|
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
|
||||||
0xc0 /* END_COLLECTION */
|
0xc0 /* END_COLLECTION */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device my_hid = {
|
static struct platform_device my_hid = {
|
||||||
.name = "hidg",
|
.name = "hidg",
|
||||||
.id = 0,
|
.id = 0,
|
||||||
.num_resources = 0,
|
.num_resources = 0,
|
||||||
.resource = 0,
|
.resource = 0,
|
||||||
.dev.platform_data = &my_hid_data,
|
.dev.platform_data = &my_hid_data,
|
||||||
};
|
};
|
||||||
|
|
||||||
You can add as many HID functions as you want, only limited by
|
You can add as many HID functions as you want, only limited by
|
||||||
the amount of interrupt endpoints your gadget driver supports.
|
the amount of interrupt endpoints your gadget driver supports.
|
||||||
|
|
||||||
Configuration with configfs
|
Configuration with configfs
|
||||||
|
===========================
|
||||||
|
|
||||||
Instead of adding fake platform devices and drivers in order to pass
|
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
|
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
|
configfs the hidg_func_descriptor.report_desc is passed to the kernel
|
||||||
by writing the appropriate stream of bytes to a configfs attribute.
|
by writing the appropriate stream of bytes to a configfs attribute.
|
||||||
|
|
||||||
Send and receive HID reports
|
Send and receive HID reports
|
||||||
|
============================
|
||||||
|
|
||||||
HID reports can be sent/received using read/write on the
|
HID reports can be sent/received using read/write on the
|
||||||
/dev/hidgX character devices. See below for an example program
|
/dev/hidgX character devices. See below for an example program
|
||||||
to do this.
|
to do this.
|
||||||
|
|
||||||
hid_gadget_test is a small interactive program to test the HID
|
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
|
gadget driver. To use, point it at a hidg device and set the
|
||||||
device type (keyboard / mouse / joystick) - E.G.:
|
device type (keyboard / mouse / joystick) - E.G.::
|
||||||
|
|
||||||
# hid_gadget_test /dev/hidg0 keyboard
|
# hid_gadget_test /dev/hidg0 keyboard
|
||||||
|
|
||||||
You are now in the prompt of hid_gadget_test. You can type any
|
You are now in the prompt of hid_gadget_test. You can type any
|
||||||
combination of options and values. Available options and
|
combination of options and values. Available options and
|
||||||
values are listed at program start. In keyboard mode you can
|
values are listed at program start. In keyboard mode you can
|
||||||
send up to six values.
|
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
|
Hit return and the corresponding report will be sent by the
|
||||||
HID gadget.
|
HID gadget.
|
||||||
|
|
||||||
Another interesting example is the caps lock test. Type
|
Another interesting example is the caps lock test. Type
|
||||||
--caps-lock and hit return. A report is then sent by the
|
--caps-lock and hit return. A report is then sent by the
|
||||||
gadget and you should receive the host answer, corresponding
|
gadget and you should receive the host answer, corresponding
|
||||||
to the caps lock LED status.
|
to the caps lock LED status::
|
||||||
|
|
||||||
--caps-lock
|
--caps-lock
|
||||||
recv report:2
|
recv report:2
|
||||||
|
|
||||||
With this command:
|
With this command::
|
||||||
|
|
||||||
# hid_gadget_test /dev/hidg1 mouse
|
# 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 <pthread.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define BUF_LEN 512
|
#define BUF_LEN 512
|
||||||
|
|
||||||
struct options {
|
struct options {
|
||||||
const char *opt;
|
const char *opt;
|
||||||
unsigned char val;
|
unsigned char val;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct options kmod[] = {
|
static struct options kmod[] = {
|
||||||
{.opt = "--left-ctrl", .val = 0x01},
|
{.opt = "--left-ctrl", .val = 0x01},
|
||||||
{.opt = "--right-ctrl", .val = 0x10},
|
{.opt = "--right-ctrl", .val = 0x10},
|
||||||
{.opt = "--left-shift", .val = 0x02},
|
{.opt = "--left-shift", .val = 0x02},
|
||||||
|
@ -149,9 +154,9 @@ static struct options kmod[] = {
|
||||||
{.opt = "--left-meta", .val = 0x08},
|
{.opt = "--left-meta", .val = 0x08},
|
||||||
{.opt = "--right-meta", .val = 0x80},
|
{.opt = "--right-meta", .val = 0x80},
|
||||||
{.opt = NULL}
|
{.opt = NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct options kval[] = {
|
static struct options kval[] = {
|
||||||
{.opt = "--return", .val = 0x28},
|
{.opt = "--return", .val = 0x28},
|
||||||
{.opt = "--esc", .val = 0x29},
|
{.opt = "--esc", .val = 0x29},
|
||||||
{.opt = "--bckspc", .val = 0x2a},
|
{.opt = "--bckspc", .val = 0x2a},
|
||||||
|
@ -183,10 +188,10 @@ static struct options kval[] = {
|
||||||
{.opt = "--up", .val = 0x52},
|
{.opt = "--up", .val = 0x52},
|
||||||
{.opt = "--num-lock", .val = 0x53},
|
{.opt = "--num-lock", .val = 0x53},
|
||||||
{.opt = NULL}
|
{.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, " ");
|
char *tok = strtok(buf, " ");
|
||||||
int key = 0;
|
int key = 0;
|
||||||
int i = 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);
|
fprintf(stderr, "unknown option: %s\n", tok);
|
||||||
}
|
}
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct options mmod[] = {
|
static struct options mmod[] = {
|
||||||
{.opt = "--b1", .val = 0x01},
|
{.opt = "--b1", .val = 0x01},
|
||||||
{.opt = "--b2", .val = 0x02},
|
{.opt = "--b2", .val = 0x02},
|
||||||
{.opt = "--b3", .val = 0x04},
|
{.opt = "--b3", .val = 0x04},
|
||||||
{.opt = NULL}
|
{.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, " ");
|
char *tok = strtok(buf, " ");
|
||||||
int mvt = 0;
|
int mvt = 0;
|
||||||
int i = 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);
|
fprintf(stderr, "unknown option: %s\n", tok);
|
||||||
}
|
}
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct options jmod[] = {
|
static struct options jmod[] = {
|
||||||
{.opt = "--b1", .val = 0x10},
|
{.opt = "--b1", .val = 0x10},
|
||||||
{.opt = "--b2", .val = 0x20},
|
{.opt = "--b2", .val = 0x20},
|
||||||
{.opt = "--b3", .val = 0x40},
|
{.opt = "--b3", .val = 0x40},
|
||||||
|
@ -287,10 +292,10 @@ static struct options jmod[] = {
|
||||||
{.opt = "--hat4", .val = 0x03},
|
{.opt = "--hat4", .val = 0x03},
|
||||||
{.opt = "--hatneutral", .val = 0x04},
|
{.opt = "--hatneutral", .val = 0x04},
|
||||||
{.opt = NULL}
|
{.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, " ");
|
char *tok = strtok(buf, " ");
|
||||||
int mvt = 0;
|
int mvt = 0;
|
||||||
int i = 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);
|
fprintf(stderr, "unknown option: %s\n", tok);
|
||||||
}
|
}
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_options(char c)
|
void print_options(char c)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (c == 'k') {
|
if (c == 'k') {
|
||||||
|
@ -358,10 +363,10 @@ void print_options(char c)
|
||||||
" three signed numbers\n"
|
" three signed numbers\n"
|
||||||
"--quit to close\n");
|
"--quit to close\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char *argv[])
|
int main(int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
const char *filename = NULL;
|
const char *filename = NULL;
|
||||||
int fd = 0;
|
int fd = 0;
|
||||||
char buf[BUF_LEN];
|
char buf[BUF_LEN];
|
||||||
|
@ -449,4 +454,4 @@ int main(int argc, const char *argv[])
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
-*- org -*-
|
==============================
|
||||||
|
Multifunction Composite Gadget
|
||||||
|
==============================
|
||||||
|
|
||||||
* Overview
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
The Multifunction Composite Gadget (or g_multi) is a composite gadget
|
The Multifunction Composite Gadget (or g_multi) is a composite gadget
|
||||||
that makes extensive use of the composite framework to provide
|
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
|
Please note that if you use non-standard configuration (that is enable
|
||||||
CDC ECM) you may need to change vendor and/or product ID.
|
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 --
|
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.
|
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.
|
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
|
Since the gadget uses standard composite framework and appears as such
|
||||||
to Linux host it does not need any additional drivers on Linux host
|
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 being the first one. Linux host will use the second
|
||||||
configuration with CDC ECM which should work better under Linux.
|
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:
|
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
|
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,
|
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
|
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!
|
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
|
The other, trickier thing is making Windows install drivers for each
|
||||||
individual function.
|
individual function.
|
||||||
|
@ -63,7 +71,8 @@ implementing USB Mass Storage class and selects appropriate driver.
|
||||||
|
|
||||||
Things are harder with RDNIS and CDC ACM.
|
Things are harder with RDNIS and CDC ACM.
|
||||||
|
|
||||||
**** RNDIS
|
RNDIS
|
||||||
|
.....
|
||||||
|
|
||||||
To make Windows select RNDIS drivers for the first function in the
|
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
|
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
|
unless you are trying to develop your own gadget in which case watch
|
||||||
out for this bug.
|
out for this bug.
|
||||||
|
|
||||||
**** CDC ACM
|
CDC ACM
|
||||||
|
.......
|
||||||
|
|
||||||
Similarly, [[file:linux-cdc-acm.inf]] is provided for 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
|
If you intend to hack the g_multi gadget be advised that rearranging
|
||||||
functions will obviously change interface numbers for each of the
|
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
|
some drivers information (changing USB port may sometimes help plus
|
||||||
you might try using USBDeview[8] to remove the phantom device).
|
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
|
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
|
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
|
as well. It most likely won't work on Windows prior to Windows XP
|
||||||
SP2.
|
SP2.
|
||||||
|
|
||||||
** Other systems
|
Other systems
|
||||||
|
-------------
|
||||||
|
|
||||||
At this moment, drivers for any other systems have not been tested.
|
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
|
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*!
|
Any testing and drivers *are* *welcome*!
|
||||||
|
|
||||||
* Authors
|
Authors
|
||||||
|
=======
|
||||||
|
|
||||||
This document has been written by Michal Nazarewicz
|
This document has been written by Michal Nazarewicz
|
||||||
([[mailto:mina86@mina86.com]]). INF files have been hacked with
|
([[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
|
template[9], Microchip's CDC ACM INF file and David Brownell's
|
||||||
([[mailto:dbrownell@users.sourceforge.net]]) original INF files.
|
([[mailto:dbrownell@users.sourceforge.net]]) original INF files.
|
||||||
|
|
||||||
* Footnotes
|
Footnotes
|
||||||
|
=========
|
||||||
|
|
||||||
[1] Remote Network Driver Interface Specification,
|
[1] Remote Network Driver Interface Specification,
|
||||||
[[http://msdn.microsoft.com/en-us/library/ee484414.aspx]].
|
[[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
|
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
|
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 net2280
|
||||||
modprobe g_printer
|
modprobe g_printer
|
||||||
|
|
||||||
|
|
||||||
The follow command line parameter can be used when loading the printer gadget
|
The follow command line parameter can be used when loading the printer gadget
|
||||||
(ex: modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 ):
|
(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
|
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
|
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
|
already have a Vendor ID please see www.usb.org for details on how to
|
||||||
get one.
|
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
|
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
|
your other USB products if you have any. It would be a good idea to
|
||||||
start numbering your products starting with say 0x0001.
|
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.
|
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.
|
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
|
either on the command line or hard code the PNP ID string used for
|
||||||
your printer product.
|
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
|
should tune this for your product. You may also want to tune the
|
||||||
size of each buffer for your product.
|
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.
|
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:
|
To compile the test code below:
|
||||||
|
|
||||||
1) save it to a file called prn_example.c
|
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
|
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
|
# 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
|
# 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
|
# prn_example -get_status
|
||||||
|
|
||||||
|
@ -107,60 +118,62 @@ To get the current printer status for the gadget driver:
|
||||||
Printer OK
|
Printer OK
|
||||||
|
|
||||||
|
|
||||||
To set printer to Selected/On-line:
|
To set printer to Selected/On-line::
|
||||||
|
|
||||||
# prn_example -selected
|
# prn_example -selected
|
||||||
|
|
||||||
|
|
||||||
To set printer to Not Selected/Off-line:
|
To set printer to Not Selected/Off-line::
|
||||||
|
|
||||||
# prn_example -not_selected
|
# prn_example -not_selected
|
||||||
|
|
||||||
|
|
||||||
To set paper status to paper out:
|
To set paper status to paper out::
|
||||||
|
|
||||||
# prn_example -paper_out
|
# prn_example -paper_out
|
||||||
|
|
||||||
|
|
||||||
To set paper status to paper loaded:
|
To set paper status to paper loaded::
|
||||||
|
|
||||||
# prn_example -paper_loaded
|
# prn_example -paper_loaded
|
||||||
|
|
||||||
|
|
||||||
To set error status to printer OK:
|
To set error status to printer OK::
|
||||||
|
|
||||||
# prn_example -no_error
|
# prn_example -no_error
|
||||||
|
|
||||||
|
|
||||||
To set error status to ERROR:
|
To set error status to ERROR::
|
||||||
|
|
||||||
# prn_example -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>
|
||||||
* 'usage()' - Show program usage.
|
#include <stdlib.h>
|
||||||
*/
|
#include <fcntl.h>
|
||||||
|
#include <linux/poll.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/usb/g_printer.h>
|
||||||
|
|
||||||
static void
|
#define PRINTER_FILE "/dev/g_printer"
|
||||||
usage(const char *option) /* I - Option string or NULL */
|
#define BUF_SIZE 512
|
||||||
{
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 'usage()' - Show program usage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(const char *option) /* I - Option string or NULL */
|
||||||
|
{
|
||||||
if (option) {
|
if (option) {
|
||||||
fprintf(stderr,"prn_example: Unknown option \"%s\"!\n",
|
fprintf(stderr,"prn_example: Unknown option \"%s\"!\n",
|
||||||
option);
|
option);
|
||||||
|
@ -186,12 +199,12 @@ usage(const char *option) /* I - Option string or NULL */
|
||||||
fputs("\n\n", stderr);
|
fputs("\n\n", stderr);
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_printer_data()
|
read_printer_data()
|
||||||
{
|
{
|
||||||
struct pollfd fd[1];
|
struct pollfd fd[1];
|
||||||
|
|
||||||
/* Open device file for printer gadget. */
|
/* Open device file for printer gadget. */
|
||||||
|
@ -236,12 +249,12 @@ read_printer_data()
|
||||||
close(fd[0].fd);
|
close(fd[0].fd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
write_printer_data()
|
write_printer_data()
|
||||||
{
|
{
|
||||||
struct pollfd fd[1];
|
struct pollfd fd[1];
|
||||||
|
|
||||||
/* Open device file for printer gadget. */
|
/* Open device file for printer gadget. */
|
||||||
|
@ -295,12 +308,12 @@ write_printer_data()
|
||||||
close(fd[0].fd);
|
close(fd[0].fd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_NB_printer_data()
|
read_NB_printer_data()
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
static char buf[BUF_SIZE];
|
static char buf[BUF_SIZE];
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
|
@ -329,12 +342,12 @@ read_NB_printer_data()
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_printer_status()
|
get_printer_status()
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
@ -357,12 +370,12 @@ get_printer_status()
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
return(retval);
|
return(retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
set_printer_status(unsigned char buf, int clear_printer_status_bit)
|
set_printer_status(unsigned char buf, int clear_printer_status_bit)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
|
@ -397,12 +410,12 @@ set_printer_status(unsigned char buf, int clear_printer_status_bit)
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
display_printer_status()
|
display_printer_status()
|
||||||
{
|
{
|
||||||
char printer_status;
|
char printer_status;
|
||||||
|
|
||||||
printer_status = get_printer_status();
|
printer_status = get_printer_status();
|
||||||
|
@ -429,12 +442,12 @@ display_printer_status()
|
||||||
}
|
}
|
||||||
|
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int i; /* Looping var */
|
int i; /* Looping var */
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
|
@ -507,4 +520,4 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(retval);
|
exit(retval);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
===============================
|
||||||
|
Linux Gadget Serial Driver v2.0
|
||||||
|
===============================
|
||||||
|
|
||||||
Linux Gadget Serial Driver v2.0
|
11/20/2004
|
||||||
11/20/2004
|
|
||||||
(updated 8-May-2008 for v2.3)
|
(updated 8-May-2008 for v2.3)
|
||||||
|
|
||||||
|
|
||||||
License and Disclaimer
|
License and Disclaimer
|
||||||
|
@ -56,7 +59,7 @@ hardware; for example, a PDA, an embedded Linux system, or a PC
|
||||||
with a USB development card.
|
with a USB development card.
|
||||||
|
|
||||||
The gadget serial driver talks over USB to either a CDC ACM driver
|
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
|
Host
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
@ -112,11 +115,11 @@ configuring the kernel. Then rebuild and install the kernel or
|
||||||
modules.
|
modules.
|
||||||
|
|
||||||
Then you must load the gadget serial driver. To load it as an
|
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
|
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
|
modprobe g_serial use_acm=0
|
||||||
|
|
||||||
|
@ -127,7 +130,7 @@ desired.
|
||||||
|
|
||||||
Your system should use mdev (from busybox) or udev to make the
|
Your system should use mdev (from busybox) or udev to make the
|
||||||
device nodes. After this gadget driver has been set up you should
|
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
|
# ls -l /dev/ttyGS0 | cat
|
||||||
crw-rw---- 1 root root 253, 0 May 8 14:10 /dev/ttyGS0
|
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
|
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
|
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
|
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
|
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
|
D: Ver= 2.00 Cls=02(comm.) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
|
||||||
P: Vendor=0525 ProdID=a4a7 Rev= 2.01
|
P: Vendor=0525 ProdID=a4a7 Rev= 2.01
|
||||||
S: Manufacturer=Linux 2.6.8.1 with net2280
|
S: Manufacturer=Linux 2.6.8.1 with net2280
|
||||||
S: Product=Gadget Serial
|
S: Product=Gadget Serial
|
||||||
S: SerialNumber=0
|
S: SerialNumber=0
|
||||||
C:* #Ifs= 2 Cfg#= 2 Atr=c0 MxPwr= 2mA
|
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
|
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
|
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
|
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=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||||
E: Ad=02(O) 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
|
If the host side Linux system is configured properly, the ACM driver
|
||||||
should be loaded automatically. The command "lsmod" should show the
|
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
|
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
|
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
|
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
|
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
|
D: Ver= 2.00 Cls=ff(vend.) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
|
||||||
P: Vendor=0525 ProdID=a4a6 Rev= 2.01
|
P: Vendor=0525 ProdID=a4a6 Rev= 2.01
|
||||||
S: Manufacturer=Linux 2.6.8.1 with net2280
|
S: Manufacturer=Linux 2.6.8.1 with net2280
|
||||||
S: Product=Gadget Serial
|
S: Product=Gadget Serial
|
||||||
S: SerialNumber=0
|
S: SerialNumber=0
|
||||||
C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 2mA
|
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
|
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=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
|
||||||
E: Ad=02(O) 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
|
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
|
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
|
modprobe usbserial vendor=0x0525 product=0xA4A6
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
=============================
|
||||||
Infinity Usb Unlimited Readme
|
Infinity Usb Unlimited Readme
|
||||||
-----------------------------
|
=============================
|
||||||
|
|
||||||
Hi all,
|
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
|
A few parameters can be used at load time
|
||||||
To use parameters, just unload the module if it is
|
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
|
In case of prebuilt module, use the command
|
||||||
insmod iuu_phoenix param=value.
|
insmod iuu_phoenix param=value.
|
||||||
|
|
||||||
Example:
|
Example::
|
||||||
|
|
||||||
modprobe iuu_phoenix clockmode=3
|
modprobe iuu_phoenix clockmode=3
|
||||||
|
|
||||||
The parameters are:
|
The parameters are:
|
||||||
|
|
||||||
parm: clockmode:1=3Mhz579,2=3Mhz680,3=6Mhz (int)
|
clockmode:
|
||||||
parm: boost:overclock boost percent 100 to 500 (int)
|
1=3Mhz579,2=3Mhz680,3=6Mhz (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)
|
boost:
|
||||||
parm: xmas:xmas color enabled or not (bool)
|
overclock boost percent 100 to 500 (int)
|
||||||
parm: debug:Debug enabled or not (bool)
|
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
|
- clockmode will provide 3 different base settings commonly adopted by
|
||||||
different software:
|
different software:
|
||||||
1. 3Mhz579
|
|
||||||
|
1. 3Mhz579
|
||||||
2. 3Mhz680
|
2. 3Mhz680
|
||||||
3. 6Mhz
|
3. 6Mhz
|
||||||
|
|
||||||
- boost provide a way to overclock the reader ( my favorite :-) )
|
- 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
|
modprobe boost=195
|
||||||
|
|
||||||
|
@ -66,7 +75,8 @@ How to tune the reader speed ?
|
||||||
- debug will produce a lot of debugging messages...
|
- debug will produce a lot of debugging messages...
|
||||||
|
|
||||||
|
|
||||||
Last notes:
|
Last notes
|
||||||
|
==========
|
||||||
|
|
||||||
Don't worry about the serial settings, the serial emulation
|
Don't worry about the serial settings, the serial emulation
|
||||||
is an abstraction, so use any speed or parity setting will
|
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,
|
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
|
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
|
(which is no longer included in Linux). It will talk only briefly
|
||||||
about how to use MSF within composite gadgets.
|
about how to use MSF within composite gadgets.
|
||||||
|
|
||||||
* Module parameters
|
Module parameters
|
||||||
|
=================
|
||||||
|
|
||||||
The mass storage gadget accepts the following mass storage specific
|
The mass storage gadget accepts the following mass storage specific
|
||||||
module parameters:
|
module parameters:
|
||||||
|
@ -146,7 +152,8 @@
|
||||||
- iProduct -- USB Product string (string)
|
- iProduct -- USB Product string (string)
|
||||||
- iSerialNumber -- SerialNumber string (sting)
|
- iSerialNumber -- SerialNumber string (sting)
|
||||||
|
|
||||||
* sysfs entries
|
sysfs entries
|
||||||
|
=============
|
||||||
|
|
||||||
For each logical unit, the gadget creates a directory in the sysfs
|
For each logical unit, the gadget creates a directory in the sysfs
|
||||||
hierarchy. Inside of it the following three files are created:
|
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
|
Other then those, as usual, the values of module parameters can be
|
||||||
read from /sys/module/g_mass_storage/parameters/* files.
|
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
|
The Mass Storage Gadget uses the Mass Storage Function to handle
|
||||||
mass storage protocol. As a composite function, MSF may be used by
|
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
|
may take a look at mass_storage.c, acm_ms.c and multi.c (sorted by
|
||||||
complexity).
|
complexity).
|
||||||
|
|
||||||
* Relation to file storage gadget
|
Relation to file storage gadget
|
||||||
|
===============================
|
||||||
|
|
||||||
The Mass Storage Function and thus the Mass Storage Gadget has been
|
The Mass Storage Function and thus the Mass Storage Gadget has been
|
||||||
based on the File Storage Gadget. The difference between the two is
|
based on the File Storage Gadget. The difference between the two is
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
=============================
|
||||||
USB 7-Segment Numeric Display
|
USB 7-Segment Numeric Display
|
||||||
|
=============================
|
||||||
|
|
||||||
Manufactured by Delcom Engineering
|
Manufactured by Delcom Engineering
|
||||||
|
|
||||||
Device Information
|
Device Information
|
||||||
|
@ -13,9 +16,13 @@ Device Modes
|
||||||
------------
|
------------
|
||||||
By default, the driver assumes the display is only 6 characters
|
By default, the driver assumes the display is only 6 characters
|
||||||
The mode for 6 characters is:
|
The mode for 6 characters is:
|
||||||
|
|
||||||
MSB 0x06; LSB 0x3f
|
MSB 0x06; LSB 0x3f
|
||||||
|
|
||||||
For the 8 character display:
|
For the 8 character display:
|
||||||
|
|
||||||
MSB 0x08; LSB 0xff
|
MSB 0x08; LSB 0xff
|
||||||
|
|
||||||
The device can accept "text" either in raw, hex, or ascii textmode.
|
The device can accept "text" either in raw, hex, or ascii textmode.
|
||||||
raw controls each segment manually,
|
raw controls each segment manually,
|
||||||
hex expects a value between 0-15 per character,
|
hex expects a value between 0-15 per character,
|
||||||
|
@ -42,5 +49,3 @@ Device Operation
|
||||||
To set multiple decimals points sum up each power.
|
To set multiple decimals points sum up each power.
|
||||||
For example, to set the 0th and 3rd decimal place
|
For example, to set the 0th and 3rd decimal place
|
||||||
echo 1001 > /sys/bus/usb/.../decimals
|
echo 1001 > /sys/bus/usb/.../decimals
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
CHANGES
|
================
|
||||||
|
mtouchusb driver
|
||||||
|
================
|
||||||
|
|
||||||
|
Changes
|
||||||
|
=======
|
||||||
|
|
||||||
- 0.3 - Created based off of scanner & INSTALL from the original touchscreen
|
- 0.3 - Created based off of scanner & INSTALL from the original touchscreen
|
||||||
driver on freecode (http://freecode.com/projects/3mtouchscreendriver)
|
driver on freecode (http://freecode.com/projects/3mtouchscreendriver)
|
||||||
- Amended for linux-2.4.18, then 2.4.19
|
- Amended for linux-2.4.18, then 2.4.19
|
||||||
|
|
||||||
- 0.5 - Complete rewrite using Linux Input in 2.6.3
|
- 0.5 - Complete rewrite using Linux Input in 2.6.3
|
||||||
Unfortunately no calibration support at this time
|
Unfortunately no calibration support at this time
|
||||||
|
|
||||||
- 1.4 - Multiple changes to support the EXII 5000UC and house cleaning
|
- 1.4 - Multiple changes to support the EXII 5000UC and house cleaning
|
||||||
Changed reset from standard USB dev reset to vendor reset
|
Changed reset from standard USB dev reset to vendor reset
|
||||||
Changed data sent to host from compensated to raw coordinates
|
Changed data sent to host from compensated to raw coordinates
|
||||||
Eliminated vendor/product module params
|
Eliminated vendor/product module params
|
||||||
Performed multiple successful tests with an EXII-5010UC
|
Performed multiple successful tests with an EXII-5010UC
|
||||||
|
|
||||||
SUPPORTED HARDWARE:
|
Supported Hardware
|
||||||
|
==================
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
All controllers have the Vendor: 0x0596 & Product: 0x0001
|
All controllers have the Vendor: 0x0596 & Product: 0x0001
|
||||||
|
|
||||||
|
@ -29,9 +37,10 @@ SUPPORTED HARDWARE:
|
||||||
USB Capacitive - Black Case EXII-5030UC
|
USB Capacitive - Black Case EXII-5030UC
|
||||||
USB Capacitive - No Case EXII-5050UC
|
USB Capacitive - No Case EXII-5050UC
|
||||||
|
|
||||||
DRIVER NOTES:
|
Driver Notes
|
||||||
|
============
|
||||||
|
|
||||||
Installation is simple, you only need to add Linux Input, Linux USB, and the
|
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.
|
driver to the kernel. The driver can also be optionally built as a module.
|
||||||
|
|
||||||
This driver appears to be one of possible 2 Linux USB Input Touchscreen
|
This driver appears to be one of possible 2 Linux USB Input Touchscreen
|
||||||
|
@ -49,24 +58,27 @@ The controller screen resolution is now 0 to 16384 for both X and Y reporting
|
||||||
the raw touch data. This is the same for the old and new capacitive USB
|
the raw touch data. This is the same for the old and new capacitive USB
|
||||||
controllers.
|
controllers.
|
||||||
|
|
||||||
Perhaps at some point an abstract function will be placed into evdev so
|
Perhaps at some point an abstract function will be placed into evdev so
|
||||||
generic functions like calibrations, resets, and vendor information can be
|
generic functions like calibrations, resets, and vendor information can be
|
||||||
requested from the userspace (And the drivers would handle the vendor specific
|
requested from the userspace (And the drivers would handle the vendor specific
|
||||||
tasks).
|
tasks).
|
||||||
|
|
||||||
TODO:
|
TODO
|
||||||
|
====
|
||||||
|
|
||||||
Implement a control urb again to handle requests to and from the device
|
Implement a control urb again to handle requests to and from the device
|
||||||
such as calibration, etc once/if it becomes available.
|
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
|
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:
|
this driver! If you want touch drivers only supported within X, please go to:
|
||||||
|
|
||||||
http://www.3m.com/3MTouchSystems/
|
http://www.3m.com/3MTouchSystems/
|
||||||
|
|
||||||
THANKS:
|
Thanks
|
||||||
|
======
|
||||||
|
|
||||||
A huge thank you to 3M Touch Systems for the EXII-5010UC controllers for
|
A huge thank you to 3M Touch Systems for the EXII-5010UC controllers for
|
||||||
testing!
|
testing!
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
====
|
||||||
|
OHCI
|
||||||
|
====
|
||||||
|
|
||||||
23-Aug-2002
|
23-Aug-2002
|
||||||
|
|
||||||
The "ohci-hcd" driver is a USB Host Controller Driver (HCD) that is derived
|
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
|
- David Brownell
|
||||||
<dbrownell@users.sourceforge.net>
|
<dbrownell@users.sourceforge.net>
|
||||||
|
|
||||||
|
|
|
@ -1,72 +1,80 @@
|
||||||
|
============
|
||||||
|
Diamonds Rio
|
||||||
|
============
|
||||||
|
|
||||||
Copyright (C) 1999, 2000 Bruce Tenison
|
Copyright (C) 1999, 2000 Bruce Tenison
|
||||||
|
|
||||||
Portions Copyright (C) 1999, 2000 David Nelson
|
Portions Copyright (C) 1999, 2000 David Nelson
|
||||||
|
|
||||||
Thanks to David Nelson for guidance and the usage of the scanner.txt
|
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.
|
and scanner.c files to model our driver and this informative file.
|
||||||
|
|
||||||
Mar. 2, 2000
|
Mar. 2, 2000
|
||||||
|
|
||||||
CHANGES
|
Changes
|
||||||
|
=======
|
||||||
|
|
||||||
- Initial Revision
|
- Initial Revision
|
||||||
|
|
||||||
|
|
||||||
OVERVIEW
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
This README will address issues regarding how to configure the kernel
|
This README will address issues regarding how to configure the kernel
|
||||||
to access a RIO 500 mp3 player.
|
to access a RIO 500 mp3 player.
|
||||||
Before I explain how to use this to access the Rio500 please be warned:
|
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
|
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
|
are in no way responsible for any damage that may occur, no matter how
|
||||||
inconsequential.
|
inconsequential.
|
||||||
|
|
||||||
It seems that the Rio has a problem when sending .mp3 with low batteries.
|
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
|
I suggest when the batteries are low and you want to transfer stuff that you
|
||||||
replace it with a fresh one. In my case, what happened is I lost two 16kb
|
replace it with a fresh one. In my case, what happened is I lost two 16kb
|
||||||
blocks (they are no longer usable to store information to it). But I don't
|
blocks (they are no longer usable to store information to it). But I don't
|
||||||
know if that's normal or not; it could simply be a problem with the flash
|
know if that's normal or not; it could simply be a problem with the flash
|
||||||
memory.
|
memory.
|
||||||
|
|
||||||
In an extreme case, I left my Rio playing overnight and the batteries wore
|
In an extreme case, I left my Rio playing overnight and the batteries wore
|
||||||
down to nothing and appear to have corrupted the flash memory. My RIO
|
down to nothing and appear to have corrupted the flash memory. My RIO
|
||||||
needed to be replaced as a result. Diamond tech support is aware of the
|
needed to be replaced as a result. Diamond tech support is aware of the
|
||||||
problem. Do NOT allow your batteries to wear down to nothing before
|
problem. Do NOT allow your batteries to wear down to nothing before
|
||||||
changing them. It appears RIO 500 firmware does not handle low battery
|
changing them. It appears RIO 500 firmware does not handle low battery
|
||||||
power well at all.
|
power well at all.
|
||||||
|
|
||||||
On systems with OHCI controllers, the kernel OHCI code appears to have
|
On systems with OHCI controllers, the kernel OHCI code appears to have
|
||||||
power on problems with some chipsets. If you are having problems
|
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
|
connecting to your RIO 500, try turning it on first and then plugging it
|
||||||
into the USB cable.
|
into the USB cable.
|
||||||
|
|
||||||
Contact information:
|
Contact Information
|
||||||
--------------------
|
-------------------
|
||||||
|
|
||||||
The main page for the project is hosted at sourceforge.net in the following
|
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
|
URL: <http://rio500.sourceforge.net>. You can also go to the project's
|
||||||
sourceforge home page at: <http://sourceforge.net/projects/rio500/>.
|
sourceforge home page at: <http://sourceforge.net/projects/rio500/>.
|
||||||
There is also a mailing list: rio500-users@lists.sourceforge.net
|
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
|
Most of the code was written by Cesar Miquel <miquel@df.uba.ar>. Keith
|
||||||
Clayton <kclayton@jps.net> is incharge of the PPC port and making sure
|
Clayton <kclayton@jps.net> is incharge of the PPC port and making sure
|
||||||
things work there. Bruce Tenison <btenison@dibbs.net> is adding support
|
things work there. Bruce Tenison <btenison@dibbs.net> is adding support
|
||||||
for .fon files and also does testing. The program will mostly sure be
|
for .fon files and also does testing. The program will mostly sure be
|
||||||
re-written and Pete Ikusz along with the rest will re-design it. I would
|
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
|
also like to thank Tri Nguyen <tmn_3022000@hotmail.com> who provided use
|
||||||
with some important information regarding the communication with the Rio.
|
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
|
A host with a USB port. Ideally, either a UHCI (Intel) or OHCI
|
||||||
(Compaq and others) hardware port should work.
|
(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
|
'lspci' which is only needed to determine the type of USB hardware
|
||||||
available in your machine.
|
available in your machine.
|
||||||
|
|
||||||
CONFIGURATION
|
Configuration
|
||||||
|
|
||||||
Using `lspci -v`, determine the type of USB hardware available.
|
Using `lspci -v`, determine the type of USB hardware available.
|
||||||
|
|
||||||
If you see something like:
|
If you see something like::
|
||||||
|
|
||||||
USB Controller: ......
|
USB Controller: ......
|
||||||
Flags: .....
|
Flags: .....
|
||||||
|
@ -92,7 +100,7 @@ Using `lspci -v`, determine the type of USB hardware available.
|
||||||
|
|
||||||
Then you have a UHCI based controller.
|
Then you have a UHCI based controller.
|
||||||
|
|
||||||
If you see something like:
|
If you see something like::
|
||||||
|
|
||||||
USB Controller: .....
|
USB Controller: .....
|
||||||
Flags: ....
|
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
|
(you may need to execute `depmod -a` to update the module
|
||||||
dependencies).
|
dependencies).
|
||||||
|
|
||||||
Add a device for the USB rio500:
|
Add a device for the USB rio500::
|
||||||
`mknod /dev/usb/rio500 c 180 64`
|
|
||||||
|
mknod /dev/usb/rio500 c 180 64
|
||||||
|
|
||||||
Set appropriate permissions for /dev/usb/rio500 (don't forget about
|
Set appropriate permissions for /dev/usb/rio500 (don't forget about
|
||||||
group and world permissions). Both read and write permissions are
|
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):
|
Load the appropriate modules (if compiled as modules):
|
||||||
|
|
||||||
OHCI:
|
OHCI::
|
||||||
|
|
||||||
modprobe usbcore
|
modprobe usbcore
|
||||||
modprobe usb-ohci
|
modprobe usb-ohci
|
||||||
modprobe rio500
|
modprobe rio500
|
||||||
|
|
||||||
UHCI:
|
UHCI::
|
||||||
|
|
||||||
modprobe usbcore
|
modprobe usbcore
|
||||||
modprobe usb-uhci (or uhci)
|
modprobe usb-uhci (or uhci)
|
||||||
modprobe rio500
|
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
|
That's it. The Rio500 Utils at: http://rio500.sourceforge.net should
|
||||||
be able to access the rio500.
|
be able to access the rio500.
|
||||||
|
|
||||||
BUGS
|
Bugs
|
||||||
|
====
|
||||||
|
|
||||||
If you encounter any problems feel free to drop me an email.
|
If you encounter any problems feel free to drop me an email.
|
||||||
|
|
||||||
Bruce Tenison
|
Bruce Tenison
|
||||||
btenison@dibbs.net
|
btenison@dibbs.net
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
usb-help.txt
|
==============
|
||||||
|
USB references
|
||||||
|
==============
|
||||||
|
|
||||||
2008-Mar-7
|
2008-Mar-7
|
||||||
|
|
||||||
For USB help other than the readme files that are located in
|
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/
|
mirrors at http://usb.in.tum.de/linux-usb/
|
||||||
and http://it.linux-usb.org
|
and http://it.linux-usb.org
|
||||||
Linux USB Guide: http://linux-usb.sourceforge.net
|
- Linux USB Guide: http://linux-usb.sourceforge.net
|
||||||
Linux-USB device overview (working devices and drivers):
|
- Linux-USB device overview (working devices and drivers):
|
||||||
http://www.qbik.ch/usb/devices/
|
http://www.qbik.ch/usb/devices/
|
||||||
|
|
||||||
The Linux-USB mailing list is at linux-usb@vger.kernel.org
|
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
|
The USB serial driver currently supports a number of different USB to
|
||||||
serial converter products, as well as some devices that use a serial
|
serial converter products, as well as some devices that use a serial
|
||||||
|
@ -8,13 +13,15 @@ INTRODUCTION
|
||||||
the different devices.
|
the different devices.
|
||||||
|
|
||||||
|
|
||||||
CONFIGURATION
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
Currently the driver can handle up to 256 different serial interfaces at
|
Currently the driver can handle up to 256 different serial interfaces at
|
||||||
one time.
|
one time.
|
||||||
|
|
||||||
The major number that the driver uses is 188 so to use the driver,
|
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/ttyUSB0 c 188 0
|
||||||
mknod /dev/ttyUSB1 c 188 1
|
mknod /dev/ttyUSB1 c 188 1
|
||||||
mknod /dev/ttyUSB2 c 188 2
|
mknod /dev/ttyUSB2 c 188 2
|
||||||
|
@ -28,12 +35,14 @@ CONFIGURATION
|
||||||
When the device is connected and recognized by the driver, the driver
|
When the device is connected and recognized by the driver, the driver
|
||||||
will print to the system log, which node(s) the device has been bound
|
will print to the system log, which node(s) the device has been bound
|
||||||
to.
|
to.
|
||||||
|
|
||||||
|
|
||||||
SPECIFIC DEVICES SUPPORTED
|
|
||||||
|
Specific Devices Supported
|
||||||
|
==========================
|
||||||
|
|
||||||
|
|
||||||
ConnectTech WhiteHEAT 4 port converter
|
ConnectTech WhiteHEAT 4 port converter
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
ConnectTech has been very forthcoming with information about their
|
ConnectTech has been very forthcoming with information about their
|
||||||
device, including providing a unit to test with.
|
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
|
HandSpring Visor, Palm USB, and Clié USB driver
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
This driver works with all HandSpring USB, Palm USB, and Sony Clié USB
|
This driver works with all HandSpring USB, Palm USB, and Sony Clié USB
|
||||||
devices.
|
devices.
|
||||||
|
@ -62,7 +72,7 @@ HandSpring Visor, Palm USB, and Clié USB driver
|
||||||
This goes against the current documentation for pilot-xfer and other
|
This goes against the current documentation for pilot-xfer and other
|
||||||
packages, but is the only way that it will work due to the hardware
|
packages, but is the only way that it will work due to the hardware
|
||||||
in the device.
|
in the device.
|
||||||
|
|
||||||
When the device is connected, try talking to it on the second port
|
When the device is connected, try talking to it on the second port
|
||||||
(this is usually /dev/ttyUSB1 if you do not have any other usb-serial
|
(this is usually /dev/ttyUSB1 if you do not have any other usb-serial
|
||||||
devices in the system.) The system log should tell you which port is
|
devices in the system.) The system log should tell you which port is
|
||||||
|
@ -78,10 +88,10 @@ HandSpring Visor, Palm USB, and Clié USB driver
|
||||||
try resetting the device, first a hot reset, and then a cold reset if
|
try resetting the device, first a hot reset, and then a cold reset if
|
||||||
necessary. Some devices need this before they can talk to the USB port
|
necessary. Some devices need this before they can talk to the USB port
|
||||||
properly.
|
properly.
|
||||||
|
|
||||||
Devices that are not compiled into the kernel can be specified with module
|
Devices that are not compiled into the kernel can be specified with module
|
||||||
parameters. e.g. modprobe visor vendor=0x54c product=0x66
|
parameters. e.g. modprobe visor vendor=0x54c product=0x66
|
||||||
|
|
||||||
There is a webpage and mailing lists for this portion of the driver at:
|
There is a webpage and mailing lists for this portion of the driver at:
|
||||||
http://sourceforge.net/projects/usbvisor/
|
http://sourceforge.net/projects/usbvisor/
|
||||||
|
|
||||||
|
@ -90,6 +100,7 @@ HandSpring Visor, Palm USB, and Clié USB driver
|
||||||
|
|
||||||
|
|
||||||
PocketPC PDA Driver
|
PocketPC PDA Driver
|
||||||
|
-------------------
|
||||||
|
|
||||||
This driver can be used to connect to Compaq iPAQ, HP Jornada, Casio EM500
|
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
|
and other PDAs running Windows CE 3.0 or PocketPC 2002 using a USB
|
||||||
|
@ -135,12 +146,13 @@ PocketPC PDA Driver
|
||||||
be used to flash the ROM, as well as the microP code.. so much for needing
|
be used to flash the ROM, as well as the microP code.. so much for needing
|
||||||
Toshiba's $350 serial cable for flashing!! :D
|
Toshiba's $350 serial cable for flashing!! :D
|
||||||
NOTE: This has NOT been tested. Use at your own risk.
|
NOTE: This has NOT been tested. Use at your own risk.
|
||||||
|
|
||||||
For any questions or problems with the driver, please contact Ganesh
|
For any questions or problems with the driver, please contact Ganesh
|
||||||
Varadarajan <ganesh@veritas.com>
|
Varadarajan <ganesh@veritas.com>
|
||||||
|
|
||||||
|
|
||||||
Keyspan PDA Serial Adapter
|
Keyspan PDA Serial Adapter
|
||||||
|
--------------------------
|
||||||
|
|
||||||
Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly
|
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).
|
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.
|
This driver also works for the Xircom/Entrega single port serial adapter.
|
||||||
|
|
||||||
Current status:
|
Current status:
|
||||||
|
|
||||||
Things that work:
|
Things that work:
|
||||||
basic input/output (tested with 'cu')
|
- basic input/output (tested with 'cu')
|
||||||
blocking write when serial line can't keep up
|
- blocking write when serial line can't keep up
|
||||||
changing baud rates (up to 115200)
|
- changing baud rates (up to 115200)
|
||||||
getting/setting modem control pins (TIOCM{GET,SET,BIS,BIC})
|
- getting/setting modem control pins (TIOCM{GET,SET,BIS,BIC})
|
||||||
sending break (although duration looks suspect)
|
- sending break (although duration looks suspect)
|
||||||
|
|
||||||
Things that don't:
|
Things that don't:
|
||||||
device strings (as logged by kernel) have trailing binary garbage
|
- device strings (as logged by kernel) have trailing binary garbage
|
||||||
device ID isn't right, might collide with other Keyspan products
|
- device ID isn't right, might collide with other Keyspan products
|
||||||
changing baud rates ought to flush tx/rx to avoid mangled half characters
|
- changing baud rates ought to flush tx/rx to avoid mangled half characters
|
||||||
|
|
||||||
Big Things on the todo list:
|
Big Things on the todo list:
|
||||||
parity, 7 vs 8 bits per char, 1 or 2 stop bits
|
- parity, 7 vs 8 bits per char, 1 or 2 stop bits
|
||||||
HW flow control
|
- HW flow control
|
||||||
not all of the standard USB descriptors are handled: Get_Status, Set_Feature
|
- not all of the standard USB descriptors are handled:
|
||||||
O_NONBLOCK, select()
|
Get_Status, Set_Feature, O_NONBLOCK, select()
|
||||||
|
|
||||||
For any questions or problems with this driver, please contact Brian
|
For any questions or problems with this driver, please contact Brian
|
||||||
Warner at warner@lothar.com
|
Warner at warner@lothar.com
|
||||||
|
|
||||||
|
|
||||||
Keyspan USA-series Serial Adapters
|
Keyspan USA-series Serial Adapters
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
Single, Dual and Quad port adapters - driver uses Keyspan supplied
|
Single, Dual and Quad port adapters - driver uses Keyspan supplied
|
||||||
firmware and is being developed with their support.
|
firmware and is being developed with their support.
|
||||||
|
|
||||||
Current status:
|
Current status:
|
||||||
|
|
||||||
The USA-18X, USA-28X, USA-19, USA-19W and USA-49W are supported and
|
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
|
have been pretty thoroughly tested at various baud rates with 8-N-1
|
||||||
character settings. Other character lengths and parity setups are
|
character settings. Other character lengths and parity setups are
|
||||||
|
@ -182,32 +199,37 @@ Keyspan USA-series Serial Adapters
|
||||||
The USA-28 isn't yet supported though doing so should be pretty
|
The USA-28 isn't yet supported though doing so should be pretty
|
||||||
straightforward. Contact the maintainer if you require this
|
straightforward. Contact the maintainer if you require this
|
||||||
functionality.
|
functionality.
|
||||||
|
|
||||||
More information is available at:
|
More information is available at:
|
||||||
|
|
||||||
http://www.carnationsoftware.com/carnation/Keyspan.html
|
http://www.carnationsoftware.com/carnation/Keyspan.html
|
||||||
|
|
||||||
For any questions or problems with this driver, please contact Hugh
|
For any questions or problems with this driver, please contact Hugh
|
||||||
Blemings at hugh@misc.nu
|
Blemings at hugh@misc.nu
|
||||||
|
|
||||||
|
|
||||||
FTDI Single Port Serial Driver
|
FTDI Single Port Serial Driver
|
||||||
|
------------------------------
|
||||||
|
|
||||||
This is a single port DB-25 serial adapter.
|
This is a single port DB-25 serial adapter.
|
||||||
|
|
||||||
Devices supported include:
|
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.
|
For any questions or problems with this driver, please contact Bill Ryder.
|
||||||
|
|
||||||
|
|
||||||
ZyXEL omni.net lcd plus ISDN TA
|
ZyXEL omni.net lcd plus ISDN TA
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
This is an ISDN TA. Please report both successes and troubles to
|
This is an ISDN TA. Please report both successes and troubles to
|
||||||
azummo@towertech.it
|
azummo@towertech.it
|
||||||
|
|
||||||
|
|
||||||
Cypress M8 CY4601 Family Serial Driver
|
Cypress M8 CY4601 Family Serial Driver
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
This driver was in most part developed by Neil "koyama" Whelchel. It
|
This driver was in most part developed by Neil "koyama" Whelchel. It
|
||||||
has been improved since that previous form to support dynamic serial
|
has been improved since that previous form to support dynamic serial
|
||||||
|
@ -215,18 +237,19 @@ Cypress M8 CY4601 Family Serial Driver
|
||||||
part stable and has been tested on an smp machine. (dual p2)
|
part stable and has been tested on an smp machine. (dual p2)
|
||||||
|
|
||||||
Chipsets supported under CY4601 family:
|
Chipsets supported under CY4601 family:
|
||||||
|
|
||||||
CY7C63723, CY7C63742, CY7C63743, CY7C64013
|
CY7C63723, CY7C63742, CY7C63743, CY7C64013
|
||||||
|
|
||||||
Devices supported:
|
Devices supported:
|
||||||
|
|
||||||
-DeLorme's USB Earthmate GPS (SiRF Star II lp arch)
|
- DeLorme's USB Earthmate GPS (SiRF Star II lp arch)
|
||||||
-Cypress HID->COM RS232 adapter
|
- Cypress HID->COM RS232 adapter
|
||||||
|
|
||||||
Note: Cypress Semiconductor claims no affiliation with the
|
Note:
|
||||||
|
Cypress Semiconductor claims no affiliation with the
|
||||||
hid->com device.
|
hid->com device.
|
||||||
|
|
||||||
Most devices using chipsets under the CY4601 family should
|
Most devices using chipsets under the CY4601 family should
|
||||||
work with the driver. As long as they stay true to the CY4601
|
work with the driver. As long as they stay true to the CY4601
|
||||||
usbserial specification.
|
usbserial specification.
|
||||||
|
|
||||||
|
@ -236,8 +259,9 @@ Cypress M8 CY4601 Family Serial Driver
|
||||||
upon start init to this setting. usbserial core provides the rest
|
upon start init to this setting. usbserial core provides the rest
|
||||||
of the termios settings, along with some custom termios so that the
|
of the termios settings, along with some custom termios so that the
|
||||||
output is in proper format and parsable.
|
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,<protocol>,<baud>,<databits>,<stopbits>,<parity>*CHECKSUM
|
||||||
$PSRF100,0,9600,8,1,0*0C
|
$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
|
If you have any questions, problems, patches, feature requests, etc. you can
|
||||||
contact me here via email:
|
contact me here via email:
|
||||||
|
|
||||||
dignome@gmail.com
|
dignome@gmail.com
|
||||||
|
|
||||||
(your problems/patches can alternately be submitted to usb-devel)
|
(your problems/patches can alternately be submitted to usb-devel)
|
||||||
|
|
||||||
|
|
||||||
Digi AccelePort Driver
|
Digi AccelePort Driver
|
||||||
|
----------------------
|
||||||
|
|
||||||
This driver supports the Digi AccelePort USB 2 and 4 devices, 2 port
|
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
|
(plus a parallel port) and 4 port USB serial converters. The driver
|
||||||
|
@ -285,42 +312,49 @@ Digi AccelePort Driver
|
||||||
|
|
||||||
|
|
||||||
Belkin USB Serial Adapter F5U103
|
Belkin USB Serial Adapter F5U103
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
Single port DB-9/PS-2 serial adapter from Belkin with firmware by eTEK Labs.
|
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
|
The Peracom single port serial adapter also works with this driver, as
|
||||||
well as the GoHubs adapter.
|
well as the GoHubs adapter.
|
||||||
|
|
||||||
Current status:
|
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
|
levels above 2.06. Read source code comments describing Belkin
|
||||||
firmware errata. Hardware output flow control is working for all
|
firmware errata. Hardware output flow control is working for all
|
||||||
firmware versions.
|
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
|
reported state. Queries of outputs (DTR,RTS) show the last
|
||||||
requested state and may not reflect current state as set by
|
requested state and may not reflect current state as set by
|
||||||
automatic hardware flow control.
|
automatic hardware flow control.
|
||||||
|
|
||||||
TO DO List:
|
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.
|
states reported by the interrupt and the states requested.
|
||||||
-- Add error reporting back to application for UART error conditions.
|
- Add error reporting back to application for UART error conditions.
|
||||||
-- Add support for flush ioctls.
|
- Add support for flush ioctls.
|
||||||
-- Add everything else that is missing :)
|
- Add everything else that is missing :)
|
||||||
|
|
||||||
For any questions or problems with this driver, please contact William
|
For any questions or problems with this driver, please contact William
|
||||||
Greathouse at wgreathouse@smva.com
|
Greathouse at wgreathouse@smva.com
|
||||||
|
|
||||||
|
|
||||||
Empeg empeg-car Mark I/II Driver
|
Empeg empeg-car Mark I/II Driver
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
This is an experimental driver to provide connectivity support for the
|
This is an experimental driver to provide connectivity support for the
|
||||||
client synchronization tools for an Empeg empeg-car mp3 player.
|
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
|
MCT USB Single Port Serial Adapter U232
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
This driver is for the MCT USB-RS232 Converter (25 pin, Model No.
|
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
|
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
|
Inside Out Networks Edgeport Driver
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
This driver supports all devices made by Inside Out Networks, specifically
|
This driver supports all devices made by Inside Out Networks, specifically
|
||||||
the following models:
|
the following models:
|
||||||
Edgeport/4
|
|
||||||
Rapidport/4
|
- Edgeport/4
|
||||||
Edgeport/4t
|
- Rapidport/4
|
||||||
Edgeport/2
|
- Edgeport/4t
|
||||||
Edgeport/4i
|
- Edgeport/2
|
||||||
Edgeport/2i
|
- Edgeport/4i
|
||||||
Edgeport/421
|
- Edgeport/2i
|
||||||
Edgeport/21
|
- Edgeport/421
|
||||||
Edgeport/8
|
- Edgeport/21
|
||||||
Edgeport/8 Dual
|
- Edgeport/8
|
||||||
Edgeport/2D8
|
- Edgeport/8 Dual
|
||||||
Edgeport/4D8
|
- Edgeport/2D8
|
||||||
Edgeport/8i
|
- Edgeport/4D8
|
||||||
Edgeport/2 DIN
|
- Edgeport/8i
|
||||||
Edgeport/4 DIN
|
- Edgeport/2 DIN
|
||||||
Edgeport/16 Dual
|
- Edgeport/4 DIN
|
||||||
|
- Edgeport/16 Dual
|
||||||
|
|
||||||
For any questions or problems with this driver, please contact Greg
|
For any questions or problems with this driver, please contact Greg
|
||||||
Kroah-Hartman at greg@kroah.com
|
Kroah-Hartman at greg@kroah.com
|
||||||
|
|
||||||
|
|
||||||
REINER SCT cyberJack pinpad/e-com USB chipcard reader
|
REINER SCT cyberJack pinpad/e-com USB chipcard reader
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
Interface to ISO 7816 compatible contactbased chipcards, e.g. GSM SIMs.
|
Interface to ISO 7816 compatible contactbased chipcards, e.g. GSM SIMs.
|
||||||
|
|
||||||
Current status:
|
Current status:
|
||||||
|
|
||||||
This is the kernel part of the driver for this USB card reader.
|
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
|
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
|
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
|
Prolific PL2303 Driver
|
||||||
|
----------------------
|
||||||
|
|
||||||
This driver supports any device that has the PL2303 chip from Prolific
|
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,
|
in it. This includes a number of single port USB to serial converters,
|
||||||
|
@ -403,11 +443,13 @@ Prolific PL2303 Driver
|
||||||
|
|
||||||
For any questions or problems with this driver, please contact Greg
|
For any questions or problems with this driver, please contact Greg
|
||||||
Kroah-Hartman at greg@kroah.com
|
Kroah-Hartman at greg@kroah.com
|
||||||
|
|
||||||
|
|
||||||
KL5KUSB105 chipset / PalmConnect USB single-port adapter
|
KL5KUSB105 chipset / PalmConnect USB single-port adapter
|
||||||
|
--------------------------------------------------------
|
||||||
|
|
||||||
Current status:
|
Current status:
|
||||||
|
|
||||||
The driver was put together by looking at the usb bus transactions
|
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
|
done by Palm's driver under Windows, so a lot of functionality is
|
||||||
still missing. Notably, serial ioctls are sometimes faked or not yet
|
still missing. Notably, serial ioctls are sometimes faked or not yet
|
||||||
|
@ -417,21 +459,25 @@ Current status:
|
||||||
are supported, but handshaking (software or hardware) is not, which is
|
are supported, but handshaking (software or hardware) is not, which is
|
||||||
why it is wise to cut down on the rate used is wise for large
|
why it is wise to cut down on the rate used is wise for large
|
||||||
transfers until this is settled.
|
transfers until this is settled.
|
||||||
|
|
||||||
See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
|
See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
|
||||||
information on this driver.
|
information on this driver.
|
||||||
|
|
||||||
Winchiphead CH341 Driver
|
Winchiphead CH341 Driver
|
||||||
|
------------------------
|
||||||
|
|
||||||
This driver is for the Winchiphead CH341 USB-RS232 Converter. This chip
|
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
|
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
|
supported by the driver. The protocol was analyzed from the behaviour
|
||||||
of the Windows driver, no datasheet is available at present.
|
of the Windows driver, no datasheet is available at present.
|
||||||
|
|
||||||
The manufacturer's website: http://www.winchiphead.com/.
|
The manufacturer's website: http://www.winchiphead.com/.
|
||||||
|
|
||||||
For any questions or problems with this driver, please contact
|
For any questions or problems with this driver, please contact
|
||||||
frank@kingswood-consulting.co.uk.
|
frank@kingswood-consulting.co.uk.
|
||||||
|
|
||||||
Moschip MCS7720, MCS7715 driver
|
Moschip MCS7720, MCS7715 driver
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
These chips are present in devices sold by various manufacturers, such as Syba
|
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
|
and Cables Unlimited. There may be others. The 7720 provides two serial
|
||||||
|
@ -449,20 +495,24 @@ Moschip MCS7720, MCS7715 driver
|
||||||
don't have one of these devices, so I can't say for sure.
|
don't have one of these devices, so I can't say for sure.
|
||||||
|
|
||||||
Generic Serial driver
|
Generic Serial driver
|
||||||
|
---------------------
|
||||||
|
|
||||||
If your device is not one of the above listed devices, compatible with
|
If your device is not one of the above listed devices, compatible with
|
||||||
the above models, you can try out the "generic" interface. This
|
the above models, you can try out the "generic" interface. This
|
||||||
interface does not provide any type of control messages sent to the
|
interface does not provide any type of control messages sent to the
|
||||||
device, and does not support any kind of device flow control. All that
|
device, and does not support any kind of device flow control. All that
|
||||||
is required of your device is that it has at least one bulk in endpoint,
|
is required of your device is that it has at least one bulk in endpoint,
|
||||||
or one bulk out 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
|
echo <vid> <pid> >/sys/bus/usb-serial/drivers/generic/new_id
|
||||||
|
|
||||||
where the <vid> and <pid> is replaced with the hex representation of your
|
where the <vid> and <pid> is replaced with the hex representation of your
|
||||||
device's vendor id and product id.
|
device's vendor id and product id.
|
||||||
If the driver is compiled as a module you can also provide one id when
|
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####
|
insmod usbserial vendor=0x#### product=0x####
|
||||||
|
|
||||||
This driver has been successfully used to connect to the NetChip USB
|
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
|
Kroah-Hartman at greg@kroah.com
|
||||||
|
|
||||||
|
|
||||||
CONTACT:
|
Contact
|
||||||
|
=======
|
||||||
|
|
||||||
If anyone has any problems using these drivers, with any of the above
|
If anyone has any problems using these drivers, with any of the above
|
||||||
specified products, please contact the specific driver's author listed
|
specified products, please contact the specific driver's author listed
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
===============
|
||||||
|
USB/IP protocol
|
||||||
|
===============
|
||||||
|
|
||||||
PRELIMINARY DRAFT, MAY CONTAIN MISTAKES!
|
PRELIMINARY DRAFT, MAY CONTAIN MISTAKES!
|
||||||
28 Jun 2011
|
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
|
the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the
|
||||||
TCP/IP connection is closed.
|
TCP/IP connection is closed.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
virtual host controller usb host
|
virtual host controller usb host
|
||||||
"client" "server"
|
"client" "server"
|
||||||
(imports USB devices) (exports USB devices)
|
(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
|
USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the
|
||||||
server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively.
|
server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
virtual host controller usb host
|
virtual host controller usb host
|
||||||
"client" "server"
|
"client" "server"
|
||||||
(imports USB devices) (exports USB devices)
|
(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.
|
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
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| Offset | Length | Value | Description |
|
||||||
0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
|
+===========+========+============+===================================================+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 |
|
||||||
2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | devices.
|
| 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | devices. |
|
||||||
4 | 4 | 0x00000000 | Status: unused, shall be set to 0
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
|
| 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
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| Offset | Length | Value | Description |
|
||||||
0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0.
|
+===========+========+============+===================================================+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0.|
|
||||||
2 | 2 | 0x0005 | Reply code: The list of exported USB devices.
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 2 | 2 | 0x0005 | Reply code: The list of exported USB devices. |
|
||||||
4 | 4 | 0x00000000 | Status: 0 for OK
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 4 | 4 | 0x00000000 | Status: 0 for OK |
|
||||||
8 | 4 | n | Number of exported devices: 0 means no exported
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | devices.
|
| 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
|
| 0x0C | | | From now on the exported n devices are described, |
|
||||||
| | | ends with the previous "number of exported
|
| | | | if any. If no devices are exported the message |
|
||||||
| | | devices" field.
|
| | | | 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.
|
| | 256 | | path: Path of the device on the host exporting the|
|
||||||
| | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
|
| | | | USB device, string closed with zero byte, e.g. |
|
||||||
| | | The unused bytes shall be filled with zero
|
| | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" |
|
||||||
| | | bytes.
|
| | | | 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
|
| 0x10C | 32 | | busid: Bus ID of the exported device, string |
|
||||||
| | | bytes shall be filled with zero bytes.
|
| | | | closed with zero byte, e.g. "3-2". The unused |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | bytes shall be filled with zero bytes. |
|
||||||
0x12C | 4 | | busnum
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x12C | 4 | | busnum |
|
||||||
0x130 | 4 | | devnum
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x130 | 4 | | devnum |
|
||||||
0x134 | 4 | | speed
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x134 | 4 | | speed |
|
||||||
0x138 | 2 | | idVendor
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x138 | 2 | | idVendor |
|
||||||
0x13A | 2 | | idProduct
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x13A | 2 | | idProduct |
|
||||||
0x13C | 2 | | bcdDevice
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x13C | 2 | | bcdDevice |
|
||||||
0x13E | 1 | | bDeviceClass
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x13E | 1 | | bDeviceClass |
|
||||||
0x13F | 1 | | bDeviceSubClass
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x13F | 1 | | bDeviceSubClass |
|
||||||
0x140 | 1 | | bDeviceProtocol
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x140 | 1 | | bDeviceProtocol |
|
||||||
0x141 | 1 | | bConfigurationValue
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x141 | 1 | | bConfigurationValue |
|
||||||
0x142 | 1 | | bNumConfigurations
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x142 | 1 | | bNumConfigurations |
|
||||||
0x143 | 1 | | bNumInterfaces
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x143 | 1 | | bNumInterfaces |
|
||||||
0x144 | | m_0 | From now on each interface is described, all
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | together bNumInterfaces times, with the
|
| 0x144 | | m_0 | From now on each interface is described, all |
|
||||||
| | | the following 4 fields:
|
| | | | together bNumInterfaces times, with the |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | the following 4 fields: |
|
||||||
| 1 | | bInterfaceClass
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | 1 | | bInterfaceClass |
|
||||||
0x145 | 1 | | bInterfaceSubClass
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x145 | 1 | | bInterfaceSubClass |
|
||||||
0x146 | 1 | | bInterfaceProtocol
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x146 | 1 | | bInterfaceProtocol |
|
||||||
0x147 | 1 | | padding byte for alignment, shall be set to zero
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 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.
|
| 0xC + | | | The second exported USB device starts at i=1 |
|
||||||
m_(i-1)*4 | | |
|
| 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
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| Offset | Length | Value | Description |
|
||||||
0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
|
+===========+========+============+===================================================+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 |
|
||||||
2 | 2 | 0x8003 | Command code: import a remote USB device.
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 2 | 2 | 0x8003 | Command code: import a remote USB device. |
|
||||||
4 | 4 | 0x00000000 | Status: unused, shall be set to 0
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 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
|
| 8 | 32 | | busid: the busid of the exported device on the |
|
||||||
| | | from the message field OP_REP_DEVLIST.busid.
|
| | | | remote host. The possible values are taken |
|
||||||
| | | A string closed with zero, the unused bytes
|
| | | | from the message field OP_REP_DEVLIST.busid. |
|
||||||
| | | shall be filled with zeros.
|
| | | | 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
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| Offset | Length | Value | Description |
|
||||||
0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0
|
+===========+========+============+===================================================+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 |
|
||||||
2 | 2 | 0x0003 | Reply code: Reply to import.
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 2 | 2 | 0x0003 | Reply code: Reply to import. |
|
||||||
4 | 4 | 0x00000000 | Status: 0 for OK
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | 1 for error
|
| 4 | 4 | 0x00000000 | Status: |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | |
|
||||||
8 | | | From now on comes the details of the imported
|
| | | | - 0 for OK |
|
||||||
| | | device, if the previous status field was OK (0),
|
| | | | - 1 for error |
|
||||||
| | | otherwise the reply ends with the status field.
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 8 | | | From now on comes the details of the imported |
|
||||||
| 256 | | path: Path of the device on the host exporting the
|
| | | | device, if the previous status field was OK (0), |
|
||||||
| | | USB device, string closed with zero byte, e.g.
|
| | | | otherwise the reply ends with the status field. |
|
||||||
| | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2"
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | The unused bytes shall be filled with zero
|
| | 256 | | path: Path of the device on the host exporting the|
|
||||||
| | | bytes.
|
| | | | USB device, string closed with zero byte, e.g. |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" |
|
||||||
0x108 | 32 | | busid: Bus ID of the exported device, string
|
| | | | The unused bytes shall be filled with zero |
|
||||||
| | | closed with zero byte, e.g. "3-2". The unused
|
| | | | bytes. |
|
||||||
| | | bytes shall be filled with zero bytes.
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x108 | 32 | | busid: Bus ID of the exported device, string |
|
||||||
0x128 | 4 | | busnum
|
| | | | closed with zero byte, e.g. "3-2". The unused |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | bytes shall be filled with zero bytes. |
|
||||||
0x12C | 4 | | devnum
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x128 | 4 | | busnum |
|
||||||
0x130 | 4 | | speed
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x12C | 4 | | devnum |
|
||||||
0x134 | 2 | | idVendor
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x130 | 4 | | speed |
|
||||||
0x136 | 2 | | idProduct
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x134 | 2 | | idVendor |
|
||||||
0x138 | 2 | | bcdDevice
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x136 | 2 | | idProduct |
|
||||||
0x139 | 1 | | bDeviceClass
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x138 | 2 | | bcdDevice |
|
||||||
0x13A | 1 | | bDeviceSubClass
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x139 | 1 | | bDeviceClass |
|
||||||
0x13B | 1 | | bDeviceProtocol
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x13A | 1 | | bDeviceSubClass |
|
||||||
0x13C | 1 | | bConfigurationValue
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x13B | 1 | | bDeviceProtocol |
|
||||||
0x13D | 1 | | bNumConfigurations
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x13C | 1 | | bConfigurationValue |
|
||||||
0x13E | 1 | | bNumInterfaces
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
|
| 0x13D | 1 | | bNumConfigurations |
|
||||||
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
|
| 0x13E | 1 | | bNumInterfaces |
|
||||||
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
|
|
||||||
USBIP_CMD_SUBMIT: Submit an URB
|
USBIP_CMD_SUBMIT:
|
||||||
|
Submit an URB
|
||||||
|
|
||||||
Offset | Length | Value | Description
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| Offset | Length | Value | Description |
|
||||||
0 | 4 | 0x00000001 | command: Submit an URB
|
+===========+========+============+===================================================+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0 | 4 | 0x00000001 | command: Submit an URB |
|
||||||
4 | 4 | | seqnum: the sequence number of the URB to submit
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 4 | 4 | | seqnum: the sequence number of the URB to submit |
|
||||||
8 | 4 | | devid
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 8 | 4 | | devid |
|
||||||
0xC | 4 | | direction: 0: USBIP_DIR_OUT
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | 1: USBIP_DIR_IN
|
| 0xC | 4 | | direction: |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | |
|
||||||
0x10 | 4 | | ep: endpoint number, possible values are: 0...15
|
| | | | - 0: USBIP_DIR_OUT |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | - 1: USBIP_DIR_IN |
|
||||||
0x14 | 4 | | transfer_flags: possible values depend on the
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | URB transfer type, see below
|
| 0x10 | 4 | | ep: endpoint number, possible values are: 0...15 |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
0x18 | 4 | | transfer_buffer_length
|
| 0x14 | 4 | | transfer_flags: possible values depend on the |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | URB transfer type, see below |
|
||||||
0x1C | 4 | | start_frame: specify the selected frame to
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | transmit an ISO frame, ignored if URB_ISO_ASAP
|
| 0x18 | 4 | | transfer_buffer_length |
|
||||||
| | | is specified at transfer_flags
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x1C | 4 | | start_frame: specify the selected frame to |
|
||||||
0x20 | 4 | | number_of_packets: number of ISO packets
|
| | | | transmit an ISO frame, ignored if URB_ISO_ASAP |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | is specified at transfer_flags |
|
||||||
0x24 | 4 | | interval: maximum time for the request on the
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | server-side host controller
|
| 0x20 | 4 | | number_of_packets: number of ISO packets |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
0x28 | 8 | | setup: data bytes for USB setup, filled with
|
| 0x24 | 4 | | interval: maximum time for the request on the |
|
||||||
| | | zeros if not used
|
| | | | server-side host controller |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
0x30 | | | URB data. For ISO transfers the padding between
|
| 0x28 | 8 | | setup: data bytes for USB setup, filled with |
|
||||||
| | | each ISO packets is not transmitted.
|
| | | | 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
|
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||||
-------------------------+------------+---------+-----------+----------+-------------
|
| 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_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no |
|
||||||
URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes
|
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||||
URB_ZERO_PACKET | 0x00000040 | no | no | only out | no
|
| URB_ISO_ASAP | 0x00000002 | no | no | no | yes |
|
||||||
URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes
|
+-------------------------+------------+---------+-----------+----------+-------------+
|
||||||
URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes
|
| URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes |
|
||||||
URB_DIR_MASK | 0x00000200 | 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
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| Offset | Length | Value | Description |
|
||||||
0 | 4 | 0x00000003 | command
|
+===========+========+============+===================================================+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0 | 4 | 0x00000003 | command |
|
||||||
4 | 4 | | seqnum: URB sequence number
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 4 | 4 | | seqnum: URB sequence number |
|
||||||
8 | 4 | | devid
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 8 | 4 | | devid |
|
||||||
0xC | 4 | | direction: 0: USBIP_DIR_OUT
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | 1: USBIP_DIR_IN
|
| 0xC | 4 | | direction: |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | |
|
||||||
0x10 | 4 | | ep: endpoint number
|
| | | | - 0: USBIP_DIR_OUT |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | - 1: USBIP_DIR_IN |
|
||||||
0x14 | 4 | | status: zero for successful URB transaction,
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | otherwise some kind of error happened.
|
| 0x10 | 4 | | ep: endpoint number |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
0x18 | 4 | n | actual_length: number of URB data bytes
|
| 0x14 | 4 | | status: zero for successful URB transaction, |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | otherwise some kind of error happened. |
|
||||||
0x1C | 4 | | start_frame: for an ISO frame the actually
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | selected frame for transmit.
|
| 0x18 | 4 | n | actual_length: number of URB data bytes |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
0x20 | 4 | | number_of_packets
|
| 0x1C | 4 | | start_frame: for an ISO frame the actually |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | selected frame for transmit. |
|
||||||
0x24 | 4 | | error_count
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x20 | 4 | | number_of_packets |
|
||||||
0x28 | 8 | | setup: data bytes for USB setup, filled with
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | zeros if not used
|
| 0x24 | 4 | | error_count |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
0x30 | n | | URB data bytes. For ISO transfers the padding
|
| 0x28 | 8 | | setup: data bytes for USB setup, filled with |
|
||||||
| | | between each ISO packets is not transmitted.
|
| | | | 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
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| Offset | Length | Value | Description |
|
||||||
0 | 4 | 0x00000002 | command: URB unlink command
|
+===========+========+============+===================================================+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0 | 4 | 0x00000002 | command: URB unlink command |
|
||||||
4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so?
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 4 | 4 | | seqnum: URB sequence number to unlink: |
|
||||||
8 | 4 | | devid
|
| | | | |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | FIXME: |
|
||||||
0xC | 4 | | direction: 0: USBIP_DIR_OUT
|
| | | | is this so? |
|
||||||
| | | 1: USBIP_DIR_IN
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 8 | 4 | | devid |
|
||||||
0x10 | 4 | | ep: endpoint number: zero
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0xC | 4 | | direction: |
|
||||||
0x14 | 4 | | seqnum: the URB sequence number given previously
|
| | | | |
|
||||||
| | | at USBIP_CMD_SUBMIT.seqnum field
|
| | | | - 0: USBIP_DIR_OUT |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | - 1: USBIP_DIR_IN |
|
||||||
0x30 | n | | URB data bytes. For ISO transfers the padding
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | between each ISO packets is not transmitted.
|
| 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
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| Offset | Length | Value | Description |
|
||||||
0 | 4 | 0x00000004 | command: reply for the URB unlink command
|
+===========+========+============+===================================================+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0 | 4 | 0x00000004 | command: reply for the URB unlink command |
|
||||||
4 | 4 | | seqnum: the unlinked URB sequence number
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 4 | 4 | | seqnum: the unlinked URB sequence number |
|
||||||
8 | 4 | | devid
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 8 | 4 | | devid |
|
||||||
0xC | 4 | | direction: 0: USBIP_DIR_OUT
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | 1: USBIP_DIR_IN
|
| 0xC | 4 | | direction: |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | |
|
||||||
0x10 | 4 | | ep: endpoint number
|
| | | | - 0: USBIP_DIR_OUT |
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| | | | - 1: USBIP_DIR_IN |
|
||||||
0x14 | 4 | | status: This is the value contained in the
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
| | | urb->status in the URB completition handler.
|
| 0x10 | 4 | | ep: endpoint number |
|
||||||
| | | FIXME: a better explanation needed.
|
+-----------+--------+------------+---------------------------------------------------+
|
||||||
-----------+--------+------------+---------------------------------------------------
|
| 0x14 | 4 | | status: This is the value contained in the |
|
||||||
0x30 | n | | URB data bytes. For ISO transfers the padding
|
| | | | urb->status in the URB completition handler. |
|
||||||
| | | between each ISO packets is not transmitted.
|
| | | | |
|
||||||
|
| | | | 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
|
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
|
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.
|
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.
|
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
|
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
|
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.
|
To collect a raw text trace, execute following steps.
|
||||||
|
|
||||||
1. Prepare
|
1. Prepare
|
||||||
|
----------
|
||||||
|
|
||||||
Mount debugfs (it has to be enabled in your kernel configuration), and
|
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
|
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
|
# mount -t debugfs none_debugs /sys/kernel/debug
|
||||||
# modprobe usbmon
|
# modprobe usbmon
|
||||||
#
|
#
|
||||||
|
|
||||||
Verify that bus sockets are present.
|
Verify that bus sockets are present:
|
||||||
|
|
||||||
# ls /sys/kernel/debug/usb/usbmon
|
# ls /sys/kernel/debug/usb/usbmon
|
||||||
0s 0u 1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
|
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
|
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.
|
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.
|
This allows to filter away annoying devices that talk continuously.
|
||||||
|
|
||||||
2. Find which bus connects to the desired device
|
2. Find which bus connects to the desired device
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
Run "cat /sys/kernel/debug/usb/devices", and find the T-line which corresponds
|
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
|
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
|
many similar devices, unplug one and compare the two
|
||||||
/sys/kernel/debug/usb/devices outputs. The T-line will have a bus number.
|
/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
|
Example::
|
||||||
D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
|
||||||
P: Vendor=0557 ProdID=2004 Rev= 1.00
|
T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 0
|
||||||
S: Manufacturer=ATEN
|
D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
|
||||||
S: Product=UC100KM V2.00
|
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
|
"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:
|
"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
|
Bus 003 Device 002: ID 0557:2004 ATEN UC100KM V2.00
|
||||||
|
|
||||||
3. Start 'cat'
|
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
|
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
|
redirected to a desirable location. This is preferred, because it is going
|
||||||
to be quite long.
|
to be quite long.
|
||||||
|
|
||||||
4. Perform the desired operation on the USB bus
|
4. Perform the desired operation on the USB bus
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
This is where you do something that creates the traffic: plug in a flash key,
|
This is where you do something that creates the traffic: plug in a flash key,
|
||||||
copy files, control a webcam, etc.
|
copy files, control a webcam, etc.
|
||||||
|
|
||||||
5. Kill cat
|
5. Kill cat
|
||||||
|
-----------
|
||||||
|
|
||||||
Usually it's done with a keyboard interrupt (Control-C).
|
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
|
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.
|
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
|
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'
|
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
|
- "Address" word (formerly a "pipe"). It consists of four fields, separated by
|
||||||
colons: URB type and direction, Bus number, Device address, Endpoint number.
|
colons: URB type and direction, Bus number, Device address, Endpoint number.
|
||||||
Type and direction are encoded with two bytes in the following manner:
|
Type and direction are encoded with two bytes in the following manner:
|
||||||
|
|
||||||
|
== == =============================
|
||||||
Ci Co Control input and output
|
Ci Co Control input and output
|
||||||
Zi Zo Isochronous input and output
|
Zi Zo Isochronous input and output
|
||||||
Ii Io Interrupt input and output
|
Ii Io Interrupt input and output
|
||||||
Bi Bo Bulk input and output
|
Bi Bo Bulk input and output
|
||||||
|
== == =============================
|
||||||
|
|
||||||
Bus number, Device address, and Endpoint are decimal numbers, but they may
|
Bus number, Device address, and Endpoint are decimal numbers, but they may
|
||||||
have leading zeros, for the sake of human readers.
|
have leading zeros, for the sake of human readers.
|
||||||
|
|
||||||
|
@ -178,24 +197,25 @@ Here is the list of words, from left to right:
|
||||||
|
|
||||||
Examples:
|
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 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
|
||||||
d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
|
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
|
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 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 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,
|
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
|
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 */
|
u64 id; /* 0: URB ID - from submission to callback */
|
||||||
unsigned char type; /* 8: Same as text; extensible. */
|
unsigned char type; /* 8: Same as text; extensible. */
|
||||||
unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */
|
unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */
|
||||||
|
@ -220,7 +240,7 @@ struct usbmon_packet {
|
||||||
int start_frame; /* 52: For ISO */
|
int start_frame; /* 52: For ISO */
|
||||||
unsigned int xfer_flags; /* 56: copy of URB's transfer_flags */
|
unsigned int xfer_flags; /* 56: copy of URB's transfer_flags */
|
||||||
unsigned int ndesc; /* 60: Actual number of ISO descriptors */
|
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),
|
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)
|
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)
|
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 queued;
|
||||||
u32 dropped;
|
u32 dropped;
|
||||||
};
|
};
|
||||||
|
|
||||||
The member "queued" refers to the number of events currently queued in the
|
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).
|
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,
|
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
|
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;
|
struct usbmon_packet *hdr;
|
||||||
void *data;
|
void *data;
|
||||||
size_t alloc; /* Length of data (can be zero) */
|
size_t alloc; /* Length of data (can be zero) */
|
||||||
};
|
};
|
||||||
|
|
||||||
Before the call, hdr, data, and alloc should be filled. Upon return, the area
|
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
|
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)
|
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
|
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 *offvec; /* Vector of events fetched */
|
||||||
uint32_t nfetch; /* Number of events to fetch (out: fetched) */
|
uint32_t nfetch; /* Number of events to fetch (out: fetched) */
|
||||||
uint32_t nflush; /* Number of events to flush */
|
uint32_t nflush; /* Number of events to flush */
|
||||||
};
|
};
|
||||||
|
|
||||||
The ioctl operates in 3 stages.
|
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:
|
The basic idea is simple:
|
||||||
|
|
||||||
To prepare, map the buffer by getting the current size, then using mmap(2).
|
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 mon_mfetch_arg fetch;
|
||||||
struct usbmon_packet *hdr;
|
struct usbmon_packet *hdr;
|
||||||
|
|
13
MAINTAINERS
13
MAINTAINERS
|
@ -15377,6 +15377,11 @@ M: Laxman Dewangan <ldewangan@nvidia.com>
|
||||||
S: Supported
|
S: Supported
|
||||||
F: drivers/spi/spi-tegra*
|
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
|
TEHUTI ETHERNET DRIVER
|
||||||
M: Andy Gospodarek <andy@greyhouse.net>
|
M: Andy Gospodarek <andy@greyhouse.net>
|
||||||
L: netdev@vger.kernel.org
|
L: netdev@vger.kernel.org
|
||||||
|
@ -16155,6 +16160,14 @@ L: linux-usb@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/usb/roles/intel-xhci-usb-role-switch.c
|
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
|
USB ISP116X DRIVER
|
||||||
M: Olav Kongas <ok@artecdesign.ee>
|
M: Olav Kongas <ok@artecdesign.ee>
|
||||||
L: linux-usb@vger.kernel.org
|
L: linux-usb@vger.kernel.org
|
||||||
|
|
|
@ -616,6 +616,7 @@
|
||||||
dr_mode = "host";
|
dr_mode = "host";
|
||||||
phys = <&usbphy2>;
|
phys = <&usbphy2>;
|
||||||
phy-names = "usb2-phy";
|
phy-names = "usb2-phy";
|
||||||
|
snps,reset-phy-on-wake;
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -904,6 +905,8 @@
|
||||||
clocks = <&cru SCLK_OTGPHY0>;
|
clocks = <&cru SCLK_OTGPHY0>;
|
||||||
clock-names = "phyclk";
|
clock-names = "phyclk";
|
||||||
#clock-cells = <0>;
|
#clock-cells = <0>;
|
||||||
|
resets = <&cru SRST_USBOTG_PHY>;
|
||||||
|
reset-names = "phy-reset";
|
||||||
};
|
};
|
||||||
|
|
||||||
usbphy1: usb-phy@334 {
|
usbphy1: usb-phy@334 {
|
||||||
|
@ -912,6 +915,8 @@
|
||||||
clocks = <&cru SCLK_OTGPHY1>;
|
clocks = <&cru SCLK_OTGPHY1>;
|
||||||
clock-names = "phyclk";
|
clock-names = "phyclk";
|
||||||
#clock-cells = <0>;
|
#clock-cells = <0>;
|
||||||
|
resets = <&cru SRST_USBHOST0_PHY>;
|
||||||
|
reset-names = "phy-reset";
|
||||||
};
|
};
|
||||||
|
|
||||||
usbphy2: usb-phy@348 {
|
usbphy2: usb-phy@348 {
|
||||||
|
@ -920,6 +925,8 @@
|
||||||
clocks = <&cru SCLK_OTGPHY2>;
|
clocks = <&cru SCLK_OTGPHY2>;
|
||||||
clock-names = "phyclk";
|
clock-names = "phyclk";
|
||||||
#clock-cells = <0>;
|
#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);
|
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)
|
static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
|
||||||
{
|
{
|
||||||
struct i2c_client *ccgx_client;
|
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));
|
sizeof(i2cd->gpu_ccgx_ucsi->type));
|
||||||
i2cd->gpu_ccgx_ucsi->addr = 0x8;
|
i2cd->gpu_ccgx_ucsi->addr = 0x8;
|
||||||
i2cd->gpu_ccgx_ucsi->irq = irq;
|
i2cd->gpu_ccgx_ucsi->irq = irq;
|
||||||
|
i2cd->gpu_ccgx_ucsi->properties = ccgx_props;
|
||||||
ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
|
ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
|
||||||
if (!ccgx_client)
|
if (!ccgx_client)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -36,3 +36,25 @@ config PHY_MESON_GXL_USB3
|
||||||
Enable this to support the Meson USB3 PHY and OTG detection
|
Enable this to support the Meson USB3 PHY and OTG detection
|
||||||
IP block found in Meson GXL and GXM SoCs.
|
IP block found in Meson GXL and GXM SoCs.
|
||||||
If unsure, say N.
|
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_MESON8B_USB2) += phy-meson8b-usb2.o
|
||||||
obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-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_GXL_USB3) += phy-meson-gxl-usb3.o
|
||||||
|
obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o
|
||||||
|
|
|
@ -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");
|
|
@ -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))
|
if (IS_ERR(priv->regmap))
|
||||||
return PTR_ERR(priv->regmap);
|
return PTR_ERR(priv->regmap);
|
||||||
|
|
||||||
priv->clk = devm_clk_get(dev, "phy");
|
priv->clk = devm_clk_get_optional(dev, "phy");
|
||||||
if (IS_ERR(priv->clk)) {
|
if (IS_ERR(priv->clk))
|
||||||
ret = PTR_ERR(priv->clk);
|
return PTR_ERR(priv->clk);
|
||||||
if (ret == -ENOENT)
|
|
||||||
priv->clk = NULL;
|
|
||||||
else
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->reset = devm_reset_control_get_optional_shared(dev, "phy");
|
priv->reset = devm_reset_control_get_optional_shared(dev, "phy");
|
||||||
if (IS_ERR(priv->reset))
|
if (IS_ERR(priv->reset))
|
||||||
|
|
|
@ -10,6 +10,17 @@ config PHY_CYGNUS_PCIE
|
||||||
Enable this to support the Broadcom Cygnus PCIe PHY.
|
Enable this to support the Broadcom Cygnus PCIe PHY.
|
||||||
If unsure, say N.
|
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
|
config BCM_KONA_USB2_PHY
|
||||||
tristate "Broadcom Kona USB2 PHY Driver"
|
tristate "Broadcom Kona USB2 PHY Driver"
|
||||||
depends on HAS_IOMEM
|
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
|
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_PCIE) += phy-bcm-sr-pcie.o
|
||||||
|
obj-$(CONFIG_PHY_BCM_SR_USB) += phy-bcm-sr-usb.o
|
||||||
|
|
|
@ -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/module.h>
|
||||||
#include <linux/phy/phy.h>
|
#include <linux/phy/phy.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
|
||||||
#define PHY_CTRL0 0x0
|
#define PHY_CTRL0 0x0
|
||||||
#define PHY_CTRL0_REF_SSP_EN BIT(2)
|
#define PHY_CTRL0_REF_SSP_EN BIT(2)
|
||||||
|
@ -24,6 +25,7 @@ struct imx8mq_usb_phy {
|
||||||
struct phy *phy;
|
struct phy *phy;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
struct regulator *vbus;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int imx8mq_usb_phy_init(struct phy *phy)
|
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)
|
static int imx8mq_phy_power_on(struct phy *phy)
|
||||||
{
|
{
|
||||||
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(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);
|
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);
|
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
|
||||||
|
|
||||||
clk_disable_unprepare(imx_phy->clk);
|
clk_disable_unprepare(imx_phy->clk);
|
||||||
|
regulator_disable(imx_phy->vbus);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -101,6 +109,10 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(imx_phy->phy))
|
if (IS_ERR(imx_phy->phy))
|
||||||
return PTR_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_set_drvdata(imx_phy->phy, imx_phy);
|
||||||
|
|
||||||
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
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.
|
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
|
config PHY_HISTB_COMBPHY
|
||||||
tristate "HiSilicon STB SoCs COMBPHY support"
|
tristate "HiSilicon STB SoCs COMBPHY support"
|
||||||
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
|
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
|
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_HISTB_COMBPHY) += phy-histb-combphy.o
|
||||||
obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
|
obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
|
||||||
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
|
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
|
||||||
|
|
|
@ -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,
|
multi-ports is first version, otherwise is second veriosn,
|
||||||
so you can easily distinguish them by banks layout.
|
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
|
config PHY_MTK_XSPHY
|
||||||
tristate "MediaTek XS-PHY Driver"
|
tristate "MediaTek XS-PHY Driver"
|
||||||
depends on ARCH_MEDIATEK && OF
|
depends on ARCH_MEDIATEK && OF
|
||||||
|
|
|
@ -4,4 +4,5 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
|
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
|
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 */
|
/* it's deprecated, make it optional for backward compatibility */
|
||||||
tphy->u3phya_ref = devm_clk_get(dev, "u3phya_ref");
|
tphy->u3phya_ref = devm_clk_get_optional(dev, "u3phya_ref");
|
||||||
if (IS_ERR(tphy->u3phya_ref)) {
|
if (IS_ERR(tphy->u3phya_ref))
|
||||||
if (PTR_ERR(tphy->u3phya_ref) == -EPROBE_DEFER)
|
return PTR_ERR(tphy->u3phya_ref);
|
||||||
return -EPROBE_DEFER;
|
|
||||||
|
|
||||||
tphy->u3phya_ref = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
tphy->src_ref_clk = U3P_REF_CLK;
|
tphy->src_ref_clk = U3P_REF_CLK;
|
||||||
tphy->src_coef = U3P_SLEW_RATE_COEF;
|
tphy->src_coef = U3P_SLEW_RATE_COEF;
|
||||||
|
|
|
@ -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;
|
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
|
#define MCB_S1G_CFG_TIMEOUT 50
|
||||||
|
|
||||||
static int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op)
|
static int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op)
|
||||||
|
@ -110,7 +342,7 @@ struct serdes_mux {
|
||||||
u32 mux;
|
u32 mux;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \
|
#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \
|
||||||
.idx = _idx, \
|
.idx = _idx, \
|
||||||
.port = _port, \
|
.port = _port, \
|
||||||
.mode = _mode, \
|
.mode = _mode, \
|
||||||
|
@ -191,8 +423,12 @@ static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
|
||||||
|
|
||||||
if (macro->idx <= SERDES1G_MAX)
|
if (macro->idx <= SERDES1G_MAX)
|
||||||
return serdes_init_s1g(macro->ctrl->regs, macro->idx);
|
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;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -384,10 +384,16 @@ int phy_reset(struct phy *phy)
|
||||||
if (!phy || !phy->ops->reset)
|
if (!phy || !phy->ops->reset)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
ret = phy_pm_runtime_get_sync(phy);
|
||||||
|
if (ret < 0 && ret != -ENOTSUPP)
|
||||||
|
return ret;
|
||||||
|
|
||||||
mutex_lock(&phy->mutex);
|
mutex_lock(&phy->mutex);
|
||||||
ret = phy->ops->reset(phy);
|
ret = phy->ops->reset(phy);
|
||||||
mutex_unlock(&phy->mutex);
|
mutex_unlock(&phy->mutex);
|
||||||
|
|
||||||
|
phy_pm_runtime_put(phy);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(phy_reset);
|
EXPORT_SYMBOL_GPL(phy_reset);
|
||||||
|
@ -564,6 +570,11 @@ void phy_put(struct phy *phy)
|
||||||
if (!phy || IS_ERR(phy))
|
if (!phy || IS_ERR(phy))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
mutex_lock(&phy->mutex);
|
||||||
|
if (phy->ops->release)
|
||||||
|
phy->ops->release(phy);
|
||||||
|
mutex_unlock(&phy->mutex);
|
||||||
|
|
||||||
module_put(phy->ops->owner);
|
module_put(phy->ops->owner);
|
||||||
put_device(&phy->dev);
|
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),
|
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[] = {
|
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_SYSCLK_EN_SEL, 0x14),
|
||||||
QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
|
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
|
* @init_count: phy common block initialization count
|
||||||
* @phy_initialized: indicate if PHY has been initialized
|
* @phy_initialized: indicate if PHY has been initialized
|
||||||
* @mode: current PHY mode
|
* @mode: current PHY mode
|
||||||
|
* @ufs_reset: optional UFS PHY reset handle
|
||||||
*/
|
*/
|
||||||
struct qcom_qmp {
|
struct qcom_qmp {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
@ -914,6 +997,8 @@ struct qcom_qmp {
|
||||||
int init_count;
|
int init_count;
|
||||||
bool phy_initialized;
|
bool phy_initialized;
|
||||||
enum phy_mode mode;
|
enum phy_mode mode;
|
||||||
|
|
||||||
|
struct reset_control *ufs_reset;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
|
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,
|
.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 = {
|
static const struct qmp_phy_cfg msm8998_usb3phy_cfg = {
|
||||||
.type = PHY_TYPE_USB3,
|
.type = PHY_TYPE_USB3,
|
||||||
.nlanes = 1,
|
.nlanes = 1,
|
||||||
|
@ -1314,6 +1424,7 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset_control_assert(qmp->ufs_reset);
|
||||||
if (cfg->has_phy_com_ctrl) {
|
if (cfg->has_phy_com_ctrl) {
|
||||||
qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
|
qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
|
||||||
SERDES_START | PCS_START);
|
SERDES_START | PCS_START);
|
||||||
|
@ -1335,8 +1446,7 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PHY Initialization */
|
static int qcom_qmp_phy_enable(struct phy *phy)
|
||||||
static int qcom_qmp_phy_init(struct phy *phy)
|
|
||||||
{
|
{
|
||||||
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
struct qmp_phy *qphy = phy_get_drvdata(phy);
|
||||||
struct qcom_qmp *qmp = qphy->qmp;
|
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");
|
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);
|
ret = qcom_qmp_phy_com_init(qphy);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1383,14 +1520,9 @@ static int qcom_qmp_phy_init(struct phy *phy)
|
||||||
cfg->rx_tbl, cfg->rx_tbl_num);
|
cfg->rx_tbl, cfg->rx_tbl_num);
|
||||||
|
|
||||||
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
|
||||||
|
ret = reset_control_deassert(qmp->ufs_reset);
|
||||||
/*
|
if (ret)
|
||||||
* UFS PHY requires the deassert of software reset before serdes start.
|
goto err_lane_rst;
|
||||||
* 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;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pull out PHY from POWER DOWN state.
|
* 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);
|
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
|
||||||
|
|
||||||
/* Pull PHY out of reset state */
|
/* Pull PHY out of reset state */
|
||||||
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
if (!cfg->no_pcs_sw_reset)
|
||||||
|
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
|
||||||
|
|
||||||
if (cfg->has_phy_dp_com_ctrl)
|
if (cfg->has_phy_dp_com_ctrl)
|
||||||
qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
|
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;
|
goto err_pcs_ready;
|
||||||
}
|
}
|
||||||
qmp->phy_initialized = true;
|
qmp->phy_initialized = true;
|
||||||
|
return 0;
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
err_pcs_ready:
|
err_pcs_ready:
|
||||||
|
reset_control_assert(qmp->ufs_reset);
|
||||||
clk_disable_unprepare(qphy->pipe_clk);
|
clk_disable_unprepare(qphy->pipe_clk);
|
||||||
err_clk_enable:
|
err_clk_enable:
|
||||||
if (cfg->has_lane_rst)
|
if (cfg->has_lane_rst)
|
||||||
|
@ -1435,7 +1568,7 @@ err_lane_rst:
|
||||||
return ret;
|
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 qmp_phy *qphy = phy_get_drvdata(phy);
|
||||||
struct qcom_qmp *qmp = qphy->qmp;
|
struct qcom_qmp *qmp = qphy->qmp;
|
||||||
|
@ -1463,44 +1596,6 @@ static int qcom_qmp_phy_exit(struct phy *phy)
|
||||||
return 0;
|
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,
|
static int qcom_qmp_phy_set_mode(struct phy *phy,
|
||||||
enum phy_mode mode, int submode)
|
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 = {
|
static const struct phy_ops qcom_qmp_phy_gen_ops = {
|
||||||
.init = qcom_qmp_phy_init,
|
.init = qcom_qmp_phy_enable,
|
||||||
.exit = qcom_qmp_phy_exit,
|
.exit = qcom_qmp_phy_disable,
|
||||||
.power_on = qcom_qmp_phy_poweron,
|
.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,
|
.set_mode = qcom_qmp_phy_set_mode,
|
||||||
.owner = THIS_MODULE,
|
.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 qcom_qmp *qmp = dev_get_drvdata(dev);
|
||||||
struct phy *generic_phy;
|
struct phy *generic_phy;
|
||||||
struct qmp_phy *qphy;
|
struct qmp_phy *qphy;
|
||||||
|
const struct phy_ops *ops = &qcom_qmp_phy_gen_ops;
|
||||||
char prop_name[MAX_PROP_NAME];
|
char prop_name[MAX_PROP_NAME];
|
||||||
int ret;
|
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)) {
|
if (IS_ERR(generic_phy)) {
|
||||||
ret = PTR_ERR(generic_phy);
|
ret = PTR_ERR(generic_phy);
|
||||||
dev_err(dev, "failed to create qphy %d\n", ret);
|
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",
|
.compatible = "qcom,msm8996-qmp-usb3-phy",
|
||||||
.data = &msm8996_usb3phy_cfg,
|
.data = &msm8996_usb3phy_cfg,
|
||||||
|
}, {
|
||||||
|
.compatible = "qcom,msm8998-qmp-pcie-phy",
|
||||||
|
.data = &msm8998_pciephy_cfg,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "qcom,msm8998-qmp-ufs-phy",
|
.compatible = "qcom,msm8998-qmp-ufs-phy",
|
||||||
.data = &sdm845_ufsphy_cfg,
|
.data = &sdm845_ufsphy_cfg,
|
||||||
|
|
|
@ -241,6 +241,7 @@
|
||||||
#define QSERDES_V3_RX_RX_BAND 0x110
|
#define QSERDES_V3_RX_RX_BAND 0x110
|
||||||
#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c
|
#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c
|
||||||
#define QSERDES_V3_RX_RX_MODE_00 0x164
|
#define QSERDES_V3_RX_RX_MODE_00 0x164
|
||||||
|
#define QSERDES_V3_RX_RX_MODE_01 0x168
|
||||||
|
|
||||||
/* Only for QMP V3 PHY - PCS registers */
|
/* Only for QMP V3 PHY - PCS registers */
|
||||||
#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004
|
#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004
|
||||||
|
@ -280,6 +281,7 @@
|
||||||
#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c
|
#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c
|
||||||
#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
|
#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
|
||||||
#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4
|
#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_LFPS_TX_ECSTART_EQTLOCK 0x0b0
|
||||||
#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8
|
#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8
|
||||||
#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc
|
#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_MIN_HIBERN8_TIME 0x138
|
||||||
#define QPHY_V3_PCS_RX_SIGDET_CTRL1 0x13c
|
#define QPHY_V3_PCS_RX_SIGDET_CTRL1 0x13c
|
||||||
#define QPHY_V3_PCS_RX_SIGDET_CTRL2 0x140
|
#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_TX_MID_TERM_CTRL1 0x1bc
|
||||||
#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4
|
#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4
|
||||||
#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
|
#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_CONFIG1 0x20c
|
||||||
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210
|
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210
|
||||||
|
|
||||||
/* Only for QMP V3 PHY - PCS_MISC registers */
|
/* Only for QMP V3 PHY - PCS_MISC registers */
|
||||||
#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c
|
#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
|
#endif
|
||||||
|
|
|
@ -822,14 +822,9 @@ static int qusb2_phy_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
qphy->iface_clk = devm_clk_get(dev, "iface");
|
qphy->iface_clk = devm_clk_get_optional(dev, "iface");
|
||||||
if (IS_ERR(qphy->iface_clk)) {
|
if (IS_ERR(qphy->iface_clk))
|
||||||
ret = PTR_ERR(qphy->iface_clk);
|
return 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->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0);
|
qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0);
|
||||||
if (IS_ERR(qphy->phy_reset)) {
|
if (IS_ERR(qphy->phy_reset)) {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/phy/phy.h>
|
#include <linux/phy/phy.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
@ -96,11 +97,10 @@ struct ufs_qcom_phy {
|
||||||
char name[UFS_QCOM_PHY_NAME_LEN];
|
char name[UFS_QCOM_PHY_NAME_LEN];
|
||||||
struct ufs_qcom_phy_calibration *cached_regs;
|
struct ufs_qcom_phy_calibration *cached_regs;
|
||||||
int cached_regs_table_size;
|
int cached_regs_table_size;
|
||||||
bool is_powered_on;
|
|
||||||
bool is_started;
|
|
||||||
struct ufs_qcom_phy_specific_ops *phy_spec_ops;
|
struct ufs_qcom_phy_specific_ops *phy_spec_ops;
|
||||||
|
|
||||||
enum phy_mode mode;
|
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
|
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
|
||||||
*/
|
*/
|
||||||
struct ufs_qcom_phy_specific_ops {
|
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);
|
void (*start_serdes)(struct ufs_qcom_phy *phy);
|
||||||
int (*is_physical_coding_sublayer_ready)(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);
|
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;
|
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
|
static
|
||||||
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy,
|
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy,
|
||||||
enum phy_mode mode, int submode)
|
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 = {
|
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_on = ufs_qcom_phy_power_on,
|
||||||
.power_off = ufs_qcom_phy_power_off,
|
.power_off = ufs_qcom_phy_power_off,
|
||||||
.set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
|
.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 = {
|
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,
|
.start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
|
||||||
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
|
.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,
|
.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;
|
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
|
static
|
||||||
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy,
|
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy,
|
||||||
enum phy_mode mode, int submode)
|
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 = {
|
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_on = ufs_qcom_phy_power_on,
|
||||||
.power_off = ufs_qcom_phy_power_off,
|
.power_off = ufs_qcom_phy_power_off,
|
||||||
.set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
|
.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 = {
|
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,
|
.start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
|
||||||
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
|
.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,
|
.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);
|
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,
|
static int __ufs_qcom_phy_clk_get(struct device *dev,
|
||||||
const char *name, struct clk **clk_out, bool err_print)
|
const char *name, struct clk **clk_out, bool err_print)
|
||||||
{
|
{
|
||||||
|
@ -528,23 +543,38 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
|
||||||
{
|
{
|
||||||
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
|
||||||
struct device *dev = phy_common->dev;
|
struct device *dev = phy_common->dev;
|
||||||
|
bool is_rate_B = false;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (phy_common->is_powered_on)
|
err = ufs_qcom_phy_get_reset(phy_common);
|
||||||
return 0;
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
if (!phy_common->is_started) {
|
err = reset_control_assert(phy_common->ufs_reset);
|
||||||
err = ufs_qcom_phy_start_serdes(phy_common);
|
if (err)
|
||||||
if (err)
|
return err;
|
||||||
return err;
|
|
||||||
|
|
||||||
err = ufs_qcom_phy_is_pcs_ready(phy_common);
|
if (phy_common->mode == PHY_MODE_UFS_HS_B)
|
||||||
if (err)
|
is_rate_B = true;
|
||||||
return err;
|
|
||||||
|
|
||||||
phy_common->is_started = 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = ufs_qcom_phy_start_serdes(phy_common);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = ufs_qcom_phy_is_pcs_ready(phy_common);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy);
|
err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
|
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;
|
goto out;
|
||||||
|
|
||||||
out_disable_ref_clk:
|
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);
|
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);
|
phy_common->phy_spec_ops->power_control(phy_common, false);
|
||||||
|
|
||||||
if (phy_common->vddp_ref_clk.reg)
|
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_pll);
|
||||||
ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
|
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
|
||||||
|
|
|
@ -19,7 +19,7 @@ config PHY_RCAR_GEN3_PCIE
|
||||||
config PHY_RCAR_GEN3_USB2
|
config PHY_RCAR_GEN3_USB2
|
||||||
tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
|
tristate "Renesas R-Car generation 3 USB 2.0 PHY driver"
|
||||||
depends on ARCH_RENESAS
|
depends on ARCH_RENESAS
|
||||||
depends on EXTCON
|
depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in
|
||||||
depends on USB_SUPPORT
|
depends on USB_SUPPORT
|
||||||
select GENERIC_PHY
|
select GENERIC_PHY
|
||||||
select USB_COMMON
|
select USB_COMMON
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Renesas Solutions Corp.
|
* Copyright (C) 2014 Renesas Solutions Corp.
|
||||||
* Copyright (C) 2014 Cogent Embedded, Inc.
|
* Copyright (C) 2014 Cogent Embedded, Inc.
|
||||||
|
* Copyright (C) 2019 Renesas Electronics Corp.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
@ -15,6 +16,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
|
||||||
#define USBHS_LPSTS 0x02
|
#define USBHS_LPSTS 0x02
|
||||||
#define USBHS_UGCTRL 0x80
|
#define USBHS_UGCTRL 0x80
|
||||||
|
@ -35,6 +37,8 @@
|
||||||
#define USBHS_UGCTRL2_USB0SEL 0x00000030
|
#define USBHS_UGCTRL2_USB0SEL 0x00000030
|
||||||
#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010
|
#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010
|
||||||
#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030
|
#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) */
|
/* USB General status register (UGSTS) */
|
||||||
#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */
|
#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_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)
|
static int rcar_gen2_phy_init(struct phy *p)
|
||||||
{
|
{
|
||||||
struct rcar_gen2_phy *phy = phy_get_drvdata(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;
|
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 = {
|
static const struct phy_ops rcar_gen2_phy_ops = {
|
||||||
.init = rcar_gen2_phy_init,
|
.init = rcar_gen2_phy_init,
|
||||||
.exit = rcar_gen2_phy_exit,
|
.exit = rcar_gen2_phy_exit,
|
||||||
|
@ -188,12 +251,55 @@ static const struct phy_ops rcar_gen2_phy_ops = {
|
||||||
.owner = THIS_MODULE,
|
.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[] = {
|
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-r8a77470",
|
||||||
{ .compatible = "renesas,usb-phy-r8a7794" },
|
.data = &rz_g1c_usb_phy_data,
|
||||||
{ .compatible = "renesas,rcar-gen2-usb-phy" },
|
},
|
||||||
{ }
|
{
|
||||||
|
.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);
|
MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
|
||||||
|
|
||||||
|
@ -224,11 +330,6 @@ static const u32 select_mask[] = {
|
||||||
[2] = USBHS_UGCTRL2_USB2SEL,
|
[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)
|
static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
@ -238,6 +339,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
const struct rcar_gen2_phy_data *data;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (!dev->of_node) {
|
if (!dev->of_node) {
|
||||||
|
@ -266,6 +368,10 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||||
drv->clk = clk;
|
drv->clk = clk;
|
||||||
drv->base = base;
|
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->num_channels = of_get_child_count(dev->of_node);
|
||||||
drv->channels = devm_kcalloc(dev, drv->num_channels,
|
drv->channels = devm_kcalloc(dev, drv->num_channels,
|
||||||
sizeof(struct rcar_gen2_channel),
|
sizeof(struct rcar_gen2_channel),
|
||||||
|
@ -294,10 +400,10 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
phy->channel = channel;
|
phy->channel = channel;
|
||||||
phy->number = n;
|
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,
|
phy->phy = devm_phy_create(dev, NULL,
|
||||||
&rcar_gen2_phy_ops);
|
data->gen2_phy_ops);
|
||||||
if (IS_ERR(phy->phy)) {
|
if (IS_ERR(phy->phy)) {
|
||||||
dev_err(dev, "Failed to create PHY\n");
|
dev_err(dev, "Failed to create PHY\n");
|
||||||
return PTR_ERR(phy->phy);
|
return PTR_ERR(phy->phy);
|
||||||
|
|
|
@ -37,11 +37,8 @@
|
||||||
|
|
||||||
/* INT_ENABLE */
|
/* INT_ENABLE */
|
||||||
#define USB2_INT_ENABLE_UCOM_INTEN BIT(3)
|
#define USB2_INT_ENABLE_UCOM_INTEN BIT(3)
|
||||||
#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2)
|
#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) /* For EHCI */
|
||||||
#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1)
|
#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) /* For OHCI */
|
||||||
#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \
|
|
||||||
USB2_INT_ENABLE_USBH_INTB_EN | \
|
|
||||||
USB2_INT_ENABLE_USBH_INTA_EN)
|
|
||||||
|
|
||||||
/* USBCTR */
|
/* USBCTR */
|
||||||
#define USB2_USBCTR_DIRPD BIT(2)
|
#define USB2_USBCTR_DIRPD BIT(2)
|
||||||
|
@ -78,10 +75,35 @@
|
||||||
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
|
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
|
||||||
#define USB2_ADPCTRL_DRVVBUS BIT(4)
|
#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 {
|
struct rcar_gen3_chan {
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
struct device *dev; /* platform_device's device */
|
||||||
struct extcon_dev *extcon;
|
struct extcon_dev *extcon;
|
||||||
struct phy *phy;
|
struct rcar_gen3_phy rphys[NUM_OF_PHYS];
|
||||||
struct regulator *vbus;
|
struct regulator *vbus;
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
enum usb_dr_mode dr_mode;
|
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;
|
void __iomem *usb2_base = ch->base;
|
||||||
u32 val = readl(usb2_base + USB2_COMMCTRL);
|
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)
|
if (host)
|
||||||
val &= ~USB2_COMMCTRL_OTG_PERI;
|
val &= ~USB2_COMMCTRL_OTG_PERI;
|
||||||
else
|
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;
|
void __iomem *usb2_base = ch->base;
|
||||||
u32 val = readl(usb2_base + USB2_LINECTRL1);
|
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);
|
val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD);
|
||||||
if (dp)
|
if (dp)
|
||||||
val |= USB2_LINECTRL1_DP_RPD;
|
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;
|
void __iomem *usb2_base = ch->base;
|
||||||
u32 val = readl(usb2_base + USB2_ADPCTRL);
|
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)
|
if (vbus)
|
||||||
val |= USB2_ADPCTRL_DRVVBUS;
|
val |= USB2_ADPCTRL_DRVVBUS;
|
||||||
else
|
else
|
||||||
|
@ -249,6 +271,42 @@ static enum phy_mode rcar_gen3_get_phy_mode(struct rcar_gen3_chan *ch)
|
||||||
return PHY_MODE_USB_DEVICE;
|
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,
|
static ssize_t role_store(struct device *dev, struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
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;
|
bool is_b_device;
|
||||||
enum phy_mode cur_mode, new_mode;
|
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;
|
return -EIO;
|
||||||
|
|
||||||
if (!strncmp(buf, "host", strlen("host")))
|
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);
|
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 -EIO;
|
||||||
|
|
||||||
return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" :
|
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)
|
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;
|
void __iomem *usb2_base = channel->base;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
/* Initialize USB2 part */
|
/* 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_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
|
||||||
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
|
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
|
||||||
|
|
||||||
/* Initialize otg part */
|
/* Initialize otg part */
|
||||||
if (channel->is_otg_channel)
|
if (channel->is_otg_channel) {
|
||||||
rcar_gen3_init_otg(channel);
|
if (rcar_gen3_needs_init_otg(channel))
|
||||||
|
rcar_gen3_init_otg(channel);
|
||||||
|
rphy->otg_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
rphy->initialized = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_gen3_phy_usb2_exit(struct phy *p)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
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;
|
void __iomem *usb2_base = channel->base;
|
||||||
u32 val;
|
u32 val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (!rcar_gen3_are_all_rphys_power_off(channel))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (channel->vbus) {
|
if (channel->vbus) {
|
||||||
ret = regulator_enable(channel->vbus);
|
ret = regulator_enable(channel->vbus);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -371,14 +454,22 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
|
||||||
val &= ~USB2_USBCTR_PLL_RST;
|
val &= ~USB2_USBCTR_PLL_RST;
|
||||||
writel(val, usb2_base + USB2_USBCTR);
|
writel(val, usb2_base + USB2_USBCTR);
|
||||||
|
|
||||||
|
rphy->powered = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_gen3_phy_usb2_power_off(struct phy *p)
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
|
rphy->powered = false;
|
||||||
|
|
||||||
|
if (!rcar_gen3_are_all_rphys_power_off(channel))
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (channel->vbus)
|
if (channel->vbus)
|
||||||
ret = regulator_disable(channel->vbus);
|
ret = regulator_disable(channel->vbus);
|
||||||
|
|
||||||
|
@ -393,6 +484,12 @@ static const struct phy_ops rcar_gen3_phy_usb2_ops = {
|
||||||
.owner = THIS_MODULE,
|
.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)
|
static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
|
||||||
{
|
{
|
||||||
struct rcar_gen3_chan *ch = _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;
|
irqreturn_t ret = IRQ_NONE;
|
||||||
|
|
||||||
if (status & USB2_OBINT_BITS) {
|
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);
|
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
|
||||||
rcar_gen3_device_recognition(ch);
|
rcar_gen3_device_recognition(ch);
|
||||||
ret = IRQ_HANDLED;
|
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[] = {
|
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-r8a77470",
|
||||||
{ .compatible = "renesas,usb2-phy-r8a77965" },
|
.data = &rz_g1c_phy_usb2_ops,
|
||||||
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
|
},
|
||||||
{ }
|
{
|
||||||
|
.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);
|
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
|
||||||
|
|
||||||
|
@ -425,13 +538,54 @@ static const unsigned int rcar_gen3_phy_cable[] = {
|
||||||
EXTCON_NONE,
|
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)
|
static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct rcar_gen3_chan *channel;
|
struct rcar_gen3_chan *channel;
|
||||||
struct phy_provider *provider;
|
struct phy_provider *provider;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int irq, ret = 0;
|
const struct phy_ops *phy_usb2_ops;
|
||||||
|
int irq, ret = 0, i;
|
||||||
|
|
||||||
if (!dev->of_node) {
|
if (!dev->of_node) {
|
||||||
dev_err(dev, "This driver needs device tree\n");
|
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);
|
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) {
|
if (channel->dr_mode != USB_DR_MODE_UNKNOWN) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -481,11 +635,21 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||||
* And then, phy-core will manage runtime pm for this device.
|
* And then, phy-core will manage runtime pm for this device.
|
||||||
*/
|
*/
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops);
|
phy_usb2_ops = of_device_get_match_data(dev);
|
||||||
if (IS_ERR(channel->phy)) {
|
if (!phy_usb2_ops)
|
||||||
dev_err(dev, "Failed to create USB2 PHY\n");
|
return -EINVAL;
|
||||||
ret = PTR_ERR(channel->phy);
|
|
||||||
goto error;
|
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->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");
|
channel->vbus = devm_regulator_get_optional(dev, "vbus");
|
||||||
|
@ -498,9 +662,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, channel);
|
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)) {
|
if (IS_ERR(provider)) {
|
||||||
dev_err(dev, "Failed to register PHY provider\n");
|
dev_err(dev, "Failed to register PHY provider\n");
|
||||||
ret = PTR_ERR(provider);
|
ret = PTR_ERR(provider);
|
||||||
|
|
|
@ -87,6 +87,7 @@ struct rockchip_emmc_phy {
|
||||||
unsigned int reg_offset;
|
unsigned int reg_offset;
|
||||||
struct regmap *reg_base;
|
struct regmap *reg_base;
|
||||||
struct clk *emmcclk;
|
struct clk *emmcclk;
|
||||||
|
unsigned int drive_impedance;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
|
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);
|
struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
|
||||||
|
|
||||||
/* Drive impedance: 50 Ohm */
|
/* Drive impedance: from DTS */
|
||||||
regmap_write(rk_phy->reg_base,
|
regmap_write(rk_phy->reg_base,
|
||||||
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
rk_phy->reg_offset + GRF_EMMCPHY_CON6,
|
||||||
HIWORD_UPDATE(PHYCTRL_DR_50OHM,
|
HIWORD_UPDATE(rk_phy->drive_impedance,
|
||||||
PHYCTRL_DR_MASK,
|
PHYCTRL_DR_MASK,
|
||||||
PHYCTRL_DR_SHIFT));
|
PHYCTRL_DR_SHIFT));
|
||||||
|
|
||||||
|
@ -314,6 +315,26 @@ static const struct phy_ops ops = {
|
||||||
.owner = THIS_MODULE,
|
.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)
|
static int rockchip_emmc_phy_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
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 phy_provider *phy_provider;
|
||||||
struct regmap *grf;
|
struct regmap *grf;
|
||||||
unsigned int reg_offset;
|
unsigned int reg_offset;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
if (!dev->parent || !dev->parent->of_node)
|
if (!dev->parent || !dev->parent->of_node)
|
||||||
return -ENODEV;
|
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_offset = reg_offset;
|
||||||
rk_phy->reg_base = grf;
|
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);
|
generic_phy = devm_phy_create(dev, dev->of_node, &ops);
|
||||||
if (IS_ERR(generic_phy)) {
|
if (IS_ERR(generic_phy)) {
|
||||||
|
|
|
@ -335,13 +335,9 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(priv->clk_parent))
|
if (IS_ERR(priv->clk_parent))
|
||||||
return PTR_ERR(priv->clk_parent);
|
return PTR_ERR(priv->clk_parent);
|
||||||
|
|
||||||
priv->clk_ext = devm_clk_get(dev, "phy-ext");
|
priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
|
||||||
if (IS_ERR(priv->clk_ext)) {
|
if (IS_ERR(priv->clk_ext))
|
||||||
if (PTR_ERR(priv->clk_ext) == -ENOENT)
|
return PTR_ERR(priv->clk_ext);
|
||||||
priv->clk_ext = NULL;
|
|
||||||
else
|
|
||||||
return PTR_ERR(priv->clk_ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->rst = devm_reset_control_get_shared(dev, "phy");
|
priv->rst = devm_reset_control_get_shared(dev, "phy");
|
||||||
if (IS_ERR(priv->rst))
|
if (IS_ERR(priv->rst))
|
||||||
|
|
|
@ -238,13 +238,9 @@ static int uniphier_u3ssphy_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(priv->clk))
|
if (IS_ERR(priv->clk))
|
||||||
return PTR_ERR(priv->clk);
|
return PTR_ERR(priv->clk);
|
||||||
|
|
||||||
priv->clk_ext = devm_clk_get(dev, "phy-ext");
|
priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
|
||||||
if (IS_ERR(priv->clk_ext)) {
|
if (IS_ERR(priv->clk_ext))
|
||||||
if (PTR_ERR(priv->clk_ext) == -ENOENT)
|
return PTR_ERR(priv->clk_ext);
|
||||||
priv->clk_ext = NULL;
|
|
||||||
else
|
|
||||||
return PTR_ERR(priv->clk_ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
priv->rst = devm_reset_control_get_shared(dev, "phy");
|
priv->rst = devm_reset_control_get_shared(dev, "phy");
|
||||||
if (IS_ERR(priv->rst))
|
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_124_SOC) += xusb-tegra124.o
|
||||||
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_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_210_SOC) += xusb-tegra210.o
|
||||||
|
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
|
||||||
|
|
|
@ -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
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* 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",
|
.compatible = "nvidia,tegra210-xusb-padctl",
|
||||||
.data = &tegra210_xusb_padctl_soc,
|
.data = &tegra210_xusb_padctl_soc,
|
||||||
},
|
},
|
||||||
|
#endif
|
||||||
|
#if defined(CONFIG_ARCH_TEGRA_186_SOC)
|
||||||
|
{
|
||||||
|
.compatible = "nvidia,tegra186-xusb-padctl",
|
||||||
|
.data = &tegra186_xusb_padctl_soc,
|
||||||
|
},
|
||||||
#endif
|
#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;
|
const struct tegra_xusb_lane_soc *soc = lane->soc;
|
||||||
u32 value;
|
u32 value;
|
||||||
|
|
||||||
|
/* skip single function lanes */
|
||||||
|
if (soc->num_funcs < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
/* choose function */
|
/* choose function */
|
||||||
value = padctl_readl(padctl, soc->offset);
|
value = padctl_readl(padctl, soc->offset);
|
||||||
value &= ~(soc->mask << soc->shift);
|
value &= ~(soc->mask << soc->shift);
|
||||||
|
@ -542,13 +552,34 @@ static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
|
||||||
device_unregister(&port->dev);
|
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)
|
static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
|
||||||
{
|
{
|
||||||
struct tegra_xusb_port *port = &usb2->base;
|
struct tegra_xusb_port *port = &usb2->base;
|
||||||
struct device_node *np = port->dev.of_node;
|
struct device_node *np = port->dev.of_node;
|
||||||
|
const char *mode;
|
||||||
|
|
||||||
usb2->internal = of_property_read_bool(np, "nvidia,internal");
|
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");
|
usb2->supply = devm_regulator_get(&port->dev, "vbus");
|
||||||
return PTR_ERR_OR_ZERO(usb2->supply);
|
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;
|
struct tegra_xusb_padctl *padctl;
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
unsigned int i;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* for backwards compatibility with old device trees */
|
/* for backwards compatibility with old device trees */
|
||||||
|
@ -876,14 +908,38 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
||||||
goto remove;
|
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);
|
err = reset_control_deassert(padctl->rst);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto remove;
|
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);
|
err = tegra_xusb_setup_pads(padctl);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
dev_err(&pdev->dev, "failed to setup pads: %d\n", err);
|
dev_err(&pdev->dev, "failed to setup pads: %d\n", err);
|
||||||
goto reset;
|
goto power_down;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = tegra_xusb_setup_ports(padctl);
|
err = tegra_xusb_setup_ports(padctl);
|
||||||
|
@ -896,6 +952,8 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
remove_pads:
|
remove_pads:
|
||||||
tegra_xusb_remove_pads(padctl);
|
tegra_xusb_remove_pads(padctl);
|
||||||
|
power_down:
|
||||||
|
regulator_bulk_disable(padctl->soc->num_supplies, padctl->supplies);
|
||||||
reset:
|
reset:
|
||||||
reset_control_assert(padctl->rst);
|
reset_control_assert(padctl->rst);
|
||||||
remove:
|
remove:
|
||||||
|
@ -911,6 +969,11 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
|
||||||
tegra_xusb_remove_ports(padctl);
|
tegra_xusb_remove_ports(padctl);
|
||||||
tegra_xusb_remove_pads(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);
|
err = reset_control_assert(padctl->rst);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
dev_err(&pdev->dev, "failed to assert reset: %d\n", err);
|
dev_err(&pdev->dev, "failed to assert reset: %d\n", err);
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include <linux/usb/otg.h>
|
||||||
|
|
||||||
/* legacy entry points for backwards-compatibility */
|
/* legacy entry points for backwards-compatibility */
|
||||||
int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
|
int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
|
||||||
int tegra_xusb_padctl_legacy_remove(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,
|
int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
|
||||||
struct device_node *np);
|
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_usb2_lane {
|
||||||
struct tegra_xusb_lane base;
|
struct tegra_xusb_lane base;
|
||||||
|
|
||||||
u32 hs_curr_level_offset;
|
u32 hs_curr_level_offset;
|
||||||
|
bool powered_on;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct tegra_xusb_usb2_lane *
|
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);
|
const struct phy_ops *ops);
|
||||||
void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad);
|
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_usb2_pad {
|
||||||
struct tegra_xusb_pad base;
|
struct tegra_xusb_pad base;
|
||||||
|
|
||||||
|
@ -271,6 +297,7 @@ struct tegra_xusb_usb2_port {
|
||||||
struct tegra_xusb_port base;
|
struct tegra_xusb_port base;
|
||||||
|
|
||||||
struct regulator *supply;
|
struct regulator *supply;
|
||||||
|
enum usb_dr_mode mode;
|
||||||
bool internal;
|
bool internal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -367,6 +394,9 @@ struct tegra_xusb_padctl_soc {
|
||||||
} ports;
|
} ports;
|
||||||
|
|
||||||
const struct tegra_xusb_padctl_ops *ops;
|
const struct tegra_xusb_padctl_ops *ops;
|
||||||
|
|
||||||
|
const char * const *supply_names;
|
||||||
|
unsigned int num_supplies;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tegra_xusb_padctl {
|
struct tegra_xusb_padctl {
|
||||||
|
@ -390,6 +420,8 @@ struct tegra_xusb_padctl {
|
||||||
unsigned int enable;
|
unsigned int enable;
|
||||||
|
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
|
struct regulator_bulk_data *supplies;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
|
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)
|
#if defined(CONFIG_ARCH_TEGRA_210_SOC)
|
||||||
extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc;
|
extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc;
|
||||||
#endif
|
#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 */
|
#endif /* __PHY_TEGRA_XUSB_H */
|
||||||
|
|
|
@ -20,6 +20,18 @@ config PHY_DM816X_USB
|
||||||
help
|
help
|
||||||
Enable this for dm816x USB to work.
|
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
|
config OMAP_CONTROL_PHY
|
||||||
tristate "OMAP CONTROL PHY Driver"
|
tristate "OMAP CONTROL PHY Driver"
|
||||||
depends on ARCH_OMAP2PLUS || COMPILE_TEST
|
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_TI_PIPE3) += phy-ti-pipe3.o
|
||||||
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
|
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
|
||||||
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.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
|
obj-$(CONFIG_PHY_TI_GMII_SEL) += phy-gmii-sel.o
|
||||||
|
|
|
@ -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 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_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_PWRCTL_CLK_FREQ_SHIFT 22
|
||||||
|
|
||||||
#define PIPE3_PHY_TX_RX_POWERON 0x3
|
#define PIPE3_PHY_RX_POWERON (0x1 << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT)
|
||||||
#define PIPE3_PHY_TX_RX_POWEROFF 0x0
|
#define PIPE3_PHY_TX_POWERON (0x2 << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT)
|
||||||
|
|
||||||
#define PCIE_PCS_MASK 0xFF0000
|
#define PCIE_PCS_MASK 0xFF0000
|
||||||
#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10
|
#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_MASK GENMASK(31, 27)
|
||||||
#define INTERFACE_SHIFT 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_MASK GENMASK(17, 14)
|
||||||
#define LOSD_SHIFT 14
|
#define LOSD_SHIFT 14
|
||||||
#define MEM_PLLDIV GENMASK(6, 5)
|
#define MEM_PLLDIV GENMASK(6, 5)
|
||||||
|
|
||||||
#define PCIEPHYRX_TRIM 0x0000001C
|
#define PIPE3_PHY_RX_TRIM 0x0000001C
|
||||||
#define MEM_DLL_TRIM_SEL GENMASK(31, 30)
|
#define MEM_DLL_TRIM_SEL_MASK GENMASK(31, 30)
|
||||||
#define MEM_DLL_TRIM_SHIFT 30
|
#define MEM_DLL_TRIM_SHIFT 30
|
||||||
|
|
||||||
#define PCIEPHYRX_DLL 0x00000024
|
#define PIPE3_PHY_RX_DLL 0x00000024
|
||||||
#define MEM_DLL_PHINT_RATE GENMASK(31, 30)
|
#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_FASTLOCK BIT(23)
|
||||||
#define MEM_CDR_LBW GENMASK(22, 21)
|
#define MEM_CDR_FASTLOCK_SHIFT 23
|
||||||
#define MEM_CDR_STEPCNT GENMASK(20, 19)
|
#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_MASK GENMASK(18, 16)
|
||||||
#define MEM_CDR_STL_SHIFT 16
|
#define MEM_CDR_STL_SHIFT 16
|
||||||
#define MEM_CDR_THR_MASK GENMASK(15, 13)
|
#define MEM_CDR_THR_MASK GENMASK(15, 13)
|
||||||
#define MEM_CDR_THR_SHIFT 13
|
#define MEM_CDR_THR_SHIFT 13
|
||||||
#define MEM_CDR_THR_MODE BIT(12)
|
#define MEM_CDR_THR_MODE BIT(12)
|
||||||
#define MEM_CDR_CDR_2NDO_SDM_MODE BIT(11)
|
#define MEM_CDR_THR_MODE_SHIFT 12
|
||||||
#define MEM_OVRD_HS_RATE BIT(26)
|
#define MEM_CDR_2NDO_SDM_MODE BIT(11)
|
||||||
|
#define MEM_CDR_2NDO_SDM_MODE_SHIFT 11
|
||||||
|
|
||||||
#define PCIEPHYRX_EQUALIZER 0x00000038
|
#define PIPE3_PHY_RX_EQUALIZER 0x00000038
|
||||||
#define MEM_EQLEV GENMASK(31, 16)
|
#define MEM_EQLEV_MASK GENMASK(31, 16)
|
||||||
#define MEM_EQFTC GENMASK(15, 11)
|
#define MEM_EQLEV_SHIFT 16
|
||||||
#define MEM_EQCTL GENMASK(10, 7)
|
#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_EQCTL_SHIFT 7
|
||||||
#define MEM_OVRD_EQLEV BIT(2)
|
#define MEM_OVRD_EQLEV BIT(2)
|
||||||
|
#define MEM_OVRD_EQLEV_SHIFT 2
|
||||||
#define MEM_OVRD_EQFTC BIT(1)
|
#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
|
* 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_IDLE_TIME 100 /* in milliseconds */
|
||||||
#define PLL_LOCK_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 {
|
struct pipe3_dpll_params {
|
||||||
u16 m;
|
u16 m;
|
||||||
u8 n;
|
u8 n;
|
||||||
|
@ -123,6 +149,27 @@ struct pipe3_dpll_map {
|
||||||
struct pipe3_dpll_params params;
|
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 {
|
struct ti_pipe3 {
|
||||||
void __iomem *pll_ctrl_base;
|
void __iomem *pll_ctrl_base;
|
||||||
void __iomem *phy_rx;
|
void __iomem *phy_rx;
|
||||||
|
@ -141,6 +188,8 @@ struct ti_pipe3 {
|
||||||
unsigned int power_reg; /* power reg. index within syscon */
|
unsigned int power_reg; /* power reg. index within syscon */
|
||||||
unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */
|
unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */
|
||||||
bool sata_refclk_enabled;
|
bool sata_refclk_enabled;
|
||||||
|
enum pipe3_mode mode;
|
||||||
|
struct pipe3_settings settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct pipe3_dpll_map dpll_map_usb[] = {
|
static struct pipe3_dpll_map dpll_map_usb[] = {
|
||||||
|
@ -163,6 +212,89 @@ static struct pipe3_dpll_map dpll_map_sata[] = {
|
||||||
{ }, /* Terminator */
|
{ }, /* 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)
|
static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset)
|
||||||
{
|
{
|
||||||
return __raw_readl(addr + 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)
|
static int ti_pipe3_power_off(struct phy *x)
|
||||||
{
|
{
|
||||||
u32 val;
|
|
||||||
int ret;
|
int ret;
|
||||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||||
|
|
||||||
|
@ -205,13 +336,13 @@ static int ti_pipe3_power_off(struct phy *x)
|
||||||
return 0;
|
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,
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ti_pipe3_calibrate(struct ti_pipe3 *phy);
|
||||||
|
|
||||||
static int ti_pipe3_power_on(struct phy *x)
|
static int ti_pipe3_power_on(struct phy *x)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
@ -219,6 +350,7 @@ static int ti_pipe3_power_on(struct phy *x)
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
struct ti_pipe3 *phy = phy_get_drvdata(x);
|
||||||
|
bool rx_pending = false;
|
||||||
|
|
||||||
if (!phy->phy_power_syscon) {
|
if (!phy->phy_power_syscon) {
|
||||||
omap_control_phy_power(phy->control_dev, 1);
|
omap_control_phy_power(phy->control_dev, 1);
|
||||||
|
@ -231,14 +363,35 @@ static int ti_pipe3_power_on(struct phy *x)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
rate = rate / 1000000;
|
rate = rate / 1000000;
|
||||||
mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
|
mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK;
|
||||||
OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK;
|
val = rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
|
||||||
val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
|
|
||||||
val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
|
|
||||||
|
|
||||||
ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
|
ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg,
|
||||||
mask, val);
|
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)
|
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)
|
static void ti_pipe3_calibrate(struct ti_pipe3 *phy)
|
||||||
{
|
{
|
||||||
u32 val;
|
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 &= ~(INTERFACE_MASK | LOSD_MASK | MEM_PLLDIV);
|
||||||
val = (0x1 << INTERFACE_SHIFT | 0xA << LOSD_SHIFT);
|
val |= (s->ana_interface << INTERFACE_SHIFT | s->ana_losd << LOSD_SHIFT);
|
||||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY, val);
|
ti_pipe3_writel(phy->phy_rx, PIPE3_PHY_RX_ANA_PROGRAMMABILITY, val);
|
||||||
|
|
||||||
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES);
|
val = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_DIGITAL_MODES);
|
||||||
val &= ~(MEM_CDR_STEPCNT | MEM_CDR_STL_MASK | MEM_CDR_THR_MASK |
|
val &= ~(MEM_HS_RATE_MASK | MEM_OVRD_HS_RATE | MEM_CDR_FASTLOCK |
|
||||||
MEM_CDR_CDR_2NDO_SDM_MODE | MEM_OVRD_HS_RATE);
|
MEM_CDR_LBW_MASK | MEM_CDR_STEPCNT_MASK | MEM_CDR_STL_MASK |
|
||||||
val |= (MEM_CDR_FASTLOCK | MEM_CDR_LBW | 0x3 << MEM_CDR_STL_SHIFT |
|
MEM_CDR_THR_MASK | MEM_CDR_THR_MODE | MEM_CDR_2NDO_SDM_MODE);
|
||||||
0x1 << MEM_CDR_THR_SHIFT | MEM_CDR_THR_MODE);
|
val |= s->dig_hs_rate << MEM_HS_RATE_SHIFT |
|
||||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES, val);
|
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 = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_TRIM);
|
||||||
val &= ~MEM_DLL_TRIM_SEL;
|
val &= ~MEM_DLL_TRIM_SEL_MASK;
|
||||||
val |= 0x2 << MEM_DLL_TRIM_SHIFT;
|
val |= s->dll_trim_sel << MEM_DLL_TRIM_SHIFT;
|
||||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_TRIM, val);
|
ti_pipe3_writel(phy->phy_rx, PIPE3_PHY_RX_TRIM, val);
|
||||||
|
|
||||||
val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DLL);
|
val = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_DLL);
|
||||||
val |= MEM_DLL_PHINT_RATE;
|
val &= ~MEM_DLL_PHINT_RATE_MASK;
|
||||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DLL, val);
|
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 = ti_pipe3_readl(phy->phy_rx, PIPE3_PHY_RX_EQUALIZER);
|
||||||
val &= ~(MEM_EQLEV | MEM_EQCTL | MEM_OVRD_EQLEV | MEM_OVRD_EQFTC);
|
val &= ~(MEM_EQLEV_MASK | MEM_EQFTC_MASK | MEM_EQCTL_MASK |
|
||||||
val |= MEM_EQFTC | 0x1 << MEM_EQCTL_SHIFT;
|
MEM_OVRD_EQLEV | MEM_OVRD_EQFTC);
|
||||||
ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_EQUALIZER, val);
|
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)
|
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
|
* as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table
|
||||||
* 18-1804.
|
* 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) {
|
if (!phy->pcs_syscon) {
|
||||||
omap_control_pcie_pcs(phy->control_dev, 0x96);
|
omap_control_pcie_pcs(phy->control_dev, 0x96);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -349,12 +525,7 @@ static int ti_pipe3_init(struct phy *x)
|
||||||
val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
|
val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT;
|
||||||
ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
|
ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg,
|
||||||
PCIE_PCS_MASK, val);
|
PCIE_PCS_MASK, val);
|
||||||
if (ret)
|
return ret;
|
||||||
return ret;
|
|
||||||
|
|
||||||
ti_pipe3_calibrate(phy);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bring it out of IDLE if it is IDLE */
|
/* 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 */
|
/* SATA has issues if re-programmed when locked */
|
||||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
|
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
|
||||||
if ((val & PLL_LOCK) && of_device_is_compatible(phy->dev->of_node,
|
if ((val & PLL_LOCK) && phy->mode == PIPE3_MODE_SATA)
|
||||||
"ti,phy-pipe3-sata"))
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Program the DPLL */
|
/* Program the DPLL */
|
||||||
|
@ -378,6 +548,8 @@ static int ti_pipe3_init(struct phy *x)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ti_pipe3_calibrate(phy);
|
||||||
|
|
||||||
return ret;
|
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
|
/* If dpll_reset_syscon is not present we wont power down SATA DPLL
|
||||||
* due to Errata i783
|
* due to Errata i783
|
||||||
*/
|
*/
|
||||||
if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") &&
|
if (phy->mode == PIPE3_MODE_SATA && !phy->dpll_reset_syscon)
|
||||||
!phy->dpll_reset_syscon)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* PCIe doesn't have internal DPLL */
|
/* 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 */
|
/* Put DPLL in IDLE mode */
|
||||||
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
|
||||||
val |= PLL_IDLE;
|
val |= PLL_IDLE;
|
||||||
|
@ -418,7 +589,7 @@ static int ti_pipe3_exit(struct phy *x)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* i783: SATA needs control bit toggle after PLL unlock */
|
/* 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,
|
regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg,
|
||||||
SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET);
|
SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET);
|
||||||
regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg,
|
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 clk *clk;
|
||||||
struct device *dev = phy->dev;
|
struct device *dev = phy->dev;
|
||||||
struct device_node *node = dev->of_node;
|
|
||||||
|
|
||||||
phy->refclk = devm_clk_get(dev, "refclk");
|
phy->refclk = devm_clk_get(dev, "refclk");
|
||||||
if (IS_ERR(phy->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
|
/* older DTBs have missing refclk in SATA PHY
|
||||||
* so don't bail out in case of 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);
|
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");
|
phy->wkupclk = devm_clk_get(dev, "wkupclk");
|
||||||
if (IS_ERR(phy->wkupclk)) {
|
if (IS_ERR(phy->wkupclk)) {
|
||||||
dev_err(dev, "unable to get wkupclk\n");
|
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);
|
phy->wkupclk = ERR_PTR(-ENODEV);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") ||
|
if (phy->mode != PIPE3_MODE_PCIE || phy->phy_power_syscon) {
|
||||||
phy->phy_power_syscon) {
|
|
||||||
phy->sys_clk = devm_clk_get(dev, "sysclk");
|
phy->sys_clk = devm_clk_get(dev, "sysclk");
|
||||||
if (IS_ERR(phy->sys_clk)) {
|
if (IS_ERR(phy->sys_clk)) {
|
||||||
dev_err(dev, "unable to get sysclk\n");
|
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");
|
clk = devm_clk_get(dev, "dpll_ref");
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
dev_err(dev, "unable to get dpll ref clk\n");
|
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;
|
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,
|
phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node,
|
||||||
"syscon-pcs");
|
"syscon-pcs");
|
||||||
if (IS_ERR(phy->pcs_syscon)) {
|
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,
|
phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
|
||||||
"syscon-pllreset");
|
"syscon-pllreset");
|
||||||
if (IS_ERR(phy->dpll_reset_syscon)) {
|
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 resource *res;
|
||||||
struct device *dev = phy->dev;
|
struct device *dev = phy->dev;
|
||||||
struct device_node *node = dev->of_node;
|
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
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,
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||||
"phy_rx");
|
"phy_rx");
|
||||||
phy->phy_rx = devm_ioremap_resource(dev, res);
|
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)
|
static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
const struct of_device_id *match;
|
|
||||||
struct device *dev = phy->dev;
|
struct device *dev = phy->dev;
|
||||||
struct device_node *node = dev->of_node;
|
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
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;
|
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,
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||||
"pll_ctrl");
|
"pll_ctrl");
|
||||||
phy->pll_ctrl_base = devm_ioremap_resource(dev, res);
|
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 ti_pipe3 *phy;
|
||||||
struct phy *generic_phy;
|
struct phy *generic_phy;
|
||||||
struct phy_provider *phy_provider;
|
struct phy_provider *phy_provider;
|
||||||
struct device_node *node = pdev->dev.of_node;
|
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
const struct of_device_id *match;
|
||||||
|
struct pipe3_data *data;
|
||||||
|
|
||||||
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
|
||||||
if (!phy)
|
if (!phy)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
phy->dev = dev;
|
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);
|
ret = ti_pipe3_get_pll_base(phy);
|
||||||
if (ret)
|
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
|
* 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)) {
|
if (!IS_ERR(phy->refclk)) {
|
||||||
clk_prepare_enable(phy->refclk);
|
clk_prepare_enable(phy->refclk);
|
||||||
phy->sata_refclk_enabled = true;
|
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[] = {
|
static const struct of_device_id ti_pipe3_id_table[] = {
|
||||||
{
|
{
|
||||||
.compatible = "ti,phy-usb3",
|
.compatible = "ti,phy-usb3",
|
||||||
.data = dpll_map_usb,
|
.data = &data_usb,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.compatible = "ti,omap-usb3",
|
.compatible = "ti,omap-usb3",
|
||||||
.data = dpll_map_usb,
|
.data = &data_usb,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.compatible = "ti,phy-pipe3-sata",
|
.compatible = "ti,phy-pipe3-sata",
|
||||||
.data = dpll_map_sata,
|
.data = &data_sata,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.compatible = "ti,phy-pipe3-pcie",
|
.compatible = "ti,phy-pipe3-pcie",
|
||||||
|
.data = &data_pcie,
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
|
@ -99,6 +99,7 @@ config SCSI_UFS_DWC_TC_PLATFORM
|
||||||
config SCSI_UFS_QCOM
|
config SCSI_UFS_QCOM
|
||||||
tristate "QCOM specific hooks to UFS controller platform driver"
|
tristate "QCOM specific hooks to UFS controller platform driver"
|
||||||
depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
|
depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
|
||||||
|
select RESET_CONTROLLER
|
||||||
help
|
help
|
||||||
This selects the QCOM specific additions to UFSHCD platform driver.
|
This selects the QCOM specific additions to UFSHCD platform driver.
|
||||||
UFS host on QCOM needs some vendor specific configuration before
|
UFS host on QCOM needs some vendor specific configuration before
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/phy/phy.h>
|
#include <linux/phy/phy.h>
|
||||||
|
#include <linux/reset-controller.h>
|
||||||
|
|
||||||
#include "ufshcd.h"
|
#include "ufshcd.h"
|
||||||
#include "ufshcd-pltfrm.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,
|
static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
|
||||||
u32 clk_cycles);
|
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,
|
static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int len,
|
||||||
const char *prefix, void *priv)
|
const char *prefix, void *priv)
|
||||||
{
|
{
|
||||||
|
@ -255,11 +261,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
|
||||||
if (is_rate_B)
|
if (is_rate_B)
|
||||||
phy_set_mode(phy, PHY_MODE_UFS_HS_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 */
|
/* phy initialization - calibrate the phy */
|
||||||
ret = phy_init(phy);
|
ret = phy_init(phy);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -268,15 +269,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
|
||||||
goto out;
|
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 */
|
/* power on phy - start serdes and phy's power and clocks */
|
||||||
ret = phy_power_on(phy);
|
ret = phy_power_on(phy);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -290,7 +282,6 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_disable_phy:
|
out_disable_phy:
|
||||||
ufs_qcom_assert_reset(hba);
|
|
||||||
phy_exit(phy);
|
phy_exit(phy);
|
||||||
out:
|
out:
|
||||||
return ret;
|
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);
|
ufs_qcom_disable_lane_clks(host);
|
||||||
phy_power_off(phy);
|
phy_power_off(phy);
|
||||||
|
|
||||||
/* Assert PHY soft reset */
|
} else if (!ufs_qcom_is_link_active(hba)) {
|
||||||
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)) {
|
|
||||||
ufs_qcom_disable_lane_clks(host);
|
ufs_qcom_disable_lane_clks(host);
|
||||||
phy_power_off(phy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
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;
|
struct phy *phy = host->generic_phy;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = phy_power_on(phy);
|
if (ufs_qcom_is_link_off(hba)) {
|
||||||
if (err) {
|
err = phy_power_on(phy);
|
||||||
dev_err(hba->dev, "%s: failed enabling regs, err = %d\n",
|
if (err) {
|
||||||
__func__, err);
|
dev_err(hba->dev, "%s: failed PHY power on: %d\n",
|
||||||
goto out;
|
__func__, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ufs_qcom_enable_lane_clks(host);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
} else if (!ufs_qcom_is_link_active(hba)) {
|
||||||
|
err = ufs_qcom_enable_lane_clks(host);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ufs_qcom_enable_lane_clks(host);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
hba->is_sys_suspended = false;
|
hba->is_sys_suspended = false;
|
||||||
|
return 0;
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ufs_qcom_dev_params {
|
struct ufs_qcom_dev_params {
|
||||||
|
@ -1118,8 +1103,6 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (on && (status == POST_CHANGE)) {
|
if (on && (status == POST_CHANGE)) {
|
||||||
phy_power_on(host->generic_phy);
|
|
||||||
|
|
||||||
/* enable the device ref clock for HS mode*/
|
/* enable the device ref clock for HS mode*/
|
||||||
if (ufshcd_is_hs_mode(&hba->pwr_info))
|
if (ufshcd_is_hs_mode(&hba->pwr_info))
|
||||||
ufs_qcom_dev_ref_clk_ctrl(host, true);
|
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)) {
|
if (!ufs_qcom_is_link_active(hba)) {
|
||||||
/* disable device ref_clk */
|
/* disable device ref_clk */
|
||||||
ufs_qcom_dev_ref_clk_ctrl(host, false);
|
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;
|
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;
|
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
|
#define ANDROID_BOOT_DEV_MAX 30
|
||||||
static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
|
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;
|
host->hba = hba;
|
||||||
ufshcd_set_variant(hba, host);
|
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
|
* voting/devoting device ref_clk source is time consuming hence
|
||||||
* skip devoting it during aggressive clock gating. This clock
|
* skip devoting it during aggressive clock gating. This clock
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#ifndef UFS_QCOM_H_
|
#ifndef UFS_QCOM_H_
|
||||||
#define UFS_QCOM_H_
|
#define UFS_QCOM_H_
|
||||||
|
|
||||||
|
#include <linux/reset-controller.h>
|
||||||
|
|
||||||
#define MAX_UFS_QCOM_HOSTS 1
|
#define MAX_UFS_QCOM_HOSTS 1
|
||||||
#define MAX_U32 (~(u32)0)
|
#define MAX_U32 (~(u32)0)
|
||||||
#define MPHY_TX_FSM_STATE 0x41
|
#define MPHY_TX_FSM_STATE 0x41
|
||||||
|
@ -237,6 +239,8 @@ struct ufs_qcom_host {
|
||||||
/* Bitmask for enabling debug prints */
|
/* Bitmask for enabling debug prints */
|
||||||
u32 dbg_print_en;
|
u32 dbg_print_en;
|
||||||
struct ufs_qcom_testbus testbus;
|
struct ufs_qcom_testbus testbus;
|
||||||
|
|
||||||
|
struct reset_controller_dev rcdev;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline u32
|
static inline u32
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
config SUNXI_SRAM
|
config SUNXI_SRAM
|
||||||
bool
|
bool
|
||||||
default ARCH_SUNXI
|
default ARCH_SUNXI
|
||||||
|
select REGMAP_MMIO
|
||||||
help
|
help
|
||||||
Say y here to enable the SRAM controller support. This
|
Say y here to enable the SRAM controller support. This
|
||||||
device is responsible on mapping the SRAM in the sunXi SoCs
|
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))
|
if (IS_ERR(clk))
|
||||||
return PTR_ERR(clk);
|
return PTR_ERR(clk);
|
||||||
|
|
||||||
ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
|
ci->fs_clk = clk = devm_clk_get_optional(&pdev->dev, "fs");
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
return PTR_ERR(clk);
|
||||||
return -EPROBE_DEFER;
|
|
||||||
ci->fs_clk = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||||
ci->base = devm_ioremap_resource(&pdev->dev, res);
|
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_rb *rb = urb->context;
|
||||||
struct acm *acm = rb->instance;
|
struct acm *acm = rb->instance;
|
||||||
unsigned long flags;
|
|
||||||
int status = urb->status;
|
int status = urb->status;
|
||||||
|
bool stopped = false;
|
||||||
|
bool stalled = false;
|
||||||
|
|
||||||
dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
|
dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
|
||||||
rb->index, urb->actual_length, status);
|
rb->index, urb->actual_length, status);
|
||||||
|
|
||||||
set_bit(rb->index, &acm->read_urbs_free);
|
|
||||||
|
|
||||||
if (!acm->dev) {
|
if (!acm->dev) {
|
||||||
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
|
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
|
||||||
return;
|
return;
|
||||||
|
@ -488,15 +487,16 @@ static void acm_read_bulk_callback(struct urb *urb)
|
||||||
break;
|
break;
|
||||||
case -EPIPE:
|
case -EPIPE:
|
||||||
set_bit(EVENT_RX_STALL, &acm->flags);
|
set_bit(EVENT_RX_STALL, &acm->flags);
|
||||||
schedule_work(&acm->work);
|
stalled = true;
|
||||||
return;
|
break;
|
||||||
case -ENOENT:
|
case -ENOENT:
|
||||||
case -ECONNRESET:
|
case -ECONNRESET:
|
||||||
case -ESHUTDOWN:
|
case -ESHUTDOWN:
|
||||||
dev_dbg(&acm->data->dev,
|
dev_dbg(&acm->data->dev,
|
||||||
"%s - urb shutting down with status: %d\n",
|
"%s - urb shutting down with status: %d\n",
|
||||||
__func__, status);
|
__func__, status);
|
||||||
return;
|
stopped = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
dev_dbg(&acm->data->dev,
|
dev_dbg(&acm->data->dev,
|
||||||
"%s - nonzero urb status received: %d\n",
|
"%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
|
* Make sure URB processing is done before marking as free to avoid
|
||||||
* in the same order. Submission has an implict barrier
|
* 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();
|
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 */
|
if (stopped || stalled) {
|
||||||
spin_lock_irqsave(&acm->read_lock, flags);
|
if (stalled)
|
||||||
acm->throttled = acm->throttle_req;
|
schedule_work(&acm->work);
|
||||||
if (!acm->throttled) {
|
return;
|
||||||
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 (test_bit(ACM_THROTTLED, &acm->flags))
|
||||||
|
return;
|
||||||
|
|
||||||
|
acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* data interface wrote those outgoing bytes */
|
/* 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.
|
* Unthrottle device in case the TTY was closed while throttled.
|
||||||
*/
|
*/
|
||||||
spin_lock_irq(&acm->read_lock);
|
clear_bit(ACM_THROTTLED, &acm->flags);
|
||||||
acm->throttled = 0;
|
|
||||||
acm->throttle_req = 0;
|
|
||||||
spin_unlock_irq(&acm->read_lock);
|
|
||||||
|
|
||||||
retval = acm_submit_read_urbs(acm, GFP_KERNEL);
|
retval = acm_submit_read_urbs(acm, GFP_KERNEL);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -826,24 +832,19 @@ static void acm_tty_throttle(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct acm *acm = tty->driver_data;
|
struct acm *acm = tty->driver_data;
|
||||||
|
|
||||||
spin_lock_irq(&acm->read_lock);
|
set_bit(ACM_THROTTLED, &acm->flags);
|
||||||
acm->throttle_req = 1;
|
|
||||||
spin_unlock_irq(&acm->read_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void acm_tty_unthrottle(struct tty_struct *tty)
|
static void acm_tty_unthrottle(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct acm *acm = tty->driver_data;
|
struct acm *acm = tty->driver_data;
|
||||||
unsigned int was_throttled;
|
|
||||||
|
|
||||||
spin_lock_irq(&acm->read_lock);
|
clear_bit(ACM_THROTTLED, &acm->flags);
|
||||||
was_throttled = acm->throttled;
|
|
||||||
acm->throttled = 0;
|
|
||||||
acm->throttle_req = 0;
|
|
||||||
spin_unlock_irq(&acm->read_lock);
|
|
||||||
|
|
||||||
if (was_throttled)
|
/* Matches the smp_mb__after_atomic() in acm_read_bulk_callback(). */
|
||||||
acm_submit_read_urbs(acm, GFP_KERNEL);
|
smp_mb();
|
||||||
|
|
||||||
|
acm_submit_read_urbs(acm, GFP_KERNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acm_tty_break_ctl(struct tty_struct *tty, int state)
|
static int acm_tty_break_ctl(struct tty_struct *tty, int state)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue