phy: for 4.17

*) Add USB PHY driver for MDM6600 on Droid
  *) Add USB PHY driver for STM32 USB PHY Controller
  *) Add inno-usb2-phy driver for hi3798cv200 SoC
  *) Add combo phy driver (SATA/USB/PCIE) for HiSilicon STB SoCs
  *) Add USB3 PHY driver for Meson GXL and GXM
  *) Add support for R8A77965 Gen3 USB 2.0 PHY in phy-rcar-gen3-usb2 driver
  *) Add support for qualcomm QUSB2 V2 and QMP V3 USB3 PHY in phy-qcom-qusb2
     and phy-qcom-qmp PHY driver respectively
  *) Add support for runtime PM in phy-qcom-qusb2 and phy-qcom-qmp PHY drivers
  *) Add support for Allwinner R40 USB PHY in sun4i-usb PHY driver
  *) Add support in rockchip-typec PHY driver to make extcon optional and
     fallback to working in host mode if extcon is missing
  *) Add support in rockchip-typec PHY driver to mux PHYs connected to DP
  *) Add support to configure slew rate parameters in phy-mtk-tphy PHY driver
  *) Add workaround for missing Vbus det interrupts on Allwinner A23/A33
  *) Add USB speed related PHY modes in phy core
  *) Fix PHY 'structure' documentation
  *) Force rockchip-typec PHY to USB2 if DP-only mode is used
  *) Fix phy-qcom-qusb2 and phy-qcom-qmp PHY drivers to follow PHY reset and
     initialization sequence as per hardware programming manual
  *) Fix Marvell BG2CD SoC USB failure in phy-berlin-usb driver
  *) Minor fixes in lpc18xx-usb-otg, xusb-tegra210 and phy-rockchip-emmc PHY
     drivers
 
 Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (GNU/Linux)
 
 iQIcBAABAgAGBQJasMDKAAoJEA5ceFyATYLZZkgP/0fjnSf2JwiJ9QPDigaUu/2V
 NU0ImYkHcRaLS6YH+UegusA8vIwmM5oCA0P8Sbc5EGkCzIpHDRiGwCn5dfT7+a+S
 rx/x2+HxplJ3aXP8acX+DCJqezI+GI7XtuP05AYZ3tIi6smrLsgpdR0pdQ0/CSVM
 Fj7mz06XYR4me94i1LXJqE5YVsRcvwNnNpxaVCRTKWfrtZx9ZlNEFAT6pZ9TeaIM
 tTA0zGmoklpmOJ6w1cvqfQynq8a+7pvydaFmmCP7jGrDlH7G6lNFPDqYco0U0zIe
 U8K7DhdyMq74fXTb+E+xsKnJwYdpXI7bfFeDlmlMevU36wHbjIXZqD3gLIt5Fk3r
 yvkmRd9w6KW7wLF/fITq3fy+k0D/xNshj81+s7q6MhflgQ2TkehR8h8XL+xsdh8J
 MYkC9w+EffpEKms5w35l/CCwEj+/oylewnbDp8ucz3YeTRAQLdjpA8KPv05bJBGb
 nfT00sM287fTyLPGwRek0L1OGDi51TLYEaHRVUwzXvpoRVZAFmO39IQQKedtfqrr
 U0AJPG4kiqQ5BLoIbYo5tvCuR75gEMcrCsjk29FiH8+xgF22Ka96A15yY0p+2M9H
 6cV6qLCVJO5QnI/cP9rMA0QDTx5ia+Rp0FXgpSbAyrb+yRDXhCCeLqLN2YGlVqwU
 Cb2cVZPDy0Xxh+BLMrJg
 =22oc
 -----END PGP SIGNATURE-----

Merge tag 'phy-for-4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next

Kishon writes:

phy: for 4.17

 *) Add USB PHY driver for MDM6600 on Droid
 *) Add USB PHY driver for STM32 USB PHY Controller
 *) Add inno-usb2-phy driver for hi3798cv200 SoC
 *) Add combo phy driver (SATA/USB/PCIE) for HiSilicon STB SoCs
 *) Add USB3 PHY driver for Meson GXL and GXM
 *) Add support for R8A77965 Gen3 USB 2.0 PHY in phy-rcar-gen3-usb2 driver
 *) Add support for qualcomm QUSB2 V2 and QMP V3 USB3 PHY in phy-qcom-qusb2
    and phy-qcom-qmp PHY driver respectively
 *) Add support for runtime PM in phy-qcom-qusb2 and phy-qcom-qmp PHY drivers
 *) Add support for Allwinner R40 USB PHY in sun4i-usb PHY driver
 *) Add support in rockchip-typec PHY driver to make extcon optional and
    fallback to working in host mode if extcon is missing
 *) Add support in rockchip-typec PHY driver to mux PHYs connected to DP
 *) Add support to configure slew rate parameters in phy-mtk-tphy PHY driver
 *) Add workaround for missing Vbus det interrupts on Allwinner A23/A33
 *) Add USB speed related PHY modes in phy core
 *) Fix PHY 'structure' documentation
 *) Force rockchip-typec PHY to USB2 if DP-only mode is used
 *) Fix phy-qcom-qusb2 and phy-qcom-qmp PHY drivers to follow PHY reset and
    initialization sequence as per hardware programming manual
 *) Fix Marvell BG2CD SoC USB failure in phy-berlin-usb driver
 *) Minor fixes in lpc18xx-usb-otg, xusb-tegra210 and phy-rockchip-emmc PHY
    drivers

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
This commit is contained in:
Greg Kroah-Hartman 2018-03-20 10:10:46 +01:00
commit 85a09bf492
45 changed files with 3511 additions and 436 deletions

View File

@ -74,6 +74,29 @@ Example:
reboot-offset = <0x4>; reboot-offset = <0x4>;
}; };
-----------------------------------------------------------------------
Hisilicon Hi3798CV200 Peripheral Controller
The Hi3798CV200 Peripheral Controller controls peripherals, queries
their status, and configures some functions of peripherals.
Required properties:
- compatible: Should contain "hisilicon,hi3798cv200-perictrl", "syscon"
and "simple-mfd".
- reg: Register address and size of Peripheral Controller.
- #address-cells: Should be 1.
- #size-cells: Should be 1.
Examples:
perictrl: peripheral-controller@8a20000 {
compatible = "hisilicon,hi3798cv200-perictrl", "syscon",
"simple-mfd";
reg = <0x8a20000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
};
----------------------------------------------------------------------- -----------------------------------------------------------------------
Hisilicon Hi6220 system controller Hisilicon Hi6220 system controller

View File

@ -6,6 +6,10 @@ Required properties:
- #phys-cells: must be 0 (see phy-bindings.txt in this directory) - #phys-cells: must be 0 (see phy-bindings.txt in this directory)
Optional properties: Optional properties:
- clocks: a phandle to the clock of this PHY
- clock-names: must be "phy"
- resets: a phandle to the reset line of this PHY
- reset-names: must be "phy"
- phy-supply: see phy-bindings.txt in this directory - phy-supply: see phy-bindings.txt in this directory

View File

@ -0,0 +1,31 @@
* Amlogic Meson GXL and GXM USB3 PHY and OTG detection binding
Required properties:
- compatible: Should be "amlogic,meson-gxl-usb3-phy"
- #phys-cells: must be 0 (see phy-bindings.txt in this directory)
- reg: The base address and length of the registers
- interrupts: the interrupt specifier for the OTG detection
- clocks: phandles to the clocks for
- the USB3 PHY
- and peripheral mode/OTG detection
- clock-names: must contain "phy" and "peripheral"
- resets: phandle to the reset lines for:
- the USB3 PHY and
- peripheral mode/OTG detection
- reset-names: must contain "phy" and "peripheral"
Optional properties:
- phy-supply: see phy-bindings.txt in this directory
Example:
usb3_phy0: phy@78080 {
compatible = "amlogic,meson-gxl-usb3-phy";
#phy-cells = <0>;
reg = <0x0 0x78080 0x0 0x20>;
interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clkc CLKID_USB_OTG>, <&clkc_AO CLKID_AO_CEC_32K>;
clock-names = "phy", "peripheral";
resets = <&reset RESET_USB_OTG>, <&reset RESET_USB_OTG>;
reset-names = "phy", "peripheral";
};

View File

@ -0,0 +1,59 @@
HiSilicon STB PCIE/SATA/USB3 PHY
Required properties:
- compatible: Should be "hisilicon,hi3798cv200-combphy"
- reg: Should be the address space for COMBPHY configuration and state
registers in peripheral controller, e.g. PERI_COMBPHY0_CFG and
PERI_COMBPHY0_STATE for COMBPHY0 Hi3798CV200 SoC.
- #phy-cells: Should be 1. The cell number is used to select the phy mode
as defined in <dt-bindings/phy/phy.h>.
- clocks: The phandle to clock provider and clock specifier pair.
- resets: The phandle to reset controller and reset specifier pair.
Refer to phy/phy-bindings.txt for the generic PHY binding properties.
Optional properties:
- hisilicon,fixed-mode: If the phy device doesn't support mode select
but a fixed mode setting, the property should be present to specify
the particular mode.
- hisilicon,mode-select-bits: If the phy device support mode select,
this property should be present to specify the register bits in
peripheral controller, as a 3 integers tuple:
<register_offset bit_shift bit_mask>.
Notes:
- Between hisilicon,fixed-mode and hisilicon,mode-select-bits, one and only
one of them should be present.
- The device node should be a child of peripheral controller that contains
COMBPHY configuration/state and PERI_CTRL register used to select PHY mode.
Refer to arm/hisilicon/hisilicon.txt for the parent peripheral controller
bindings.
Examples:
perictrl: peripheral-controller@8a20000 {
compatible = "hisilicon,hi3798cv200-perictrl", "syscon",
"simple-mfd";
reg = <0x8a20000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x8a20000 0x1000>;
combphy0: phy@850 {
compatible = "hisilicon,hi3798cv200-combphy";
reg = <0x850 0x8>;
#phy-cells = <1>;
clocks = <&crg HISTB_COMBPHY0_CLK>;
resets = <&crg 0x188 4>;
hisilicon,fixed-mode = <PHY_TYPE_USB3>;
};
combphy1: phy@858 {
compatible = "hisilicon,hi3798cv200-combphy";
reg = <0x858 0x8>;
#phy-cells = <1>;
clocks = <&crg HISTB_COMBPHY1_CLK>;
resets = <&crg 0x188 12>;
hisilicon,mode-select-bits = <0x0008 11 (0x3 << 11)>;
};
};

View File

@ -0,0 +1,71 @@
Device tree bindings for HiSilicon INNO USB2 PHY
Required properties:
- compatible: Should be one of the following strings:
"hisilicon,inno-usb2-phy",
"hisilicon,hi3798cv200-usb2-phy".
- reg: Should be the address space for PHY configuration register in peripheral
controller, e.g. PERI_USB0 for USB 2.0 PHY01 on Hi3798CV200 SoC.
- clocks: The phandle and clock specifier pair for INNO USB2 PHY device
reference clock.
- resets: The phandle and reset specifier pair for INNO USB2 PHY device reset
signal.
- #address-cells: Must be 1.
- #size-cells: Must be 0.
The INNO USB2 PHY device should be a child node of peripheral controller that
contains the PHY configuration register, and each device suppports up to 2 PHY
ports which are represented as child nodes of INNO USB2 PHY device.
Required properties for PHY port node:
- reg: The PHY port instance number.
- #phy-cells: Defined by generic PHY bindings. Must be 0.
- resets: The phandle and reset specifier pair for PHY port reset signal.
Refer to phy/phy-bindings.txt for the generic PHY binding properties
Example:
perictrl: peripheral-controller@8a20000 {
compatible = "hisilicon,hi3798cv200-perictrl", "simple-mfd";
reg = <0x8a20000 0x1000>;
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x8a20000 0x1000>;
usb2_phy1: usb2-phy@120 {
compatible = "hisilicon,hi3798cv200-usb2-phy";
reg = <0x120 0x4>;
clocks = <&crg HISTB_USB2_PHY1_REF_CLK>;
resets = <&crg 0xbc 4>;
#address-cells = <1>;
#size-cells = <0>;
usb2_phy1_port0: phy@0 {
reg = <0>;
#phy-cells = <0>;
resets = <&crg 0xbc 8>;
};
usb2_phy1_port1: phy@1 {
reg = <1>;
#phy-cells = <0>;
resets = <&crg 0xbc 9>;
};
};
usb2_phy2: usb2-phy@124 {
compatible = "hisilicon,hi3798cv200-usb2-phy";
reg = <0x124 0x4>;
clocks = <&crg HISTB_USB2_PHY2_REF_CLK>;
resets = <&crg 0xbc 6>;
#address-cells = <1>;
#size-cells = <0>;
usb2_phy2_port0: phy@0 {
reg = <0>;
#phy-cells = <0>;
resets = <&crg 0xbc 10>;
};
};
};

View File

@ -0,0 +1,29 @@
Device tree binding documentation for Motorola Mapphone MDM6600 USB PHY
Required properties:
- compatible Must be "motorola,mapphone-mdm6600"
- enable-gpios GPIO to enable the USB PHY
- power-gpios GPIO to power on the device
- reset-gpios GPIO to reset the device
- motorola,mode-gpios Two GPIOs to configure MDM6600 USB start-up mode for
normal mode versus USB flashing mode
- motorola,cmd-gpios Three GPIOs to control the power state of the MDM6600
- motorola,status-gpios Three GPIOs to read the power state of the MDM6600
Example:
usb-phy {
compatible = "motorola,mapphone-mdm6600";
enable-gpios = <&gpio3 31 GPIO_ACTIVE_LOW>;
power-gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>;
motorola,mode-gpios = <&gpio5 20 GPIO_ACTIVE_HIGH>,
<&gpio5 21 GPIO_ACTIVE_HIGH>;
motorola,cmd-gpios = <&gpio4 7 GPIO_ACTIVE_HIGH>,
<&gpio4 8 GPIO_ACTIVE_HIGH>,
<&gpio5 14 GPIO_ACTIVE_HIGH>;
motorola,status-gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>,
<&gpio2 21 GPIO_ACTIVE_HIGH>,
<&gpio2 23 GPIO_ACTIVE_HIGH>;
#phy-cells = <0>;
};

View File

@ -27,6 +27,10 @@ Optional properties (controller (parent) node):
- reg : offset and length of register shared by multiple ports, - reg : offset and length of register shared by multiple ports,
exclude port's private register. It is needed on mt2701 exclude port's private register. It is needed on mt2701
and mt8173, but not on mt2712. and mt8173, but not on mt2712.
- mediatek,src-ref-clk-mhz : frequency of reference clock for slew rate
calibrate
- mediatek,src-coef : coefficient for slew rate calibrate, depends on
SoC process
Required properties (port (child) node): Required properties (port (child) node):
- reg : address and length of the register set for the port. - reg : address and length of the register set for the port.

View File

@ -14,25 +14,9 @@ Required properties:
- resets : a list of phandle + reset specifier pairs - resets : a list of phandle + reset specifier pairs
- reset-names : string reset name, must be: - reset-names : string reset name, must be:
"uphy", "uphy-pipe", "uphy-tcphy" "uphy", "uphy-pipe", "uphy-tcphy"
- extcon : extcon specifier for the Power Delivery
Note, there are 2 type-c phys for RK3399, and they are almost identical, except Optional properties:
these registers(description below), every register node contains 3 sections: - extcon : extcon specifier for the Power Delivery
offset, enable bit, write mask bit.
- rockchip,typec-conn-dir : the register of type-c connector direction,
for type-c phy0, it must be <0xe580 0 16>;
for type-c phy1, it must be <0xe58c 0 16>;
- rockchip,usb3tousb2-en : the register of type-c force usb3 to usb2 enable
control.
for type-c phy0, it must be <0xe580 3 19>;
for type-c phy1, it must be <0xe58c 3 19>;
- rockchip,external-psm : the register of type-c phy external psm clock
selection.
for type-c phy0, it must be <0xe588 14 30>;
for type-c phy1, it must be <0xe594 14 30>;
- rockchip,pipe-status : the register of type-c phy pipe status.
for type-c phy0, it must be <0xe5c0 0 0>;
for type-c phy1, it must be <0xe5c0 16 16>;
Required nodes : a sub-node is required for each port the phy provides. Required nodes : a sub-node is required for each port the phy provides.
The sub-node name is used to identify dp or usb3 port, The sub-node name is used to identify dp or usb3 port,
@ -43,6 +27,13 @@ Required nodes : a sub-node is required for each port the phy provides.
Required properties (port (child) node): Required properties (port (child) node):
- #phy-cells : must be 0, See ./phy-bindings.txt for details. - #phy-cells : must be 0, See ./phy-bindings.txt for details.
Deprecated properties, do not use in new device tree sources, these
properties are determined by the compatible value:
- rockchip,typec-conn-dir
- rockchip,usb3tousb2-en
- rockchip,external-psm
- rockchip,pipe-status
Example: Example:
tcphy0: phy@ff7c0000 { tcphy0: phy@ff7c0000 {
compatible = "rockchip,rk3399-typec-phy"; compatible = "rockchip,rk3399-typec-phy";
@ -58,10 +49,6 @@ Example:
<&cru SRST_UPHY0_PIPE_L00>, <&cru SRST_UPHY0_PIPE_L00>,
<&cru SRST_P_UPHY0_TCPHY>; <&cru SRST_P_UPHY0_TCPHY>;
reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
rockchip,typec-conn-dir = <0xe580 0 16>;
rockchip,usb3tousb2-en = <0xe580 3 19>;
rockchip,external-psm = <0xe588 14 30>;
rockchip,pipe-status = <0xe5c0 0 0>;
tcphy0_dp: dp-port { tcphy0_dp: dp-port {
#phy-cells = <0>; #phy-cells = <0>;
@ -86,10 +73,6 @@ Example:
<&cru SRST_UPHY1_PIPE_L00>, <&cru SRST_UPHY1_PIPE_L00>,
<&cru SRST_P_UPHY1_TCPHY>; <&cru SRST_P_UPHY1_TCPHY>;
reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
rockchip,typec-conn-dir = <0xe58c 0 16>;
rockchip,usb3tousb2-en = <0xe58c 3 19>;
rockchip,external-psm = <0xe594 14 30>;
rockchip,pipe-status = <0xe5c0 16 16>;
tcphy1_dp: dp-port { tcphy1_dp: dp-port {
#phy-cells = <0>; #phy-cells = <0>;

View File

@ -0,0 +1,73 @@
STMicroelectronics STM32 USB HS PHY controller
The STM32 USBPHYC block contains a dual port High Speed UTMI+ PHY and a UTMI
switch. It controls PHY configuration and status, and the UTMI+ switch that
selects either OTG or HOST controller for the second PHY port. It also sets
PLL configuration.
USBPHYC
|_ PLL
|
|_ PHY port#1 _________________ HOST controller
| _ |
| / 1|________________|
|_ PHY port#2 ----| |________________
| \_0| |
|_ UTMI switch_______| OTG controller
Phy provider node
=================
Required properties:
- compatible: must be "st,stm32mp1-usbphyc"
- reg: address and length of the usb phy control register set
- clocks: phandle + clock specifier for the PLL phy clock
- #address-cells: number of address cells for phys sub-nodes, must be <1>
- #size-cells: number of size cells for phys sub-nodes, must be <0>
Optional properties:
- assigned-clocks: phandle + clock specifier for the PLL phy clock
- assigned-clock-parents: the PLL phy clock parent
- resets: phandle + reset specifier
Required nodes: one sub-node per port the controller provides.
Phy sub-nodes
==============
Required properties:
- reg: phy port index
- phy-supply: phandle to the regulator providing 3V3 power to the PHY,
see phy-bindings.txt in the same directory.
- vdda1v1-supply: phandle to the regulator providing 1V1 power to the PHY
- vdda1v8-supply: phandle to the regulator providing 1V8 power to the PHY
- #phy-cells: see phy-bindings.txt in the same directory, must be <0> for PHY
port#1 and must be <1> for PHY port#2, to select USB controller
Example:
usbphyc: usb-phy@5a006000 {
compatible = "st,stm32mp1-usbphyc";
reg = <0x5a006000 0x1000>;
clocks = <&rcc_clk USBPHY_K>;
resets = <&rcc_rst USBPHY_R>;
#address-cells = <1>;
#size-cells = <0>;
usbphyc_port0: usb-phy@0 {
reg = <0>;
phy-supply = <&vdd_usb>;
vdda1v1-supply = <&reg11>;
vdda1v8-supply = <&reg18>
#phy-cells = <0>;
};
usbphyc_port1: usb-phy@1 {
reg = <1>;
phy-supply = <&vdd_usb>;
vdda1v1-supply = <&reg11>;
vdda1v8-supply = <&reg18>
#phy-cells = <1>;
};
};

View File

@ -8,7 +8,8 @@ Required properties:
- compatible: compatible list, contains: - compatible: compatible list, contains:
"qcom,ipq8074-qmp-pcie-phy" for PCIe phy on IPQ8074 "qcom,ipq8074-qmp-pcie-phy" for PCIe phy on IPQ8074
"qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996, "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
"qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996. "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996,
"qcom,qmp-v3-usb3-phy" for USB3 QMP V3 phy.
- reg: offset and length of register set for PHY's common serdes block. - reg: offset and length of register set for PHY's common serdes block.
@ -25,10 +26,13 @@ Required properties:
- clock-names: "cfg_ahb" for phy config clock, - clock-names: "cfg_ahb" for phy config clock,
"aux" for phy aux clock, "aux" for phy aux clock,
"ref" for 19.2 MHz ref clk, "ref" for 19.2 MHz ref clk,
"com_aux" for phy common block aux clock,
For "qcom,msm8996-qmp-pcie-phy" must contain: For "qcom,msm8996-qmp-pcie-phy" must contain:
"aux", "cfg_ahb", "ref". "aux", "cfg_ahb", "ref".
For "qcom,msm8996-qmp-usb3-phy" must contain: For "qcom,msm8996-qmp-usb3-phy" must contain:
"aux", "cfg_ahb", "ref". "aux", "cfg_ahb", "ref".
For "qcom,qmp-v3-usb3-phy" must contain:
"aux", "cfg_ahb", "ref", "com_aux".
- resets: a list of phandles and reset controller specifier pairs, - resets: a list of phandles and reset controller specifier pairs,
one for each entry in reset-names. one for each entry in reset-names.

View File

@ -4,7 +4,10 @@ Qualcomm QUSB2 phy controller
QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets. QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
Required properties: Required properties:
- compatible: compatible list, contains "qcom,msm8996-qusb2-phy". - compatible: compatible list, contains
"qcom,msm8996-qusb2-phy" for 14nm PHY on msm8996,
"qcom,qusb2-v2-phy" for QUSB2 V2 PHY.
- reg: offset and length of the PHY register set. - reg: offset and length of the PHY register set.
- #phy-cells: must be 0. - #phy-cells: must be 0.

View File

@ -8,6 +8,8 @@ Required properties:
SoC. SoC.
"renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796 "renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796
SoC. SoC.
"renesas,usb2-phy-r8a77965" if the device is a part of an
R8A77965 SoC.
"renesas,usb2-phy-r8a77995" if the device is a part of an "renesas,usb2-phy-r8a77995" if the device is a part of an
R8A77995 SoC. R8A77995 SoC.
"renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device. "renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device.

View File

@ -11,6 +11,8 @@ Required properties:
SoC. SoC.
"renesas,r8a7796-usb3-phy" if the device is a part of an R8A7796 "renesas,r8a7796-usb3-phy" if the device is a part of an R8A7796
SoC. SoC.
"renesas,r8a77965-usb3-phy" if the device is a part of an
R8A77965 SoC.
"renesas,rcar-gen3-usb3-phy" for a generic R-Car Gen3 compatible "renesas,rcar-gen3-usb3-phy" for a generic R-Car Gen3 compatible
device. device.

View File

@ -11,6 +11,7 @@ Required properties:
* allwinner,sun8i-a33-usb-phy * allwinner,sun8i-a33-usb-phy
* allwinner,sun8i-a83t-usb-phy * allwinner,sun8i-a83t-usb-phy
* allwinner,sun8i-h3-usb-phy * allwinner,sun8i-h3-usb-phy
* allwinner,sun8i-r40-usb-phy
* allwinner,sun8i-v3s-usb-phy * allwinner,sun8i-v3s-usb-phy
* allwinner,sun50i-a64-usb-phy * allwinner,sun50i-a64-usb-phy
- reg : a list of offset + length pairs - reg : a list of offset + length pairs

View File

@ -112,6 +112,7 @@ enum sun4i_usb_phy_type {
sun8i_a33_phy, sun8i_a33_phy,
sun8i_a83t_phy, sun8i_a83t_phy,
sun8i_h3_phy, sun8i_h3_phy,
sun8i_r40_phy,
sun8i_v3s_phy, sun8i_v3s_phy,
sun50i_a64_phy, sun50i_a64_phy,
}; };
@ -410,11 +411,13 @@ static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data)
return true; return true;
/* /*
* The A31 companion pmic (axp221) does not generate vbus change * The A31/A23/A33 companion pmics (AXP221/AXP223) do not
* interrupts when the board is driving vbus, so we must poll * generate vbus change interrupts when the board is driving
* vbus using the N_VBUSEN pin on the pmic, so we must poll
* when using the pmic for vbus-det _and_ we're driving vbus. * when using the pmic for vbus-det _and_ we're driving vbus.
*/ */
if (data->cfg->type == sun6i_a31_phy && if ((data->cfg->type == sun6i_a31_phy ||
data->cfg->type == sun8i_a33_phy) &&
data->vbus_power_supply && data->phys[0].regulator_on) data->vbus_power_supply && data->phys[0].regulator_on)
return true; return true;
@ -885,7 +888,7 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
.num_phys = 2, .num_phys = 2,
.type = sun4i_a10_phy, .type = sun6i_a31_phy,
.disc_thresh = 3, .disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A10, .phyctl_offset = REG_PHYCTL_A10,
.dedicated_clocks = true, .dedicated_clocks = true,
@ -919,6 +922,16 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
.phy0_dual_route = true, .phy0_dual_route = true,
}; };
static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = {
.num_phys = 3,
.type = sun8i_r40_phy,
.disc_thresh = 3,
.phyctl_offset = REG_PHYCTL_A33,
.dedicated_clocks = true,
.enable_pmu_unk1 = true,
.phy0_dual_route = true,
};
static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = {
.num_phys = 1, .num_phys = 1,
.type = sun8i_v3s_phy, .type = sun8i_v3s_phy,
@ -948,6 +961,7 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
{ .compatible = "allwinner,sun8i-a83t-usb-phy", .data = &sun8i_a83t_cfg }, { .compatible = "allwinner,sun8i-a83t-usb-phy", .data = &sun8i_a83t_cfg },
{ .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
{ .compatible = "allwinner,sun8i-r40-usb-phy", .data = &sun8i_r40_cfg },
{ .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg },
{ .compatible = "allwinner,sun50i-a64-usb-phy", { .compatible = "allwinner,sun50i-a64-usb-phy",
.data = &sun50i_a64_cfg}, .data = &sun50i_a64_cfg},

View File

@ -18,10 +18,21 @@ config PHY_MESON_GXL_USB2
default ARCH_MESON default ARCH_MESON
depends on OF && (ARCH_MESON || COMPILE_TEST) depends on OF && (ARCH_MESON || COMPILE_TEST)
depends on USB_SUPPORT depends on USB_SUPPORT
select USB_COMMON
select GENERIC_PHY select GENERIC_PHY
select REGMAP_MMIO select REGMAP_MMIO
help help
Enable this to support the Meson USB2 PHYs found in Meson Enable this to support the Meson USB2 PHYs found in Meson
GXL and GXM SoCs. GXL and GXM SoCs.
If unsure, say N. If unsure, say N.
config PHY_MESON_GXL_USB3
tristate "Meson GXL and GXM USB3 PHY drivers"
default ARCH_MESON
depends on OF && (ARCH_MESON || COMPILE_TEST)
depends on USB_SUPPORT
select GENERIC_PHY
select REGMAP_MMIO
help
Enable this to support the Meson USB3 PHY and OTG detection
IP block found in Meson GXL and GXM SoCs.
If unsure, say N.

View File

@ -1,2 +1,3 @@
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_GXL_USB3) += phy-meson-gxl-usb3.o

View File

@ -11,14 +11,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/usb/of.h>
/* bits [31:27] are read-only */ /* bits [31:27] are read-only */
#define U2P_R0 0x0 #define U2P_R0 0x0
@ -70,12 +71,11 @@
/* bits [31:14] are read-only */ /* bits [31:14] are read-only */
#define U2P_R2 0x8 #define U2P_R2 0x8
#define U2P_R2_DATA_IN_MASK GENMASK(3, 0) #define U2P_R2_TESTDATA_IN_MASK GENMASK(7, 0)
#define U2P_R2_DATA_IN_EN_MASK GENMASK(7, 4) #define U2P_R2_TESTADDR_MASK GENMASK(11, 8)
#define U2P_R2_ADDR_MASK GENMASK(11, 8) #define U2P_R2_TESTDATA_OUT_SEL BIT(12)
#define U2P_R2_DATA_OUT_SEL BIT(12) #define U2P_R2_TESTCLK BIT(13)
#define U2P_R2_CLK BIT(13) #define U2P_R2_TESTDATA_OUT_MASK GENMASK(17, 14)
#define U2P_R2_DATA_OUT_MASK GENMASK(17, 14)
#define U2P_R2_ACA_PIN_RANGE_C BIT(18) #define U2P_R2_ACA_PIN_RANGE_C BIT(18)
#define U2P_R2_ACA_PIN_RANGE_B BIT(19) #define U2P_R2_ACA_PIN_RANGE_B BIT(19)
#define U2P_R2_ACA_PIN_RANGE_A BIT(20) #define U2P_R2_ACA_PIN_RANGE_A BIT(20)
@ -99,6 +99,8 @@ struct phy_meson_gxl_usb2_priv {
struct regmap *regmap; struct regmap *regmap;
enum phy_mode mode; enum phy_mode mode;
int is_enabled; int is_enabled;
struct clk *clk;
struct reset_control *reset;
}; };
static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = { static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
@ -108,6 +110,31 @@ static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
.max_register = U2P_R3, .max_register = U2P_R3,
}; };
static int phy_meson_gxl_usb2_init(struct phy *phy)
{
struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
int ret;
ret = reset_control_reset(priv->reset);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
return 0;
}
static int phy_meson_gxl_usb2_exit(struct phy *phy)
{
struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
clk_disable_unprepare(priv->clk);
return 0;
}
static int phy_meson_gxl_usb2_reset(struct phy *phy) static int phy_meson_gxl_usb2_reset(struct phy *phy)
{ {
struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy); struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
@ -195,6 +222,8 @@ static int phy_meson_gxl_usb2_power_on(struct phy *phy)
} }
static const struct phy_ops phy_meson_gxl_usb2_ops = { static const struct phy_ops phy_meson_gxl_usb2_ops = {
.init = phy_meson_gxl_usb2_init,
.exit = phy_meson_gxl_usb2_exit,
.power_on = phy_meson_gxl_usb2_power_on, .power_on = phy_meson_gxl_usb2_power_on,
.power_off = phy_meson_gxl_usb2_power_off, .power_off = phy_meson_gxl_usb2_power_off,
.set_mode = phy_meson_gxl_usb2_set_mode, .set_mode = phy_meson_gxl_usb2_set_mode,
@ -210,6 +239,7 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
struct phy_meson_gxl_usb2_priv *priv; struct phy_meson_gxl_usb2_priv *priv;
struct phy *phy; struct phy *phy;
void __iomem *base; void __iomem *base;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
@ -222,28 +252,34 @@ static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
if (IS_ERR(base)) if (IS_ERR(base))
return PTR_ERR(base); return PTR_ERR(base);
switch (of_usb_get_dr_mode_by_phy(dev->of_node, -1)) { /* start in host mode */
case USB_DR_MODE_PERIPHERAL: priv->mode = PHY_MODE_USB_HOST;
priv->mode = PHY_MODE_USB_DEVICE;
break;
case USB_DR_MODE_OTG:
priv->mode = PHY_MODE_USB_OTG;
break;
case USB_DR_MODE_HOST:
default:
priv->mode = PHY_MODE_USB_HOST;
break;
}
priv->regmap = devm_regmap_init_mmio(dev, base, priv->regmap = devm_regmap_init_mmio(dev, base,
&phy_meson_gxl_usb2_regmap_conf); &phy_meson_gxl_usb2_regmap_conf);
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");
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
if (ret == -ENOENT)
priv->clk = NULL;
else
return ret;
}
priv->reset = devm_reset_control_get_optional_shared(dev, "phy");
if (IS_ERR(priv->reset))
return PTR_ERR(priv->reset);
phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops); phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
if (IS_ERR(phy)) { if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY\n"); ret = PTR_ERR(phy);
return PTR_ERR(phy); if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to create PHY\n");
return ret;
} }
phy_set_drvdata(phy, priv); phy_set_drvdata(phy, priv);

View File

@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Meson GXL USB3 PHY and OTG mode detection driver
*
* Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.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>
#define USB_R0 0x00
#define USB_R0_P30_FSEL_MASK GENMASK(5, 0)
#define USB_R0_P30_PHY_RESET BIT(6)
#define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7)
#define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8)
#define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9)
#define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14)
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
#define USB_R0_U2D_ACT BIT(31)
#define USB_R1 0x04
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
#define USB_R1_U3H_PME_ENABLE BIT(1)
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2)
#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7)
#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12)
#define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16)
#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17)
#define USB_R1_U3H_HOST_MSI_ENABLE BIT(18)
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
#define USB_R2 0x08
#define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0)
#define USB_R2_P30_CR_READ BIT(16)
#define USB_R2_P30_CR_WRITE BIT(17)
#define USB_R2_P30_CR_CAP_ADDR BIT(18)
#define USB_R2_P30_CR_CAP_DATA BIT(19)
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
#define USB_R3 0x0c
#define USB_R3_P30_SSC_ENABLE BIT(0)
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
#define USB_R3_P30_REF_SSP_EN BIT(13)
#define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16)
#define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19)
#define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24)
#define USB_R4 0x10
#define USB_R4_P21_PORT_RESET_0 BIT(0)
#define USB_R4_P21_SLEEP_M0 BIT(1)
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
#define USB_R4_P21_ONLY BIT(4)
#define USB_R5 0x14
#define USB_R5_ID_DIG_SYNC BIT(0)
#define USB_R5_ID_DIG_REG BIT(1)
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
#define USB_R5_ID_DIG_EN_0 BIT(4)
#define USB_R5_ID_DIG_EN_1 BIT(5)
#define USB_R5_ID_DIG_CURR BIT(6)
#define USB_R5_ID_DIG_IRQ BIT(7)
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
/* read-only register */
#define USB_R6 0x18
#define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0)
#define USB_R6_P30_CR_ACK BIT(16)
struct phy_meson_gxl_usb3_priv {
struct regmap *regmap;
enum phy_mode mode;
struct clk *clk_phy;
struct clk *clk_peripheral;
struct reset_control *reset;
};
static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
.max_register = USB_R6,
};
static int phy_meson_gxl_usb3_power_on(struct phy *phy)
{
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
USB_R5_ID_DIG_EN_0);
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
USB_R5_ID_DIG_EN_1);
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
return 0;
}
static int phy_meson_gxl_usb3_power_off(struct phy *phy)
{
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0);
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0);
return 0;
}
static int phy_meson_gxl_usb3_set_mode(struct phy *phy, enum phy_mode mode)
{
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
switch (mode) {
case PHY_MODE_USB_HOST:
regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
0);
break;
case PHY_MODE_USB_DEVICE:
regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
USB_R0_U2D_ACT);
regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
USB_R4_P21_SLEEP_M0);
break;
default:
dev_err(&phy->dev, "unsupported PHY mode %d\n", mode);
return -EINVAL;
}
priv->mode = mode;
return 0;
}
static int phy_meson_gxl_usb3_init(struct phy *phy)
{
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
int ret;
ret = reset_control_reset(priv->reset);
if (ret)
goto err;
ret = clk_prepare_enable(priv->clk_phy);
if (ret)
goto err;
ret = clk_prepare_enable(priv->clk_peripheral);
if (ret)
goto err_disable_clk_phy;
ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode);
if (ret)
goto err_disable_clk_peripheral;
regmap_update_bits(priv->regmap, USB_R1,
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
return 0;
err_disable_clk_peripheral:
clk_disable_unprepare(priv->clk_peripheral);
err_disable_clk_phy:
clk_disable_unprepare(priv->clk_phy);
err:
return ret;
}
static int phy_meson_gxl_usb3_exit(struct phy *phy)
{
struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
clk_disable_unprepare(priv->clk_peripheral);
clk_disable_unprepare(priv->clk_phy);
return 0;
}
static const struct phy_ops phy_meson_gxl_usb3_ops = {
.power_on = phy_meson_gxl_usb3_power_on,
.power_off = phy_meson_gxl_usb3_power_off,
.set_mode = phy_meson_gxl_usb3_set_mode,
.init = phy_meson_gxl_usb3_init,
.exit = phy_meson_gxl_usb3_exit,
.owner = THIS_MODULE,
};
static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_meson_gxl_usb3_priv *priv;
struct resource *res;
struct phy *phy;
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_meson_gxl_usb3_regmap_conf);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
priv->clk_phy = devm_clk_get(dev, "phy");
if (IS_ERR(priv->clk_phy))
return PTR_ERR(priv->clk_phy);
priv->clk_peripheral = devm_clk_get(dev, "peripheral");
if (IS_ERR(priv->clk_peripheral))
return PTR_ERR(priv->clk_peripheral);
priv->reset = devm_reset_control_array_get_shared(dev);
if (IS_ERR(priv->reset))
return PTR_ERR(priv->reset);
/*
* default to host mode as hardware defaults and/or boot-loader
* behavior can result in this PHY starting up in device mode. this
* default and the initialization in phy_meson_gxl_usb3_init ensure
* that we reproducibly start in a known mode on all devices.
*/
priv->mode = PHY_MODE_USB_HOST;
phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_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_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_gxl_usb3_of_match[] = {
{ .compatible = "amlogic,meson-gxl-usb3-phy", },
{ },
};
MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
static struct platform_driver phy_meson_gxl_usb3_driver = {
.probe = phy_meson_gxl_usb3_probe,
.driver = {
.name = "phy-meson-gxl-usb3",
.of_match_table = phy_meson_gxl_usb3_of_match,
},
};
module_platform_driver(phy_meson_gxl_usb3_driver);
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver");
MODULE_LICENSE("GPL v2");

View File

@ -4,6 +4,7 @@
config PHY_HI6220_USB config PHY_HI6220_USB
tristate "hi6220 USB PHY support" tristate "hi6220 USB PHY support"
depends on (ARCH_HISI && ARM64) || COMPILE_TEST depends on (ARCH_HISI && ARM64) || COMPILE_TEST
depends on HAS_IOMEM
select GENERIC_PHY select GENERIC_PHY
select MFD_SYSCON select MFD_SYSCON
help help
@ -11,6 +12,25 @@ 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_HISTB_COMBPHY
tristate "HiSilicon STB SoCs COMBPHY support"
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the HISILICON STB SoCs COMBPHY.
If unsure, say N.
config PHY_HISI_INNO_USB2
tristate "HiSilicon INNO USB2 PHY support"
depends on (ARCH_HISI && ARM64) || COMPILE_TEST
select GENERIC_PHY
select MFD_SYSCON
help
Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports
USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one
USB host port to accept one USB device.
config PHY_HIX5HD2_SATA config PHY_HIX5HD2_SATA
tristate "HIX5HD2 SATA PHY Driver" tristate "HIX5HD2 SATA PHY Driver"
depends on ARCH_HIX5HD2 && OF && HAS_IOMEM depends on ARCH_HIX5HD2 && OF && HAS_IOMEM

View File

@ -1,2 +1,4 @@
obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o
obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o
obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o
obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o

View File

@ -0,0 +1,197 @@
/*
* HiSilicon INNO USB2 PHY Driver.
*
* Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/reset.h>
#define INNO_PHY_PORT_NUM 2
#define REF_CLK_STABLE_TIME 100 /* unit:us */
#define UTMI_CLK_STABLE_TIME 200 /* unit:us */
#define TEST_CLK_STABLE_TIME 2 /* unit:ms */
#define PHY_CLK_STABLE_TIME 2 /* unit:ms */
#define UTMI_RST_COMPLETE_TIME 2 /* unit:ms */
#define POR_RST_COMPLETE_TIME 300 /* unit:us */
#define PHY_TEST_DATA GENMASK(7, 0)
#define PHY_TEST_ADDR GENMASK(15, 8)
#define PHY_TEST_PORT GENMASK(18, 16)
#define PHY_TEST_WREN BIT(21)
#define PHY_TEST_CLK BIT(22) /* rising edge active */
#define PHY_TEST_RST BIT(23) /* low active */
#define PHY_CLK_ENABLE BIT(2)
struct hisi_inno_phy_port {
struct reset_control *utmi_rst;
struct hisi_inno_phy_priv *priv;
};
struct hisi_inno_phy_priv {
void __iomem *mmio;
struct clk *ref_clk;
struct reset_control *por_rst;
struct hisi_inno_phy_port ports[INNO_PHY_PORT_NUM];
};
static void hisi_inno_phy_write_reg(struct hisi_inno_phy_priv *priv,
u8 port, u32 addr, u32 data)
{
void __iomem *reg = priv->mmio;
u32 val;
val = (data & PHY_TEST_DATA) |
((addr << 8) & PHY_TEST_ADDR) |
((port << 16) & PHY_TEST_PORT) |
PHY_TEST_WREN | PHY_TEST_RST;
writel(val, reg);
val |= PHY_TEST_CLK;
writel(val, reg);
val &= ~PHY_TEST_CLK;
writel(val, reg);
}
static void hisi_inno_phy_setup(struct hisi_inno_phy_priv *priv)
{
/* The phy clk is controlled by the port0 register 0x06. */
hisi_inno_phy_write_reg(priv, 0, 0x06, PHY_CLK_ENABLE);
msleep(PHY_CLK_STABLE_TIME);
}
static int hisi_inno_phy_init(struct phy *phy)
{
struct hisi_inno_phy_port *port = phy_get_drvdata(phy);
struct hisi_inno_phy_priv *priv = port->priv;
int ret;
ret = clk_prepare_enable(priv->ref_clk);
if (ret)
return ret;
udelay(REF_CLK_STABLE_TIME);
reset_control_deassert(priv->por_rst);
udelay(POR_RST_COMPLETE_TIME);
/* Set up phy registers */
hisi_inno_phy_setup(priv);
reset_control_deassert(port->utmi_rst);
udelay(UTMI_RST_COMPLETE_TIME);
return 0;
}
static int hisi_inno_phy_exit(struct phy *phy)
{
struct hisi_inno_phy_port *port = phy_get_drvdata(phy);
struct hisi_inno_phy_priv *priv = port->priv;
reset_control_assert(port->utmi_rst);
reset_control_assert(priv->por_rst);
clk_disable_unprepare(priv->ref_clk);
return 0;
}
static const struct phy_ops hisi_inno_phy_ops = {
.init = hisi_inno_phy_init,
.exit = hisi_inno_phy_exit,
.owner = THIS_MODULE,
};
static int hisi_inno_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct hisi_inno_phy_priv *priv;
struct phy_provider *provider;
struct device_node *child;
struct resource *res;
int i = 0;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->mmio)) {
ret = PTR_ERR(priv->mmio);
return ret;
}
priv->ref_clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->ref_clk))
return PTR_ERR(priv->ref_clk);
priv->por_rst = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(priv->por_rst))
return PTR_ERR(priv->por_rst);
for_each_child_of_node(np, child) {
struct reset_control *rst;
struct phy *phy;
rst = of_reset_control_get_exclusive(child, NULL);
if (IS_ERR(rst))
return PTR_ERR(rst);
priv->ports[i].utmi_rst = rst;
priv->ports[i].priv = priv;
phy = devm_phy_create(dev, child, &hisi_inno_phy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_bus_width(phy, 8);
phy_set_drvdata(phy, &priv->ports[i]);
i++;
if (i > INNO_PHY_PORT_NUM) {
dev_warn(dev, "Support %d ports in maximum\n", i);
break;
}
}
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(provider);
}
static const struct of_device_id hisi_inno_phy_of_match[] = {
{ .compatible = "hisilicon,inno-usb2-phy", },
{ .compatible = "hisilicon,hi3798cv200-usb2-phy", },
{ },
};
MODULE_DEVICE_TABLE(of, hisi_inno_phy_of_match);
static struct platform_driver hisi_inno_phy_driver = {
.probe = hisi_inno_phy_probe,
.driver = {
.name = "hisi-inno-phy",
.of_match_table = hisi_inno_phy_of_match,
}
};
module_platform_driver(hisi_inno_phy_driver);
MODULE_DESCRIPTION("HiSilicon INNO USB2 PHY Driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,289 @@
/*
* COMBPHY driver for HiSilicon STB SoCs
*
* Copyright (C) 2016-2017 HiSilicon Co., Ltd. http://www.hisilicon.com
*
* Authors: Jianguo Sun <sunjianguo1@huawei.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <dt-bindings/phy/phy.h>
#define COMBPHY_MODE_PCIE 0
#define COMBPHY_MODE_USB3 1
#define COMBPHY_MODE_SATA 2
#define COMBPHY_CFG_REG 0x0
#define COMBPHY_BYPASS_CODEC BIT(31)
#define COMBPHY_TEST_WRITE BIT(24)
#define COMBPHY_TEST_DATA_SHIFT 20
#define COMBPHY_TEST_DATA_MASK GENMASK(23, 20)
#define COMBPHY_TEST_ADDR_SHIFT 12
#define COMBPHY_TEST_ADDR_MASK GENMASK(16, 12)
#define COMBPHY_CLKREF_OUT_OEN BIT(0)
struct histb_combphy_mode {
int fixed;
int select;
u32 reg;
u32 shift;
u32 mask;
};
struct histb_combphy_priv {
void __iomem *mmio;
struct regmap *syscon;
struct reset_control *por_rst;
struct clk *ref_clk;
struct phy *phy;
struct histb_combphy_mode mode;
};
static void nano_register_write(struct histb_combphy_priv *priv,
u32 addr, u32 data)
{
void __iomem *reg = priv->mmio + COMBPHY_CFG_REG;
u32 val;
/* Set up address and data for the write */
val = readl(reg);
val &= ~COMBPHY_TEST_ADDR_MASK;
val |= addr << COMBPHY_TEST_ADDR_SHIFT;
val &= ~COMBPHY_TEST_DATA_MASK;
val |= data << COMBPHY_TEST_DATA_SHIFT;
writel(val, reg);
/* Flip strobe control to trigger the write */
val &= ~COMBPHY_TEST_WRITE;
writel(val, reg);
val |= COMBPHY_TEST_WRITE;
writel(val, reg);
}
static int is_mode_fixed(struct histb_combphy_mode *mode)
{
return (mode->fixed != PHY_NONE) ? true : false;
}
static int histb_combphy_set_mode(struct histb_combphy_priv *priv)
{
struct histb_combphy_mode *mode = &priv->mode;
struct regmap *syscon = priv->syscon;
u32 hw_sel;
if (is_mode_fixed(mode))
return 0;
switch (mode->select) {
case PHY_TYPE_SATA:
hw_sel = COMBPHY_MODE_SATA;
break;
case PHY_TYPE_PCIE:
hw_sel = COMBPHY_MODE_PCIE;
break;
case PHY_TYPE_USB3:
hw_sel = COMBPHY_MODE_USB3;
break;
default:
return -EINVAL;
}
return regmap_update_bits(syscon, mode->reg, mode->mask,
hw_sel << mode->shift);
}
static int histb_combphy_init(struct phy *phy)
{
struct histb_combphy_priv *priv = phy_get_drvdata(phy);
u32 val;
int ret;
ret = histb_combphy_set_mode(priv);
if (ret)
return ret;
/* Clear bypass bit to enable encoding/decoding */
val = readl(priv->mmio + COMBPHY_CFG_REG);
val &= ~COMBPHY_BYPASS_CODEC;
writel(val, priv->mmio + COMBPHY_CFG_REG);
ret = clk_prepare_enable(priv->ref_clk);
if (ret)
return ret;
reset_control_deassert(priv->por_rst);
/* Enable EP clock */
val = readl(priv->mmio + COMBPHY_CFG_REG);
val |= COMBPHY_CLKREF_OUT_OEN;
writel(val, priv->mmio + COMBPHY_CFG_REG);
/* Need to wait for EP clock stable */
mdelay(5);
/* Configure nano phy registers as suggested by vendor */
nano_register_write(priv, 0x1, 0x8);
nano_register_write(priv, 0xc, 0x9);
nano_register_write(priv, 0x1a, 0x4);
return 0;
}
static int histb_combphy_exit(struct phy *phy)
{
struct histb_combphy_priv *priv = phy_get_drvdata(phy);
u32 val;
/* Disable EP clock */
val = readl(priv->mmio + COMBPHY_CFG_REG);
val &= ~COMBPHY_CLKREF_OUT_OEN;
writel(val, priv->mmio + COMBPHY_CFG_REG);
reset_control_assert(priv->por_rst);
clk_disable_unprepare(priv->ref_clk);
return 0;
}
static const struct phy_ops histb_combphy_ops = {
.init = histb_combphy_init,
.exit = histb_combphy_exit,
.owner = THIS_MODULE,
};
static struct phy *histb_combphy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct histb_combphy_priv *priv = dev_get_drvdata(dev);
struct histb_combphy_mode *mode = &priv->mode;
if (args->args_count < 1) {
dev_err(dev, "invalid number of arguments\n");
return ERR_PTR(-EINVAL);
}
mode->select = args->args[0];
if (mode->select < PHY_TYPE_SATA || mode->select > PHY_TYPE_USB3) {
dev_err(dev, "invalid phy mode select argument\n");
return ERR_PTR(-EINVAL);
}
if (is_mode_fixed(mode) && mode->select != mode->fixed) {
dev_err(dev, "mode select %d mismatch fixed phy mode %d\n",
mode->select, mode->fixed);
return ERR_PTR(-EINVAL);
}
return priv->phy;
}
static int histb_combphy_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct histb_combphy_priv *priv;
struct device_node *np = dev->of_node;
struct histb_combphy_mode *mode;
struct resource *res;
u32 vals[3];
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->mmio)) {
ret = PTR_ERR(priv->mmio);
return ret;
}
priv->syscon = syscon_node_to_regmap(np->parent);
if (IS_ERR(priv->syscon)) {
dev_err(dev, "failed to find peri_ctrl syscon regmap\n");
return PTR_ERR(priv->syscon);
}
mode = &priv->mode;
mode->fixed = PHY_NONE;
ret = of_property_read_u32(np, "hisilicon,fixed-mode", &mode->fixed);
if (ret == 0)
dev_dbg(dev, "found fixed phy mode %d\n", mode->fixed);
ret = of_property_read_u32_array(np, "hisilicon,mode-select-bits",
vals, ARRAY_SIZE(vals));
if (ret == 0) {
if (is_mode_fixed(mode)) {
dev_err(dev, "found select bits for fixed mode phy\n");
return -EINVAL;
}
mode->reg = vals[0];
mode->shift = vals[1];
mode->mask = vals[2];
dev_dbg(dev, "found mode select bits\n");
} else {
if (!is_mode_fixed(mode)) {
dev_err(dev, "no valid select bits found for non-fixed phy\n");
return -ENODEV;
}
}
priv->ref_clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->ref_clk)) {
dev_err(dev, "failed to find ref clock\n");
return PTR_ERR(priv->ref_clk);
}
priv->por_rst = devm_reset_control_get(dev, NULL);
if (IS_ERR(priv->por_rst)) {
dev_err(dev, "failed to get poweron reset\n");
return PTR_ERR(priv->por_rst);
}
priv->phy = devm_phy_create(dev, NULL, &histb_combphy_ops);
if (IS_ERR(priv->phy)) {
dev_err(dev, "failed to create combphy\n");
return PTR_ERR(priv->phy);
}
dev_set_drvdata(dev, priv);
phy_set_drvdata(priv->phy, priv);
phy_provider = devm_of_phy_provider_register(dev, histb_combphy_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id histb_combphy_of_match[] = {
{ .compatible = "hisilicon,hi3798cv200-combphy" },
{ },
};
MODULE_DEVICE_TABLE(of, histb_combphy_of_match);
static struct platform_driver histb_combphy_driver = {
.probe = histb_combphy_probe,
.driver = {
.name = "combphy",
.of_match_table = histb_combphy_of_match,
},
};
module_platform_driver(histb_combphy_driver);
MODULE_DESCRIPTION("HiSilicon STB COMBPHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -127,7 +127,7 @@ static int phy_berlin_usb_power_on(struct phy *phy)
writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
priv->base + USB_PHY_ANALOG); priv->base + USB_PHY_ANALOG);
writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | DISCON_THRESHOLD_270 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);

View File

@ -306,6 +306,8 @@ struct mtk_tphy {
const struct mtk_phy_pdata *pdata; const struct mtk_phy_pdata *pdata;
struct mtk_phy_instance **phys; struct mtk_phy_instance **phys;
int nphys; int nphys;
int src_ref_clk; /* MHZ, reference clock for slew rate calibrate */
int src_coef; /* coefficient for slew rate calibrate */
}; };
static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
@ -360,16 +362,17 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
writel(tmp, fmreg + U3P_U2FREQ_FMMONR1); writel(tmp, fmreg + U3P_U2FREQ_FMMONR1);
if (fm_out) { if (fm_out) {
/* ( 1024 / FM_OUT ) x reference clock frequency x 0.028 */ /* ( 1024 / FM_OUT ) x reference clock frequency x coef */
tmp = U3P_FM_DET_CYCLE_CNT * U3P_REF_CLK * U3P_SLEW_RATE_COEF; tmp = tphy->src_ref_clk * tphy->src_coef;
tmp /= fm_out; tmp = (tmp * U3P_FM_DET_CYCLE_CNT) / fm_out;
calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR); calibration_val = DIV_ROUND_CLOSEST(tmp, U3P_SR_COEF_DIVISOR);
} else { } else {
/* if FM detection fail, set default value */ /* if FM detection fail, set default value */
calibration_val = 4; calibration_val = 4;
} }
dev_dbg(tphy->dev, "phy:%d, fm_out:%d, calib:%d\n", dev_dbg(tphy->dev, "phy:%d, fm_out:%d, calib:%d (clk:%d, coef:%d)\n",
instance->index, fm_out, calibration_val); instance->index, fm_out, calibration_val,
tphy->src_ref_clk, tphy->src_coef);
/* set HS slew rate */ /* set HS slew rate */
tmp = readl(com + U3P_USBPHYACR5); tmp = readl(com + U3P_USBPHYACR5);
@ -688,8 +691,7 @@ static void pcie_phy_instance_power_on(struct mtk_tphy *tphy,
u32 tmp; u32 tmp;
tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD);
tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_MCU_BUS_CK_GATE_EN | tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST);
P3C_REG_IP_SW_RST);
writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD);
tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE);
@ -1042,6 +1044,13 @@ static int mtk_tphy_probe(struct platform_device *pdev)
tphy->u3phya_ref = NULL; tphy->u3phya_ref = NULL;
} }
tphy->src_ref_clk = U3P_REF_CLK;
tphy->src_coef = U3P_SLEW_RATE_COEF;
/* update parameters of slew rate calibrate if exist */
device_property_read_u32(dev, "mediatek,src-ref-clk-mhz",
&tphy->src_ref_clk);
device_property_read_u32(dev, "mediatek,src-coef", &tphy->src_coef);
port = 0; port = 0;
for_each_child_of_node(np, child_np) { for_each_child_of_node(np, child_np) {
struct mtk_phy_instance *instance; struct mtk_phy_instance *instance;

View File

@ -10,3 +10,11 @@ config PHY_CPCAP_USB
help help
Enable this for USB to work on Motorola phones and tablets Enable this for USB to work on Motorola phones and tablets
such as Droid 4. such as Droid 4.
config PHY_MAPPHONE_MDM6600
tristate "Motorola Mapphone MDM6600 modem USB PHY driver"
depends on OF && USB_SUPPORT
select GENERIC_PHY
help
Enable this for MDM6600 USB modem to work on Motorola phones
and tablets such as Droid 4.

View File

@ -3,3 +3,4 @@
# #
obj-$(CONFIG_PHY_CPCAP_USB) += phy-cpcap-usb.o obj-$(CONFIG_PHY_CPCAP_USB) += phy-cpcap-usb.o
obj-$(CONFIG_PHY_MAPPHONE_MDM6600) += phy-mapphone-mdm6600.o

View File

@ -0,0 +1,542 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Motorola Mapphone MDM6600 modem GPIO controlled USB PHY driver
* Copyright (C) 2018 Tony Lindgren <tony@atomide.com>
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#define PHY_MDM6600_PHY_DELAY_MS 4000 /* PHY enable 2.2s to 3.5s */
#define PHY_MDM6600_ENABLED_DELAY_MS 8000 /* 8s more total for MDM6600 */
enum phy_mdm6600_ctrl_lines {
PHY_MDM6600_ENABLE, /* USB PHY enable */
PHY_MDM6600_POWER, /* Device power */
PHY_MDM6600_RESET, /* Device reset */
PHY_MDM6600_NR_CTRL_LINES,
};
enum phy_mdm6600_bootmode_lines {
PHY_MDM6600_MODE0, /* out USB mode0 and OOB wake */
PHY_MDM6600_MODE1, /* out USB mode1, in OOB wake */
PHY_MDM6600_NR_MODE_LINES,
};
enum phy_mdm6600_cmd_lines {
PHY_MDM6600_CMD0,
PHY_MDM6600_CMD1,
PHY_MDM6600_CMD2,
PHY_MDM6600_NR_CMD_LINES,
};
enum phy_mdm6600_status_lines {
PHY_MDM6600_STATUS0,
PHY_MDM6600_STATUS1,
PHY_MDM6600_STATUS2,
PHY_MDM6600_NR_STATUS_LINES,
};
/*
* MDM6600 command codes. These are based on Motorola Mapphone Linux
* kernel tree.
*/
enum phy_mdm6600_cmd {
PHY_MDM6600_CMD_BP_PANIC_ACK,
PHY_MDM6600_CMD_DATA_ONLY_BYPASS, /* Reroute USB to CPCAP PHY */
PHY_MDM6600_CMD_FULL_BYPASS, /* Reroute USB to CPCAP PHY */
PHY_MDM6600_CMD_NO_BYPASS, /* Request normal USB mode */
PHY_MDM6600_CMD_BP_SHUTDOWN_REQ, /* Request device power off */
PHY_MDM6600_CMD_BP_UNKNOWN_5,
PHY_MDM6600_CMD_BP_UNKNOWN_6,
PHY_MDM6600_CMD_UNDEFINED,
};
/*
* MDM6600 status codes. These are based on Motorola Mapphone Linux
* kernel tree.
*/
enum phy_mdm6600_status {
PHY_MDM6600_STATUS_PANIC, /* Seems to be really off */
PHY_MDM6600_STATUS_PANIC_BUSY_WAIT,
PHY_MDM6600_STATUS_QC_DLOAD,
PHY_MDM6600_STATUS_RAM_DOWNLOADER, /* MDM6600 USB flashing mode */
PHY_MDM6600_STATUS_PHONE_CODE_AWAKE, /* MDM6600 normal USB mode */
PHY_MDM6600_STATUS_PHONE_CODE_ASLEEP,
PHY_MDM6600_STATUS_SHUTDOWN_ACK,
PHY_MDM6600_STATUS_UNDEFINED,
};
static const char * const
phy_mdm6600_status_name[] = {
"off", "busy", "qc_dl", "ram_dl", "awake",
"asleep", "shutdown", "undefined",
};
struct phy_mdm6600 {
struct device *dev;
struct phy *generic_phy;
struct phy_provider *phy_provider;
struct gpio_desc *ctrl_gpios[PHY_MDM6600_NR_CTRL_LINES];
struct gpio_descs *mode_gpios;
struct gpio_descs *status_gpios;
struct gpio_descs *cmd_gpios;
struct delayed_work bootup_work;
struct delayed_work status_work;
struct completion ack;
bool enabled; /* mdm6600 phy enabled */
bool running; /* mdm6600 boot done */
int status;
};
static int phy_mdm6600_init(struct phy *x)
{
struct phy_mdm6600 *ddata = phy_get_drvdata(x);
struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
if (!ddata->enabled)
return -EPROBE_DEFER;
gpiod_set_value_cansleep(enable_gpio, 0);
return 0;
}
static int phy_mdm6600_power_on(struct phy *x)
{
struct phy_mdm6600 *ddata = phy_get_drvdata(x);
struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
if (!ddata->enabled)
return -ENODEV;
gpiod_set_value_cansleep(enable_gpio, 1);
return 0;
}
static int phy_mdm6600_power_off(struct phy *x)
{
struct phy_mdm6600 *ddata = phy_get_drvdata(x);
struct gpio_desc *enable_gpio = ddata->ctrl_gpios[PHY_MDM6600_ENABLE];
if (!ddata->enabled)
return -ENODEV;
gpiod_set_value_cansleep(enable_gpio, 0);
return 0;
}
static const struct phy_ops gpio_usb_ops = {
.init = phy_mdm6600_init,
.power_on = phy_mdm6600_power_on,
.power_off = phy_mdm6600_power_off,
.owner = THIS_MODULE,
};
/**
* phy_mdm6600_cmd() - send a command request to mdm6600
* @ddata: device driver data
*
* Configures the three command request GPIOs to the specified value.
*/
static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
{
int values[PHY_MDM6600_NR_CMD_LINES];
int i;
val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
values[i] = (val & BIT(i)) >> i;
gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
ddata->cmd_gpios->desc, values);
}
/**
* phy_mdm6600_status() - read mdm6600 status lines
* @ddata: device driver data
*/
static void phy_mdm6600_status(struct work_struct *work)
{
struct phy_mdm6600 *ddata;
struct device *dev;
int values[PHY_MDM6600_NR_STATUS_LINES];
int error, i, val = 0;
ddata = container_of(work, struct phy_mdm6600, status_work.work);
dev = ddata->dev;
error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
ddata->status_gpios->desc,
values);
if (error)
return;
for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++) {
val |= values[i] << i;
dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
__func__, i, values[i], val);
}
ddata->status = val;
dev_info(dev, "modem status: %i %s\n",
ddata->status,
phy_mdm6600_status_name[ddata->status & 7]);
complete(&ddata->ack);
}
static irqreturn_t phy_mdm6600_irq_thread(int irq, void *data)
{
struct phy_mdm6600 *ddata = data;
schedule_delayed_work(&ddata->status_work, msecs_to_jiffies(10));
return IRQ_HANDLED;
}
/**
* phy_mdm6600_wakeirq_thread - handle mode1 line OOB wake after booting
* @irq: interrupt
* @data: interrupt handler data
*
* GPIO mode1 is used initially as output to configure the USB boot
* mode for mdm6600. After booting it is used as input for OOB wake
* signal from mdm6600 to the SoC. Just use it for debug info only
* for now.
*/
static irqreturn_t phy_mdm6600_wakeirq_thread(int irq, void *data)
{
struct phy_mdm6600 *ddata = data;
struct gpio_desc *mode_gpio1;
mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
dev_dbg(ddata->dev, "OOB wake on mode_gpio1: %i\n",
gpiod_get_value(mode_gpio1));
return IRQ_HANDLED;
}
/**
* phy_mdm6600_init_irq() - initialize mdm6600 status IRQ lines
* @ddata: device driver data
*/
static void phy_mdm6600_init_irq(struct phy_mdm6600 *ddata)
{
struct device *dev = ddata->dev;
int i, error, irq;
for (i = PHY_MDM6600_STATUS0;
i <= PHY_MDM6600_STATUS2; i++) {
struct gpio_desc *gpio = ddata->status_gpios->desc[i];
irq = gpiod_to_irq(gpio);
if (irq <= 0)
continue;
error = devm_request_threaded_irq(dev, irq, NULL,
phy_mdm6600_irq_thread,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"mdm6600",
ddata);
if (error)
dev_warn(dev, "no modem status irq%i: %i\n",
irq, error);
}
}
struct phy_mdm6600_map {
const char *name;
int direction;
};
static const struct phy_mdm6600_map
phy_mdm6600_ctrl_gpio_map[PHY_MDM6600_NR_CTRL_LINES] = {
{ "enable", GPIOD_OUT_LOW, }, /* low = phy disabled */
{ "power", GPIOD_OUT_LOW, }, /* low = off */
{ "reset", GPIOD_OUT_HIGH, }, /* high = reset */
};
/**
* phy_mdm6600_init_lines() - initialize mdm6600 GPIO lines
* @ddata: device driver data
*/
static int phy_mdm6600_init_lines(struct phy_mdm6600 *ddata)
{
struct device *dev = ddata->dev;
int i;
/* MDM6600 control lines */
for (i = 0; i < ARRAY_SIZE(phy_mdm6600_ctrl_gpio_map); i++) {
const struct phy_mdm6600_map *map =
&phy_mdm6600_ctrl_gpio_map[i];
struct gpio_desc **gpio = &ddata->ctrl_gpios[i];
*gpio = devm_gpiod_get(dev, map->name, map->direction);
if (IS_ERR(*gpio)) {
dev_info(dev, "gpio %s error %li\n",
map->name, PTR_ERR(*gpio));
return PTR_ERR(*gpio);
}
}
/* MDM6600 USB start-up mode output lines */
ddata->mode_gpios = devm_gpiod_get_array(dev, "motorola,mode",
GPIOD_OUT_LOW);
if (IS_ERR(ddata->mode_gpios))
return PTR_ERR(ddata->mode_gpios);
if (ddata->mode_gpios->ndescs != PHY_MDM6600_NR_MODE_LINES)
return -EINVAL;
/* MDM6600 status input lines */
ddata->status_gpios = devm_gpiod_get_array(dev, "motorola,status",
GPIOD_IN);
if (IS_ERR(ddata->status_gpios))
return PTR_ERR(ddata->status_gpios);
if (ddata->status_gpios->ndescs != PHY_MDM6600_NR_STATUS_LINES)
return -EINVAL;
/* MDM6600 cmd output lines */
ddata->cmd_gpios = devm_gpiod_get_array(dev, "motorola,cmd",
GPIOD_OUT_LOW);
if (IS_ERR(ddata->cmd_gpios))
return PTR_ERR(ddata->cmd_gpios);
if (ddata->cmd_gpios->ndescs != PHY_MDM6600_NR_CMD_LINES)
return -EINVAL;
return 0;
}
/**
* phy_mdm6600_device_power_on() - power on mdm6600 device
* @ddata: device driver data
*
* To get the integrated USB phy in MDM6600 takes some hoops. We must ensure
* the shared USB bootmode GPIOs are configured, then request modem start-up,
* reset and power-up.. And then we need to recycle the shared USB bootmode
* GPIOs as they are also used for Out of Band (OOB) wake for the USB and
* TS 27.010 serial mux.
*/
static int phy_mdm6600_device_power_on(struct phy_mdm6600 *ddata)
{
struct gpio_desc *mode_gpio0, *mode_gpio1, *reset_gpio, *power_gpio;
int error = 0, wakeirq;
mode_gpio0 = ddata->mode_gpios->desc[PHY_MDM6600_MODE0];
mode_gpio1 = ddata->mode_gpios->desc[PHY_MDM6600_MODE1];
reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
power_gpio = ddata->ctrl_gpios[PHY_MDM6600_POWER];
/*
* Shared GPIOs must be low for normal USB mode. After booting
* they are used for OOB wake signaling. These can be also used
* to configure USB flashing mode later on based on a module
* parameter.
*/
gpiod_set_value_cansleep(mode_gpio0, 0);
gpiod_set_value_cansleep(mode_gpio1, 0);
/* Request start-up mode */
phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_NO_BYPASS);
/* Request a reset first */
gpiod_set_value_cansleep(reset_gpio, 0);
msleep(100);
/* Toggle power GPIO to request mdm6600 to start */
gpiod_set_value_cansleep(power_gpio, 1);
msleep(100);
gpiod_set_value_cansleep(power_gpio, 0);
/*
* Looks like the USB PHY needs between 2.2 to 4 seconds.
* If we try to use it before that, we will get L3 errors
* from omap-usb-host trying to access the PHY. See also
* phy_mdm6600_init() for -EPROBE_DEFER.
*/
msleep(PHY_MDM6600_PHY_DELAY_MS);
ddata->enabled = true;
/* Booting up the rest of MDM6600 will take total about 8 seconds */
dev_info(ddata->dev, "Waiting for power up request to complete..\n");
if (wait_for_completion_timeout(&ddata->ack,
msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS))) {
if (ddata->status > PHY_MDM6600_STATUS_PANIC &&
ddata->status < PHY_MDM6600_STATUS_SHUTDOWN_ACK)
dev_info(ddata->dev, "Powered up OK\n");
} else {
ddata->enabled = false;
error = -ETIMEDOUT;
dev_err(ddata->dev, "Timed out powering up\n");
}
/* Reconfigure mode1 GPIO as input for OOB wake */
gpiod_direction_input(mode_gpio1);
wakeirq = gpiod_to_irq(mode_gpio1);
if (wakeirq <= 0)
return wakeirq;
error = devm_request_threaded_irq(ddata->dev, wakeirq, NULL,
phy_mdm6600_wakeirq_thread,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"mdm6600-wake",
ddata);
if (error)
dev_warn(ddata->dev, "no modem wakeirq irq%i: %i\n",
wakeirq, error);
ddata->running = true;
return error;
}
/**
* phy_mdm6600_device_power_off() - power off mdm6600 device
* @ddata: device driver data
*/
static void phy_mdm6600_device_power_off(struct phy_mdm6600 *ddata)
{
struct gpio_desc *reset_gpio =
ddata->ctrl_gpios[PHY_MDM6600_RESET];
ddata->enabled = false;
phy_mdm6600_cmd(ddata, PHY_MDM6600_CMD_BP_SHUTDOWN_REQ);
msleep(100);
gpiod_set_value_cansleep(reset_gpio, 1);
dev_info(ddata->dev, "Waiting for power down request to complete.. ");
if (wait_for_completion_timeout(&ddata->ack,
msecs_to_jiffies(5000))) {
if (ddata->status == PHY_MDM6600_STATUS_PANIC)
dev_info(ddata->dev, "Powered down OK\n");
} else {
dev_err(ddata->dev, "Timed out powering down\n");
}
}
static void phy_mdm6600_deferred_power_on(struct work_struct *work)
{
struct phy_mdm6600 *ddata;
int error;
ddata = container_of(work, struct phy_mdm6600, bootup_work.work);
error = phy_mdm6600_device_power_on(ddata);
if (error)
dev_err(ddata->dev, "Device not functional\n");
}
static const struct of_device_id phy_mdm6600_id_table[] = {
{ .compatible = "motorola,mapphone-mdm6600", },
{},
};
MODULE_DEVICE_TABLE(of, phy_mdm6600_id_table);
static int phy_mdm6600_probe(struct platform_device *pdev)
{
struct phy_mdm6600 *ddata;
int error;
ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
INIT_DELAYED_WORK(&ddata->bootup_work,
phy_mdm6600_deferred_power_on);
INIT_DELAYED_WORK(&ddata->status_work, phy_mdm6600_status);
init_completion(&ddata->ack);
ddata->dev = &pdev->dev;
platform_set_drvdata(pdev, ddata);
error = phy_mdm6600_init_lines(ddata);
if (error)
return error;
phy_mdm6600_init_irq(ddata);
ddata->generic_phy = devm_phy_create(ddata->dev, NULL, &gpio_usb_ops);
if (IS_ERR(ddata->generic_phy)) {
error = PTR_ERR(ddata->generic_phy);
goto cleanup;
}
phy_set_drvdata(ddata->generic_phy, ddata);
ddata->phy_provider =
devm_of_phy_provider_register(ddata->dev,
of_phy_simple_xlate);
if (IS_ERR(ddata->phy_provider)) {
error = PTR_ERR(ddata->phy_provider);
goto cleanup;
}
schedule_delayed_work(&ddata->bootup_work, 0);
/*
* See phy_mdm6600_device_power_on(). We should be able
* to remove this eventually when ohci-platform can deal
* with -EPROBE_DEFER.
*/
msleep(PHY_MDM6600_PHY_DELAY_MS + 500);
return 0;
cleanup:
phy_mdm6600_device_power_off(ddata);
return error;
}
static int phy_mdm6600_remove(struct platform_device *pdev)
{
struct phy_mdm6600 *ddata = platform_get_drvdata(pdev);
struct gpio_desc *reset_gpio = ddata->ctrl_gpios[PHY_MDM6600_RESET];
if (!ddata->running)
wait_for_completion_timeout(&ddata->ack,
msecs_to_jiffies(PHY_MDM6600_ENABLED_DELAY_MS));
gpiod_set_value_cansleep(reset_gpio, 1);
phy_mdm6600_device_power_off(ddata);
cancel_delayed_work_sync(&ddata->bootup_work);
cancel_delayed_work_sync(&ddata->status_work);
return 0;
}
static struct platform_driver phy_mdm6600_driver = {
.probe = phy_mdm6600_probe,
.remove = phy_mdm6600_remove,
.driver = {
.name = "phy-mapphone-mdm6600",
.of_match_table = of_match_ptr(phy_mdm6600_id_table),
},
};
module_platform_driver(phy_mdm6600_driver);
MODULE_ALIAS("platform:gpio_usb");
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
MODULE_DESCRIPTION("mdm6600 gpio usb phy driver");
MODULE_LICENSE("GPL v2");

View File

@ -351,6 +351,8 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
mutex_lock(&phy->mutex); mutex_lock(&phy->mutex);
ret = phy->ops->set_mode(phy, mode); ret = phy->ops->set_mode(phy, mode);
if (!ret)
phy->attrs.mode = mode;
mutex_unlock(&phy->mutex); mutex_unlock(&phy->mutex);
return ret; return ret;

View File

@ -60,8 +60,14 @@ static int lpc18xx_usb_otg_phy_power_on(struct phy *phy)
return ret; return ret;
/* The bit in CREG is cleared to enable the PHY */ /* The bit in CREG is cleared to enable the PHY */
return regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0, ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
LPC18XX_CREG_CREG0_USB0PHY, 0); LPC18XX_CREG_CREG0_USB0PHY, 0);
if (ret) {
clk_disable(lpc->clk);
return ret;
}
return 0;
} }
static int lpc18xx_usb_otg_phy_power_off(struct phy *phy) static int lpc18xx_usb_otg_phy_power_off(struct phy *phy)

View File

@ -1,15 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (c) 2017, The Linux Foundation. All rights reserved. * Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/ */
#include <linux/clk.h> #include <linux/clk.h>
@ -31,124 +22,7 @@
#include <dt-bindings/phy/phy.h> #include <dt-bindings/phy/phy.h>
/* QMP PHY QSERDES COM registers */ #include "phy-qcom-qmp.h"
#define QSERDES_COM_BG_TIMER 0x00c
#define QSERDES_COM_SSC_EN_CENTER 0x010
#define QSERDES_COM_SSC_ADJ_PER1 0x014
#define QSERDES_COM_SSC_ADJ_PER2 0x018
#define QSERDES_COM_SSC_PER1 0x01c
#define QSERDES_COM_SSC_PER2 0x020
#define QSERDES_COM_SSC_STEP_SIZE1 0x024
#define QSERDES_COM_SSC_STEP_SIZE2 0x028
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034
#define QSERDES_COM_CLK_ENABLE1 0x038
#define QSERDES_COM_SYS_CLK_CTRL 0x03c
#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040
#define QSERDES_COM_PLL_IVCO 0x048
#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c
#define QSERDES_COM_LOCK_CMP2_MODE0 0x050
#define QSERDES_COM_LOCK_CMP3_MODE0 0x054
#define QSERDES_COM_LOCK_CMP1_MODE1 0x058
#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c
#define QSERDES_COM_LOCK_CMP3_MODE1 0x060
#define QSERDES_COM_BG_TRIM 0x070
#define QSERDES_COM_CLK_EP_DIV 0x074
#define QSERDES_COM_CP_CTRL_MODE0 0x078
#define QSERDES_COM_CP_CTRL_MODE1 0x07c
#define QSERDES_COM_PLL_RCTRL_MODE0 0x084
#define QSERDES_COM_PLL_RCTRL_MODE1 0x088
#define QSERDES_COM_PLL_CCTRL_MODE0 0x090
#define QSERDES_COM_PLL_CCTRL_MODE1 0x094
#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8
#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac
#define QSERDES_COM_RESETSM_CNTRL 0x0b4
#define QSERDES_COM_RESTRIM_CTRL 0x0bc
#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4
#define QSERDES_COM_LOCK_CMP_EN 0x0c8
#define QSERDES_COM_LOCK_CMP_CFG 0x0cc
#define QSERDES_COM_DEC_START_MODE0 0x0d0
#define QSERDES_COM_DEC_START_MODE1 0x0d4
#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc
#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0
#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4
#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8
#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec
#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114
#define QSERDES_COM_VCO_TUNE_CTRL 0x124
#define QSERDES_COM_VCO_TUNE_MAP 0x128
#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c
#define QSERDES_COM_VCO_TUNE2_MODE0 0x130
#define QSERDES_COM_VCO_TUNE1_MODE1 0x134
#define QSERDES_COM_VCO_TUNE2_MODE1 0x138
#define QSERDES_COM_VCO_TUNE_TIMER1 0x144
#define QSERDES_COM_VCO_TUNE_TIMER2 0x148
#define QSERDES_COM_BG_CTRL 0x170
#define QSERDES_COM_CLK_SELECT 0x174
#define QSERDES_COM_HSCLK_SEL 0x178
#define QSERDES_COM_CORECLK_DIV 0x184
#define QSERDES_COM_CORE_CLK_EN 0x18c
#define QSERDES_COM_C_READY_STATUS 0x190
#define QSERDES_COM_CMN_CONFIG 0x194
#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c
#define QSERDES_COM_DEBUG_BUS0 0x1a0
#define QSERDES_COM_DEBUG_BUS1 0x1a4
#define QSERDES_COM_DEBUG_BUS2 0x1a8
#define QSERDES_COM_DEBUG_BUS3 0x1ac
#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0
#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc
/* QMP PHY TX registers */
#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054
#define QSERDES_TX_DEBUG_BUS_SEL 0x064
#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068
#define QSERDES_TX_LANE_MODE 0x094
#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac
/* QMP PHY RX registers */
#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010
#define QSERDES_RX_UCDR_SO_GAIN 0x01c
#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040
#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048
#define QSERDES_RX_RX_TERM_BW 0x090
#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4
#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8
#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc
#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0
#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108
#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c
#define QSERDES_RX_SIGDET_ENABLES 0x110
#define QSERDES_RX_SIGDET_CNTRL 0x114
#define QSERDES_RX_SIGDET_LVL 0x118
#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c
#define QSERDES_RX_RX_BAND 0x120
#define QSERDES_RX_RX_INTERFACE_MODE 0x12c
/* QMP PHY PCS registers */
#define QPHY_POWER_DOWN_CONTROL 0x04
#define QPHY_TXDEEMPH_M6DB_V0 0x24
#define QPHY_TXDEEMPH_M3P5DB_V0 0x28
#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54
#define QPHY_RX_IDLE_DTCT_CNTRL 0x58
#define QPHY_POWER_STATE_CONFIG1 0x60
#define QPHY_POWER_STATE_CONFIG2 0x64
#define QPHY_POWER_STATE_CONFIG4 0x6c
#define QPHY_LOCK_DETECT_CONFIG1 0x80
#define QPHY_LOCK_DETECT_CONFIG2 0x84
#define QPHY_LOCK_DETECT_CONFIG3 0x88
#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0
#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4
#define QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB 0x1A8
#define QPHY_OSC_DTCT_ACTIONS 0x1AC
#define QPHY_RX_SIGDET_LVL 0x1D8
#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1DC
#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1E0
/* QPHY_SW_RESET bit */ /* QPHY_SW_RESET bit */
#define SW_RESET BIT(0) #define SW_RESET BIT(0)
@ -164,6 +38,34 @@
/* QPHY_COM_PCS_READY_STATUS bit */ /* QPHY_COM_PCS_READY_STATUS bit */
#define PCS_READY BIT(0) #define PCS_READY BIT(0)
/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */
/* DP PHY soft reset */
#define SW_DPPHY_RESET BIT(0)
/* mux to select DP PHY reset control, 0:HW control, 1: software reset */
#define SW_DPPHY_RESET_MUX BIT(1)
/* USB3 PHY soft reset */
#define SW_USB3PHY_RESET BIT(2)
/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
#define SW_USB3PHY_RESET_MUX BIT(3)
/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */
#define USB3_MODE BIT(0) /* enables USB3 mode */
#define DP_MODE BIT(1) /* enables DP mode */
/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */
#define ARCVR_DTCT_EN BIT(0)
#define ALFPS_DTCT_EN BIT(1)
#define ARCVR_DTCT_EVENT_SEL BIT(4)
/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */
#define IRQ_CLEAR BIT(0)
/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */
#define RCVR_DETECT BIT(0)
/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */
#define CLAMP_EN BIT(0) /* enables i/o clamp_n */
#define PHY_INIT_COMPLETE_TIMEOUT 1000 #define PHY_INIT_COMPLETE_TIMEOUT 1000
#define POWER_DOWN_DELAY_US_MIN 10 #define POWER_DOWN_DELAY_US_MIN 10
#define POWER_DOWN_DELAY_US_MAX 11 #define POWER_DOWN_DELAY_US_MAX 11
@ -210,6 +112,9 @@ enum qphy_reg_layout {
QPHY_SW_RESET, QPHY_SW_RESET,
QPHY_START_CTRL, QPHY_START_CTRL,
QPHY_PCS_READY_STATUS, QPHY_PCS_READY_STATUS,
QPHY_PCS_AUTONOMOUS_MODE_CTRL,
QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
QPHY_PCS_LFPS_RXTERM_IRQ_STATUS,
}; };
static const unsigned int pciephy_regs_layout[] = { static const unsigned int pciephy_regs_layout[] = {
@ -237,6 +142,18 @@ static const unsigned int usb3phy_regs_layout[] = {
[QPHY_SW_RESET] = 0x00, [QPHY_SW_RESET] = 0x00,
[QPHY_START_CTRL] = 0x08, [QPHY_START_CTRL] = 0x08,
[QPHY_PCS_READY_STATUS] = 0x17c, [QPHY_PCS_READY_STATUS] = 0x17c,
[QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d4,
[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0d8,
[QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178,
};
static const unsigned int qmp_v3_usb3phy_regs_layout[] = {
[QPHY_SW_RESET] = 0x00,
[QPHY_START_CTRL] = 0x08,
[QPHY_PCS_READY_STATUS] = 0x174,
[QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8,
[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc,
[QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
}; };
static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
@ -467,6 +384,112 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = {
QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3), QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3),
}; };
static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07),
};
static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06),
};
static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75),
};
static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = {
/* FLL settings */
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
/* Lock Det settings */
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
};
/* struct qmp_phy_cfg - per-PHY initialization config */ /* struct qmp_phy_cfg - per-PHY initialization config */
struct qmp_phy_cfg { struct qmp_phy_cfg {
/* phy-type - PCIE/UFS/USB */ /* phy-type - PCIE/UFS/USB */
@ -511,6 +534,12 @@ struct qmp_phy_cfg {
/* power_down delay in usec */ /* power_down delay in usec */
int pwrdn_delay_min; int pwrdn_delay_min;
int pwrdn_delay_max; int pwrdn_delay_max;
/* true, if PHY has a separate DP_COM control block */
bool has_phy_dp_com_ctrl;
/* Register offset of secondary tx/rx lanes for USB DP combo PHY */
unsigned int tx_b_lane_offset;
unsigned int rx_b_lane_offset;
}; };
/** /**
@ -520,6 +549,7 @@ struct qmp_phy_cfg {
* @tx: iomapped memory space for lane's tx * @tx: iomapped memory space for lane's tx
* @rx: iomapped memory space for lane's rx * @rx: iomapped memory space for lane's rx
* @pcs: iomapped memory space for lane's pcs * @pcs: iomapped memory space for lane's pcs
* @pcs_misc: iomapped memory space for lane's pcs_misc
* @pipe_clk: pipe lock * @pipe_clk: pipe lock
* @index: lane index * @index: lane index
* @qmp: QMP phy to which this lane belongs * @qmp: QMP phy to which this lane belongs
@ -530,6 +560,7 @@ struct qmp_phy {
void __iomem *tx; void __iomem *tx;
void __iomem *rx; void __iomem *rx;
void __iomem *pcs; void __iomem *pcs;
void __iomem *pcs_misc;
struct clk *pipe_clk; struct clk *pipe_clk;
unsigned int index; unsigned int index;
struct qcom_qmp *qmp; struct qcom_qmp *qmp;
@ -541,6 +572,7 @@ struct qmp_phy {
* *
* @dev: device * @dev: device
* @serdes: iomapped memory space for phy's serdes * @serdes: iomapped memory space for phy's serdes
* @dp_com: iomapped memory space for phy's dp_com control block
* *
* @clks: array of clocks required by phy * @clks: array of clocks required by phy
* @resets: array of resets required by phy * @resets: array of resets required by phy
@ -550,12 +582,15 @@ struct qmp_phy {
* @phys: array of per-lane phy descriptors * @phys: array of per-lane phy descriptors
* @phy_mutex: mutex lock for PHY common block initialization * @phy_mutex: mutex lock for PHY common block initialization
* @init_count: phy common block initialization count * @init_count: phy common block initialization count
* @phy_initialized: indicate if PHY has been initialized
* @mode: current PHY mode
*/ */
struct qcom_qmp { struct qcom_qmp {
struct device *dev; struct device *dev;
void __iomem *serdes; void __iomem *serdes;
void __iomem *dp_com;
struct clk **clks; struct clk_bulk_data *clks;
struct reset_control **resets; struct reset_control **resets;
struct regulator_bulk_data *vregs; struct regulator_bulk_data *vregs;
@ -564,6 +599,8 @@ struct qcom_qmp {
struct mutex phy_mutex; struct mutex phy_mutex;
int init_count; int init_count;
bool phy_initialized;
enum phy_mode mode;
}; };
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
@ -595,6 +632,10 @@ static const char * const msm8996_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "aux", "cfg_ahb", "ref",
}; };
static const char * const qmp_v3_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",
};
/* list of resets */ /* list of resets */
static const char * const msm8996_pciephy_reset_l[] = { static const char * const msm8996_pciephy_reset_l[] = {
"phy", "common", "cfg", "phy", "common", "cfg",
@ -701,6 +742,38 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = {
.pwrdn_delay_max = 1005, /* us */ .pwrdn_delay_max = 1005, /* us */
}; };
static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
.type = PHY_TYPE_USB3,
.nlanes = 1,
.serdes_tbl = qmp_v3_usb3_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
.tx_tbl = qmp_v3_usb3_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
.rx_tbl = qmp_v3_usb3_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
.pcs_tbl = qmp_v3_usb3_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
.clk_list = qmp_v3_phy_clk_l,
.num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.regs = qmp_v3_usb3phy_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
.pwrdn_ctrl = SW_PWRDN,
.mask_pcs_ready = PHYSTATUS,
.pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN,
.pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
.has_phy_dp_com_ctrl = true,
.tx_b_lane_offset = 0x400,
.rx_b_lane_offset = 0x400,
};
static void qcom_qmp_phy_configure(void __iomem *base, static void qcom_qmp_phy_configure(void __iomem *base,
const unsigned int *regs, const unsigned int *regs,
const struct qmp_phy_init_tbl tbl[], const struct qmp_phy_init_tbl tbl[],
@ -724,44 +797,20 @@ static int qcom_qmp_phy_poweron(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;
int num = qmp->cfg->num_vregs;
int ret; int ret;
dev_vdbg(&phy->dev, "Powering on QMP phy\n");
/* turn on regulator supplies */
ret = regulator_bulk_enable(num, qmp->vregs);
if (ret) {
dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
return ret;
}
ret = clk_prepare_enable(qphy->pipe_clk); ret = clk_prepare_enable(qphy->pipe_clk);
if (ret) { if (ret)
dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret); dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret);
regulator_bulk_disable(num, qmp->vregs);
return ret;
}
return 0; return ret;
}
static int qcom_qmp_phy_poweroff(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
clk_disable_unprepare(qphy->pipe_clk);
regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs);
return 0;
} }
static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp) static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
{ {
const struct qmp_phy_cfg *cfg = qmp->cfg; const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes; void __iomem *serdes = qmp->serdes;
void __iomem *dp_com = qmp->dp_com;
int ret, i; int ret, i;
mutex_lock(&qmp->phy_mutex); mutex_lock(&qmp->phy_mutex);
@ -770,7 +819,23 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
return 0; return 0;
} }
/* turn on regulator supplies */
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
if (ret) {
dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
goto err_reg_enable;
}
for (i = 0; i < cfg->num_resets; i++) { for (i = 0; i < cfg->num_resets; i++) {
ret = reset_control_assert(qmp->resets[i]);
if (ret) {
dev_err(qmp->dev, "%s reset assert failed\n",
cfg->reset_list[i]);
goto err_rst_assert;
}
}
for (i = cfg->num_resets - 1; i >= 0; i--) {
ret = reset_control_deassert(qmp->resets[i]); ret = reset_control_deassert(qmp->resets[i]);
if (ret) { if (ret) {
dev_err(qmp->dev, "%s reset deassert failed\n", dev_err(qmp->dev, "%s reset deassert failed\n",
@ -779,10 +844,33 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
} }
} }
ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
if (ret) {
dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret);
goto err_rst;
}
if (cfg->has_phy_com_ctrl) if (cfg->has_phy_com_ctrl)
qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
SW_PWRDN); SW_PWRDN);
if (cfg->has_phy_dp_com_ctrl) {
qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL,
SW_PWRDN);
/* override hardware control for reset of qmp phy */
qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL,
USB3_MODE | DP_MODE);
/* bring both QMP USB and QMP DP PHYs PCS block out of reset */
qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
}
/* Serdes configuration */ /* Serdes configuration */
qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl, qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl,
cfg->serdes_tbl_num); cfg->serdes_tbl_num);
@ -803,7 +891,7 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
if (ret) { if (ret) {
dev_err(qmp->dev, dev_err(qmp->dev,
"phy common block init timed-out\n"); "phy common block init timed-out\n");
goto err_rst; goto err_com_init;
} }
} }
@ -811,9 +899,14 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
return 0; return 0;
err_com_init:
clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
err_rst: err_rst:
while (--i >= 0) while (++i < cfg->num_resets)
reset_control_assert(qmp->resets[i]); reset_control_assert(qmp->resets[i]);
err_rst_assert:
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
err_reg_enable:
mutex_unlock(&qmp->phy_mutex); mutex_unlock(&qmp->phy_mutex);
return ret; return ret;
@ -843,6 +936,10 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
while (--i >= 0) while (--i >= 0)
reset_control_assert(qmp->resets[i]); reset_control_assert(qmp->resets[i]);
clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
mutex_unlock(&qmp->phy_mutex); mutex_unlock(&qmp->phy_mutex);
return 0; return 0;
@ -857,24 +954,16 @@ static int qcom_qmp_phy_init(struct phy *phy)
void __iomem *tx = qphy->tx; void __iomem *tx = qphy->tx;
void __iomem *rx = qphy->rx; void __iomem *rx = qphy->rx;
void __iomem *pcs = qphy->pcs; void __iomem *pcs = qphy->pcs;
void __iomem *dp_com = qmp->dp_com;
void __iomem *status; void __iomem *status;
unsigned int mask, val; unsigned int mask, val;
int ret, i; int ret;
dev_vdbg(qmp->dev, "Initializing QMP phy\n"); dev_vdbg(qmp->dev, "Initializing QMP phy\n");
for (i = 0; i < qmp->cfg->num_clks; i++) {
ret = clk_prepare_enable(qmp->clks[i]);
if (ret) {
dev_err(qmp->dev, "failed to enable %s clk, err=%d\n",
qmp->cfg->clk_list[i], ret);
goto err_clk;
}
}
ret = qcom_qmp_phy_com_init(qmp); ret = qcom_qmp_phy_com_init(qmp);
if (ret) if (ret)
goto err_clk; return ret;
if (cfg->has_lane_rst) { if (cfg->has_lane_rst) {
ret = reset_control_deassert(qphy->lane_rst); ret = reset_control_deassert(qphy->lane_rst);
@ -887,7 +976,16 @@ static int qcom_qmp_phy_init(struct phy *phy)
/* Tx, Rx, and PCS configurations */ /* Tx, Rx, and PCS configurations */
qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num);
/* Configuration for other LANE for USB-DP combo PHY */
if (cfg->has_phy_dp_com_ctrl)
qcom_qmp_phy_configure(tx + cfg->tx_b_lane_offset, cfg->regs,
cfg->tx_tbl, cfg->tx_tbl_num);
qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num);
if (cfg->has_phy_dp_com_ctrl)
qcom_qmp_phy_configure(rx + cfg->rx_b_lane_offset, cfg->regs,
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);
/* /*
@ -899,11 +997,13 @@ static int qcom_qmp_phy_init(struct phy *phy)
if (cfg->has_pwrdn_delay) if (cfg->has_pwrdn_delay)
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
/* start SerDes and Phy-Coding-Sublayer */
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
/* Pull PHY out of reset state */ /* Pull PHY out of reset state */
qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
if (cfg->has_phy_dp_com_ctrl)
qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
/* 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]; status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
mask = cfg->mask_pcs_ready; mask = cfg->mask_pcs_ready;
@ -914,6 +1014,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
dev_err(qmp->dev, "phy initialization timed-out\n"); dev_err(qmp->dev, "phy initialization timed-out\n");
goto err_pcs_ready; goto err_pcs_ready;
} }
qmp->phy_initialized = true;
return ret; return ret;
@ -922,9 +1023,6 @@ err_pcs_ready:
reset_control_assert(qphy->lane_rst); reset_control_assert(qphy->lane_rst);
err_lane_rst: err_lane_rst:
qcom_qmp_phy_com_exit(qmp); qcom_qmp_phy_com_exit(qmp);
err_clk:
while (--i >= 0)
clk_disable_unprepare(qmp->clks[i]);
return ret; return ret;
} }
@ -934,7 +1032,8 @@ static int qcom_qmp_phy_exit(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;
const struct qmp_phy_cfg *cfg = qmp->cfg; const struct qmp_phy_cfg *cfg = qmp->cfg;
int i = cfg->num_clks;
clk_disable_unprepare(qphy->pipe_clk);
/* PHY reset */ /* PHY reset */
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
@ -950,8 +1049,127 @@ static int qcom_qmp_phy_exit(struct phy *phy)
qcom_qmp_phy_com_exit(qmp); qcom_qmp_phy_com_exit(qmp);
while (--i >= 0) qmp->phy_initialized = false;
clk_disable_unprepare(qmp->clks[i]);
return 0;
}
static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
qmp->mode = mode;
return 0;
}
static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy)
{
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *pcs = qphy->pcs;
void __iomem *pcs_misc = qphy->pcs_misc;
u32 intr_mask;
if (qmp->mode == PHY_MODE_USB_HOST_SS ||
qmp->mode == PHY_MODE_USB_DEVICE_SS)
intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN;
else
intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL;
/* Clear any pending interrupts status */
qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
/* Writing 1 followed by 0 clears the interrupt */
qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL);
/* Enable required PHY autonomous mode interrupts */
qphy_setbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask);
/* Enable i/o clamp_n for autonomous mode */
if (pcs_misc)
qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
}
static void qcom_qmp_phy_disable_autonomous_mode(struct qmp_phy *qphy)
{
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *pcs = qphy->pcs;
void __iomem *pcs_misc = qphy->pcs_misc;
/* Disable i/o clamp_n on resume for normal mode */
if (pcs_misc)
qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN);
qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL],
ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN);
qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
/* Writing 1 followed by 0 clears the interrupt */
qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR);
}
static int __maybe_unused qcom_qmp_phy_runtime_suspend(struct device *dev)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
struct qmp_phy *qphy = qmp->phys[0];
const struct qmp_phy_cfg *cfg = qmp->cfg;
dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode);
/* Supported only for USB3 PHY */
if (cfg->type != PHY_TYPE_USB3)
return 0;
if (!qmp->phy_initialized) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
qcom_qmp_phy_enable_autonomous_mode(qphy);
clk_disable_unprepare(qphy->pipe_clk);
clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
return 0;
}
static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev)
{
struct qcom_qmp *qmp = dev_get_drvdata(dev);
struct qmp_phy *qphy = qmp->phys[0];
const struct qmp_phy_cfg *cfg = qmp->cfg;
int ret = 0;
dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode);
/* Supported only for USB3 PHY */
if (cfg->type != PHY_TYPE_USB3)
return 0;
if (!qmp->phy_initialized) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
if (ret) {
dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret);
return ret;
}
ret = clk_prepare_enable(qphy->pipe_clk);
if (ret) {
dev_err(dev, "pipe_clk enable failed, err=%d\n", ret);
clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
return ret;
}
qcom_qmp_phy_disable_autonomous_mode(qphy);
return 0; return 0;
} }
@ -1000,29 +1218,17 @@ static int qcom_qmp_phy_reset_init(struct device *dev)
static int qcom_qmp_phy_clk_init(struct device *dev) static int qcom_qmp_phy_clk_init(struct device *dev)
{ {
struct qcom_qmp *qmp = dev_get_drvdata(dev); struct qcom_qmp *qmp = dev_get_drvdata(dev);
int ret, i; int num = qmp->cfg->num_clks;
int i;
qmp->clks = devm_kcalloc(dev, qmp->cfg->num_clks, qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
sizeof(*qmp->clks), GFP_KERNEL);
if (!qmp->clks) if (!qmp->clks)
return -ENOMEM; return -ENOMEM;
for (i = 0; i < qmp->cfg->num_clks; i++) { for (i = 0; i < num; i++)
struct clk *_clk; qmp->clks[i].id = qmp->cfg->clk_list[i];
const char *name = qmp->cfg->clk_list[i];
_clk = devm_clk_get(dev, name); return devm_clk_bulk_get(dev, num, qmp->clks);
if (IS_ERR(_clk)) {
ret = PTR_ERR(_clk);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get %s clk, %d\n",
name, ret);
return ret;
}
qmp->clks[i] = _clk;
}
return 0;
} }
/* /*
@ -1078,7 +1284,7 @@ static const struct phy_ops qcom_qmp_phy_gen_ops = {
.init = qcom_qmp_phy_init, .init = qcom_qmp_phy_init,
.exit = qcom_qmp_phy_exit, .exit = qcom_qmp_phy_exit,
.power_on = qcom_qmp_phy_poweron, .power_on = qcom_qmp_phy_poweron,
.power_off = qcom_qmp_phy_poweroff, .set_mode = qcom_qmp_phy_set_mode,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
@ -1097,7 +1303,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
/* /*
* Get memory resources for each phy lane: * Get memory resources for each phy lane:
* Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2; and
* pcs_misc (optional) -> 3.
*/ */
qphy->tx = of_iomap(np, 0); qphy->tx = of_iomap(np, 0);
if (!qphy->tx) if (!qphy->tx)
@ -1111,6 +1318,10 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
if (!qphy->pcs) if (!qphy->pcs)
return -ENOMEM; return -ENOMEM;
qphy->pcs_misc = of_iomap(np, 3);
if (!qphy->pcs_misc)
dev_vdbg(dev, "PHY pcs_misc-reg not used\n");
/* /*
* Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3 * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3
* based phys, so they essentially have pipe clock. So, * based phys, so they essentially have pipe clock. So,
@ -1169,11 +1380,19 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, { }, {
.compatible = "qcom,ipq8074-qmp-pcie-phy", .compatible = "qcom,ipq8074-qmp-pcie-phy",
.data = &ipq8074_pciephy_cfg, .data = &ipq8074_pciephy_cfg,
}, {
.compatible = "qcom,qmp-v3-usb3-phy",
.data = &qmp_v3_usb3phy_cfg,
}, },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table); MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
static const struct dev_pm_ops qcom_qmp_phy_pm_ops = {
SET_RUNTIME_PM_OPS(qcom_qmp_phy_runtime_suspend,
qcom_qmp_phy_runtime_resume, NULL)
};
static int qcom_qmp_phy_probe(struct platform_device *pdev) static int qcom_qmp_phy_probe(struct platform_device *pdev)
{ {
struct qcom_qmp *qmp; struct qcom_qmp *qmp;
@ -1192,6 +1411,11 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
qmp->dev = dev; qmp->dev = dev;
dev_set_drvdata(dev, qmp); dev_set_drvdata(dev, qmp);
/* Get the specific init parameters of QMP phy */
qmp->cfg = of_device_get_match_data(dev);
if (!qmp->cfg)
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res); base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) if (IS_ERR(base))
@ -1200,10 +1424,18 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
/* per PHY serdes; usually located at base address */ /* per PHY serdes; usually located at base address */
qmp->serdes = base; qmp->serdes = base;
mutex_init(&qmp->phy_mutex); /* per PHY dp_com; if PHY has dp_com control block */
if (qmp->cfg->has_phy_dp_com_ctrl) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"dp_com");
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
/* Get the specific init parameters of QMP phy */ qmp->dp_com = base;
qmp->cfg = of_device_get_match_data(dev); }
mutex_init(&qmp->phy_mutex);
ret = qcom_qmp_phy_clk_init(dev); ret = qcom_qmp_phy_clk_init(dev);
if (ret) if (ret)
@ -1229,12 +1461,21 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
id = 0; id = 0;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
/*
* Prevent runtime pm from being ON by default. Users can enable
* it using power/control in sysfs.
*/
pm_runtime_forbid(dev);
for_each_available_child_of_node(dev->of_node, child) { for_each_available_child_of_node(dev->of_node, child) {
/* Create per-lane phy */ /* Create per-lane phy */
ret = qcom_qmp_phy_create(dev, child, id); ret = qcom_qmp_phy_create(dev, child, id);
if (ret) { if (ret) {
dev_err(dev, "failed to create lane%d phy, %d\n", dev_err(dev, "failed to create lane%d phy, %d\n",
id, ret); id, ret);
pm_runtime_disable(dev);
return ret; return ret;
} }
@ -1246,6 +1487,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
if (ret) { if (ret) {
dev_err(qmp->dev, dev_err(qmp->dev,
"failed to register pipe clock source\n"); "failed to register pipe clock source\n");
pm_runtime_disable(dev);
return ret; return ret;
} }
id++; id++;
@ -1254,6 +1496,8 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (!IS_ERR(phy_provider)) if (!IS_ERR(phy_provider))
dev_info(dev, "Registered Qcom-QMP phy\n"); dev_info(dev, "Registered Qcom-QMP phy\n");
else
pm_runtime_disable(dev);
return PTR_ERR_OR_ZERO(phy_provider); return PTR_ERR_OR_ZERO(phy_provider);
} }
@ -1262,6 +1506,7 @@ static struct platform_driver qcom_qmp_phy_driver = {
.probe = qcom_qmp_phy_probe, .probe = qcom_qmp_phy_probe,
.driver = { .driver = {
.name = "qcom-qmp-phy", .name = "qcom-qmp-phy",
.pm = &qcom_qmp_phy_pm_ops,
.of_match_table = qcom_qmp_phy_of_match_table, .of_match_table = qcom_qmp_phy_of_match_table,
}, },
}; };

View File

@ -0,0 +1,280 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017, The Linux Foundation. All rights reserved.
*/
#ifndef QCOM_PHY_QMP_H_
#define QCOM_PHY_QMP_H_
/* Only for QMP V2 PHY - QSERDES COM registers */
#define QSERDES_COM_BG_TIMER 0x00c
#define QSERDES_COM_SSC_EN_CENTER 0x010
#define QSERDES_COM_SSC_ADJ_PER1 0x014
#define QSERDES_COM_SSC_ADJ_PER2 0x018
#define QSERDES_COM_SSC_PER1 0x01c
#define QSERDES_COM_SSC_PER2 0x020
#define QSERDES_COM_SSC_STEP_SIZE1 0x024
#define QSERDES_COM_SSC_STEP_SIZE2 0x028
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034
#define QSERDES_COM_CLK_ENABLE1 0x038
#define QSERDES_COM_SYS_CLK_CTRL 0x03c
#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040
#define QSERDES_COM_PLL_IVCO 0x048
#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c
#define QSERDES_COM_LOCK_CMP2_MODE0 0x050
#define QSERDES_COM_LOCK_CMP3_MODE0 0x054
#define QSERDES_COM_LOCK_CMP1_MODE1 0x058
#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c
#define QSERDES_COM_LOCK_CMP3_MODE1 0x060
#define QSERDES_COM_BG_TRIM 0x070
#define QSERDES_COM_CLK_EP_DIV 0x074
#define QSERDES_COM_CP_CTRL_MODE0 0x078
#define QSERDES_COM_CP_CTRL_MODE1 0x07c
#define QSERDES_COM_PLL_RCTRL_MODE0 0x084
#define QSERDES_COM_PLL_RCTRL_MODE1 0x088
#define QSERDES_COM_PLL_CCTRL_MODE0 0x090
#define QSERDES_COM_PLL_CCTRL_MODE1 0x094
#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8
#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac
#define QSERDES_COM_RESETSM_CNTRL 0x0b4
#define QSERDES_COM_RESTRIM_CTRL 0x0bc
#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4
#define QSERDES_COM_LOCK_CMP_EN 0x0c8
#define QSERDES_COM_LOCK_CMP_CFG 0x0cc
#define QSERDES_COM_DEC_START_MODE0 0x0d0
#define QSERDES_COM_DEC_START_MODE1 0x0d4
#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc
#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0
#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4
#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8
#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec
#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114
#define QSERDES_COM_VCO_TUNE_CTRL 0x124
#define QSERDES_COM_VCO_TUNE_MAP 0x128
#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c
#define QSERDES_COM_VCO_TUNE2_MODE0 0x130
#define QSERDES_COM_VCO_TUNE1_MODE1 0x134
#define QSERDES_COM_VCO_TUNE2_MODE1 0x138
#define QSERDES_COM_VCO_TUNE_TIMER1 0x144
#define QSERDES_COM_VCO_TUNE_TIMER2 0x148
#define QSERDES_COM_BG_CTRL 0x170
#define QSERDES_COM_CLK_SELECT 0x174
#define QSERDES_COM_HSCLK_SEL 0x178
#define QSERDES_COM_CORECLK_DIV 0x184
#define QSERDES_COM_CORE_CLK_EN 0x18c
#define QSERDES_COM_C_READY_STATUS 0x190
#define QSERDES_COM_CMN_CONFIG 0x194
#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c
#define QSERDES_COM_DEBUG_BUS0 0x1a0
#define QSERDES_COM_DEBUG_BUS1 0x1a4
#define QSERDES_COM_DEBUG_BUS2 0x1a8
#define QSERDES_COM_DEBUG_BUS3 0x1ac
#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0
#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc
/* Only for QMP V2 PHY - TX registers */
#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054
#define QSERDES_TX_DEBUG_BUS_SEL 0x064
#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068
#define QSERDES_TX_LANE_MODE 0x094
#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac
/* Only for QMP V2 PHY - RX registers */
#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010
#define QSERDES_RX_UCDR_SO_GAIN 0x01c
#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040
#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048
#define QSERDES_RX_RX_TERM_BW 0x090
#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4
#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8
#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc
#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0
#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108
#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c
#define QSERDES_RX_SIGDET_ENABLES 0x110
#define QSERDES_RX_SIGDET_CNTRL 0x114
#define QSERDES_RX_SIGDET_LVL 0x118
#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c
#define QSERDES_RX_RX_BAND 0x120
#define QSERDES_RX_RX_INTERFACE_MODE 0x12c
/* Only for QMP V2 PHY - PCS registers */
#define QPHY_POWER_DOWN_CONTROL 0x04
#define QPHY_TXDEEMPH_M6DB_V0 0x24
#define QPHY_TXDEEMPH_M3P5DB_V0 0x28
#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54
#define QPHY_RX_IDLE_DTCT_CNTRL 0x58
#define QPHY_POWER_STATE_CONFIG1 0x60
#define QPHY_POWER_STATE_CONFIG2 0x64
#define QPHY_POWER_STATE_CONFIG4 0x6c
#define QPHY_LOCK_DETECT_CONFIG1 0x80
#define QPHY_LOCK_DETECT_CONFIG2 0x84
#define QPHY_LOCK_DETECT_CONFIG3 0x88
#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0
#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4
#define QPHY_PLL_LOCK_CHK_DLY_TIME_AUXCLK_LSB 0x1A8
#define QPHY_OSC_DTCT_ACTIONS 0x1AC
#define QPHY_RX_SIGDET_LVL 0x1D8
#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1DC
#define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1E0
/* Only for QMP V3 PHY - DP COM registers */
#define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00
#define QPHY_V3_DP_COM_SW_RESET 0x04
#define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08
#define QPHY_V3_DP_COM_SWI_CTRL 0x0c
#define QPHY_V3_DP_COM_TYPEC_CTRL 0x10
#define QPHY_V3_DP_COM_TYPEC_PWRDN_CTRL 0x14
#define QPHY_V3_DP_COM_RESET_OVRD_CTRL 0x1c
/* Only for QMP V3 PHY - QSERDES COM registers */
#define QSERDES_V3_COM_BG_TIMER 0x00c
#define QSERDES_V3_COM_SSC_EN_CENTER 0x010
#define QSERDES_V3_COM_SSC_ADJ_PER1 0x014
#define QSERDES_V3_COM_SSC_ADJ_PER2 0x018
#define QSERDES_V3_COM_SSC_PER1 0x01c
#define QSERDES_V3_COM_SSC_PER2 0x020
#define QSERDES_V3_COM_SSC_STEP_SIZE1 0x024
#define QSERDES_V3_COM_SSC_STEP_SIZE2 0x028
#define QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN 0x034
#define QSERDES_V3_COM_CLK_ENABLE1 0x038
#define QSERDES_V3_COM_SYS_CLK_CTRL 0x03c
#define QSERDES_V3_COM_SYSCLK_BUF_ENABLE 0x040
#define QSERDES_V3_COM_PLL_IVCO 0x048
#define QSERDES_V3_COM_LOCK_CMP1_MODE0 0x098
#define QSERDES_V3_COM_LOCK_CMP2_MODE0 0x09c
#define QSERDES_V3_COM_LOCK_CMP3_MODE0 0x0a0
#define QSERDES_V3_COM_LOCK_CMP1_MODE1 0x0a4
#define QSERDES_V3_COM_LOCK_CMP2_MODE1 0x0a8
#define QSERDES_V3_COM_LOCK_CMP3_MODE1 0x0ac
#define QSERDES_V3_COM_CLK_EP_DIV 0x05c
#define QSERDES_V3_COM_CP_CTRL_MODE0 0x060
#define QSERDES_V3_COM_CP_CTRL_MODE1 0x064
#define QSERDES_V3_COM_PLL_RCTRL_MODE0 0x068
#define QSERDES_V3_COM_PLL_RCTRL_MODE1 0x06c
#define QSERDES_V3_COM_PLL_CCTRL_MODE0 0x070
#define QSERDES_V3_COM_PLL_CCTRL_MODE1 0x074
#define QSERDES_V3_COM_SYSCLK_EN_SEL 0x080
#define QSERDES_V3_COM_RESETSM_CNTRL 0x088
#define QSERDES_V3_COM_RESETSM_CNTRL2 0x08c
#define QSERDES_V3_COM_LOCK_CMP_EN 0x090
#define QSERDES_V3_COM_LOCK_CMP_CFG 0x094
#define QSERDES_V3_COM_DEC_START_MODE0 0x0b0
#define QSERDES_V3_COM_DEC_START_MODE1 0x0b4
#define QSERDES_V3_COM_DIV_FRAC_START1_MODE0 0x0b8
#define QSERDES_V3_COM_DIV_FRAC_START2_MODE0 0x0bc
#define QSERDES_V3_COM_DIV_FRAC_START3_MODE0 0x0c0
#define QSERDES_V3_COM_DIV_FRAC_START1_MODE1 0x0c4
#define QSERDES_V3_COM_DIV_FRAC_START2_MODE1 0x0c8
#define QSERDES_V3_COM_DIV_FRAC_START3_MODE1 0x0cc
#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0 0x0d8
#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0 0x0dc
#define QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1 0x0e0
#define QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1 0x0e4
#define QSERDES_V3_COM_VCO_TUNE_CTRL 0x0ec
#define QSERDES_V3_COM_VCO_TUNE_MAP 0x0f0
#define QSERDES_V3_COM_VCO_TUNE1_MODE0 0x0f4
#define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8
#define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc
#define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100
#define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c
#define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120
#define QSERDES_V3_COM_CLK_SELECT 0x138
#define QSERDES_V3_COM_HSCLK_SEL 0x13c
#define QSERDES_V3_COM_CORECLK_DIV_MODE0 0x148
#define QSERDES_V3_COM_CORECLK_DIV_MODE1 0x14c
#define QSERDES_V3_COM_CORE_CLK_EN 0x154
#define QSERDES_V3_COM_C_READY_STATUS 0x158
#define QSERDES_V3_COM_CMN_CONFIG 0x15c
#define QSERDES_V3_COM_SVS_MODE_CLK_SEL 0x164
#define QSERDES_V3_COM_DEBUG_BUS0 0x168
#define QSERDES_V3_COM_DEBUG_BUS1 0x16c
#define QSERDES_V3_COM_DEBUG_BUS2 0x170
#define QSERDES_V3_COM_DEBUG_BUS3 0x174
#define QSERDES_V3_COM_DEBUG_BUS_SEL 0x178
/* Only for QMP V3 PHY - TX registers */
#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX 0x044
#define QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX 0x048
#define QSERDES_V3_TX_DEBUG_BUS_SEL 0x058
#define QSERDES_V3_TX_HIGHZ_DRVR_EN 0x060
#define QSERDES_V3_TX_LANE_MODE_1 0x08c
#define QSERDES_V3_TX_RCV_DETECT_LVL_2 0x0a4
/* Only for QMP V3 PHY - RX registers */
#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c
#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014
#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030
#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
#define QSERDES_V3_RX_RX_TERM_BW 0x07c
#define QSERDES_V3_RX_RX_EQ_GAIN2_LSB 0x0c8
#define QSERDES_V3_RX_RX_EQ_GAIN2_MSB 0x0cc
#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d4
#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3 0x0d8
#define QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4 0x0dc
#define QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x0f8
#define QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x0fc
#define QSERDES_V3_RX_SIGDET_ENABLES 0x100
#define QSERDES_V3_RX_SIGDET_CNTRL 0x104
#define QSERDES_V3_RX_SIGDET_LVL 0x108
#define QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL 0x10c
#define QSERDES_V3_RX_RX_BAND 0x110
#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c
/* Only for QMP V3 PHY - PCS registers */
#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004
#define QPHY_V3_PCS_TXMGN_V0 0x00c
#define QPHY_V3_PCS_TXMGN_V1 0x010
#define QPHY_V3_PCS_TXMGN_V2 0x014
#define QPHY_V3_PCS_TXMGN_V3 0x018
#define QPHY_V3_PCS_TXMGN_V4 0x01c
#define QPHY_V3_PCS_TXMGN_LS 0x020
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0 0x024
#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0 0x028
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1 0x02c
#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1 0x030
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V2 0x034
#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2 0x038
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V3 0x03c
#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3 0x040
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V4 0x044
#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4 0x048
#define QPHY_V3_PCS_TXDEEMPH_M6DB_LS 0x04c
#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS 0x050
#define QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE 0x054
#define QPHY_V3_PCS_RX_IDLE_DTCT_CNTRL 0x058
#define QPHY_V3_PCS_RATE_SLEW_CNTRL 0x05c
#define QPHY_V3_PCS_POWER_STATE_CONFIG1 0x060
#define QPHY_V3_PCS_POWER_STATE_CONFIG2 0x064
#define QPHY_V3_PCS_POWER_STATE_CONFIG4 0x06c
#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L 0x070
#define QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H 0x074
#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L 0x078
#define QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H 0x07c
#define QPHY_V3_PCS_LOCK_DETECT_CONFIG1 0x080
#define QPHY_V3_PCS_LOCK_DETECT_CONFIG2 0x084
#define QPHY_V3_PCS_LOCK_DETECT_CONFIG3 0x088
#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c
#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4
#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK 0x0b0
#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8
#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc
#define QPHY_V3_PCS_FLL_CNTRL1 0x0c4
#define QPHY_V3_PCS_FLL_CNTRL2 0x0c8
#define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc
#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0
#define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4
#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
/* Only for QMP V3 PHY - PCS_MISC registers */
#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c
#endif

View File

@ -1,14 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/* /*
* Copyright (c) 2017, The Linux Foundation. All rights reserved. * Copyright (c) 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/ */
#include <linux/clk.h> #include <linux/clk.h>
@ -37,28 +29,57 @@
#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c #define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c
#define QUSB2PHY_PLL_PWR_CTRL 0x18 #define QUSB2PHY_PLL_PWR_CTRL 0x18
#define QUSB2PHY_PLL_STATUS 0x38 /* QUSB2PHY_PLL_STATUS register bits */
#define PLL_LOCKED BIT(5) #define PLL_LOCKED BIT(5)
#define QUSB2PHY_PORT_TUNE1 0x80 /* QUSB2PHY_PLL_COMMON_STATUS_ONE register bits */
#define QUSB2PHY_PORT_TUNE2 0x84 #define CORE_READY_STATUS BIT(0)
#define QUSB2PHY_PORT_TUNE3 0x88
#define QUSB2PHY_PORT_TUNE4 0x8c
#define QUSB2PHY_PORT_TUNE5 0x90
#define QUSB2PHY_PORT_TEST2 0x9c
#define QUSB2PHY_PORT_POWERDOWN 0xb4 /* QUSB2PHY_PORT_POWERDOWN register bits */
#define CLAMP_N_EN BIT(5) #define CLAMP_N_EN BIT(5)
#define FREEZIO_N BIT(1) #define FREEZIO_N BIT(1)
#define POWER_DOWN BIT(0) #define POWER_DOWN BIT(0)
/* QUSB2PHY_PWR_CTRL1 register bits */
#define PWR_CTRL1_VREF_SUPPLY_TRIM BIT(5)
#define PWR_CTRL1_CLAMP_N_EN BIT(1)
#define QUSB2PHY_REFCLK_ENABLE BIT(0) #define QUSB2PHY_REFCLK_ENABLE BIT(0)
#define PHY_CLK_SCHEME_SEL BIT(0) #define PHY_CLK_SCHEME_SEL BIT(0)
/* QUSB2PHY_INTR_CTRL register bits */
#define DMSE_INTR_HIGH_SEL BIT(4)
#define DPSE_INTR_HIGH_SEL BIT(3)
#define CHG_DET_INTR_EN BIT(2)
#define DMSE_INTR_EN BIT(1)
#define DPSE_INTR_EN BIT(0)
/* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE register bits */
#define CORE_PLL_EN_FROM_RESET BIT(4)
#define CORE_RESET BIT(5)
#define CORE_RESET_MUX BIT(6)
#define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO 0x04
#define QUSB2PHY_PLL_CLOCK_INVERTERS 0x18c
#define QUSB2PHY_PLL_CMODE 0x2c
#define QUSB2PHY_PLL_LOCK_DELAY 0x184
#define QUSB2PHY_PLL_DIGITAL_TIMERS_TWO 0xb4
#define QUSB2PHY_PLL_BIAS_CONTROL_1 0x194
#define QUSB2PHY_PLL_BIAS_CONTROL_2 0x198
#define QUSB2PHY_PWR_CTRL2 0x214
#define QUSB2PHY_IMP_CTRL1 0x220
#define QUSB2PHY_IMP_CTRL2 0x224
#define QUSB2PHY_CHG_CTRL2 0x23c
struct qusb2_phy_init_tbl { struct qusb2_phy_init_tbl {
unsigned int offset; unsigned int offset;
unsigned int val; unsigned int val;
/*
* register part of layout ?
* if yes, then offset gives index in the reg-layout
*/
int in_layout;
}; };
#define QUSB2_PHY_INIT_CFG(o, v) \ #define QUSB2_PHY_INIT_CFG(o, v) \
@ -67,30 +88,136 @@ struct qusb2_phy_init_tbl {
.val = v, \ .val = v, \
} }
#define QUSB2_PHY_INIT_CFG_L(o, v) \
{ \
.offset = o, \
.val = v, \
.in_layout = 1, \
}
/* set of registers with offsets different per-PHY */
enum qusb2phy_reg_layout {
QUSB2PHY_PLL_CORE_INPUT_OVERRIDE,
QUSB2PHY_PLL_STATUS,
QUSB2PHY_PORT_TUNE1,
QUSB2PHY_PORT_TUNE2,
QUSB2PHY_PORT_TUNE3,
QUSB2PHY_PORT_TUNE4,
QUSB2PHY_PORT_TUNE5,
QUSB2PHY_PORT_TEST1,
QUSB2PHY_PORT_TEST2,
QUSB2PHY_PORT_POWERDOWN,
QUSB2PHY_INTR_CTRL,
};
static const unsigned int msm8996_regs_layout[] = {
[QUSB2PHY_PLL_STATUS] = 0x38,
[QUSB2PHY_PORT_TUNE1] = 0x80,
[QUSB2PHY_PORT_TUNE2] = 0x84,
[QUSB2PHY_PORT_TUNE3] = 0x88,
[QUSB2PHY_PORT_TUNE4] = 0x8c,
[QUSB2PHY_PORT_TUNE5] = 0x90,
[QUSB2PHY_PORT_TEST1] = 0xb8,
[QUSB2PHY_PORT_TEST2] = 0x9c,
[QUSB2PHY_PORT_POWERDOWN] = 0xb4,
[QUSB2PHY_INTR_CTRL] = 0xbc,
};
static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8), QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0xf8),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3), QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0xb3),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83), QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0x83),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0), QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0xc0),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TEST2, 0x14),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00),
}; };
static const unsigned int qusb2_v2_regs_layout[] = {
[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
[QUSB2PHY_PLL_STATUS] = 0x1a0,
[QUSB2PHY_PORT_TUNE1] = 0x240,
[QUSB2PHY_PORT_TUNE2] = 0x244,
[QUSB2PHY_PORT_TUNE3] = 0x248,
[QUSB2PHY_PORT_TUNE4] = 0x24c,
[QUSB2PHY_PORT_TUNE5] = 0x250,
[QUSB2PHY_PORT_TEST1] = 0x254,
[QUSB2PHY_PORT_TEST2] = 0x258,
[QUSB2PHY_PORT_POWERDOWN] = 0x210,
[QUSB2PHY_INTR_CTRL] = 0x230,
};
static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = {
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_LOCK_DELAY, 0x0a),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_1, 0x40),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_BIAS_CONTROL_2, 0x20),
QUSB2_PHY_INIT_CFG(QUSB2PHY_PWR_CTRL2, 0x21),
QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL1, 0x0),
QUSB2_PHY_INIT_CFG(QUSB2PHY_IMP_CTRL2, 0x58),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE1, 0x30),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE2, 0x29),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE3, 0xca),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE4, 0x04),
QUSB2_PHY_INIT_CFG_L(QUSB2PHY_PORT_TUNE5, 0x03),
QUSB2_PHY_INIT_CFG(QUSB2PHY_CHG_CTRL2, 0x0),
};
struct qusb2_phy_cfg { struct qusb2_phy_cfg {
const struct qusb2_phy_init_tbl *tbl; const struct qusb2_phy_init_tbl *tbl;
/* number of entries in the table */ /* number of entries in the table */
unsigned int tbl_num; unsigned int tbl_num;
/* offset to PHY_CLK_SCHEME register in TCSR map */ /* offset to PHY_CLK_SCHEME register in TCSR map */
unsigned int clk_scheme_offset; unsigned int clk_scheme_offset;
/* array of registers with different offsets */
const unsigned int *regs;
unsigned int mask_core_ready;
unsigned int disable_ctrl;
unsigned int autoresume_en;
/* true if PHY has PLL_TEST register to select clk_scheme */
bool has_pll_test;
/* true if TUNE1 register must be updated by fused value, else TUNE2 */
bool update_tune1_with_efuse;
/* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */
bool has_pll_override;
}; };
static const struct qusb2_phy_cfg msm8996_phy_cfg = { static const struct qusb2_phy_cfg msm8996_phy_cfg = {
.tbl = msm8996_init_tbl, .tbl = msm8996_init_tbl,
.tbl_num = ARRAY_SIZE(msm8996_init_tbl), .tbl_num = ARRAY_SIZE(msm8996_init_tbl),
.regs = msm8996_regs_layout,
.has_pll_test = true,
.disable_ctrl = (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
.mask_core_ready = PLL_LOCKED,
.autoresume_en = BIT(3),
};
static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = {
.tbl = qusb2_v2_init_tbl,
.tbl_num = ARRAY_SIZE(qusb2_v2_init_tbl),
.regs = qusb2_v2_regs_layout,
.disable_ctrl = (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN |
POWER_DOWN),
.mask_core_ready = CORE_READY_STATUS,
.has_pll_override = true,
.autoresume_en = BIT(0),
}; };
static const char * const qusb2_phy_vreg_names[] = { static const char * const qusb2_phy_vreg_names[] = {
@ -116,6 +243,8 @@ static const char * const qusb2_phy_vreg_names[] = {
* *
* @cfg: phy config data * @cfg: phy config data
* @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
* @phy_initialized: indicate if PHY has been initialized
* @mode: current PHY mode
*/ */
struct qusb2_phy { struct qusb2_phy {
struct phy *phy; struct phy *phy;
@ -132,6 +261,8 @@ struct qusb2_phy {
const struct qusb2_phy_cfg *cfg; const struct qusb2_phy_cfg *cfg;
bool has_se_clk_scheme; bool has_se_clk_scheme;
bool phy_initialized;
enum phy_mode mode;
}; };
static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val) static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val)
@ -160,26 +291,32 @@ static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val)
static inline static inline
void qcom_qusb2_phy_configure(void __iomem *base, void qcom_qusb2_phy_configure(void __iomem *base,
const unsigned int *regs,
const struct qusb2_phy_init_tbl tbl[], int num) const struct qusb2_phy_init_tbl tbl[], int num)
{ {
int i; int i;
for (i = 0; i < num; i++) for (i = 0; i < num; i++) {
writel(tbl[i].val, base + tbl[i].offset); if (tbl[i].in_layout)
writel(tbl[i].val, base + regs[tbl[i].offset]);
else
writel(tbl[i].val, base + tbl[i].offset);
}
} }
/* /*
* Fetches HS Tx tuning value from nvmem and sets the * Fetches HS Tx tuning value from nvmem and sets the
* QUSB2PHY_PORT_TUNE2 register. * QUSB2PHY_PORT_TUNE1/2 register.
* For error case, skip setting the value and use the default value. * For error case, skip setting the value and use the default value.
*/ */
static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
{ {
struct device *dev = &qphy->phy->dev; struct device *dev = &qphy->phy->dev;
const struct qusb2_phy_cfg *cfg = qphy->cfg;
u8 *val; u8 *val;
/* /*
* Read efuse register having TUNE2 parameter's high nibble. * Read efuse register having TUNE2/1 parameter's high nibble.
* If efuse register shows value as 0x0, or if we fail to find * If efuse register shows value as 0x0, or if we fail to find
* a valid efuse register settings, then use default value * a valid efuse register settings, then use default value
* as 0xB for high nibble that we have already set while * as 0xB for high nibble that we have already set while
@ -191,58 +328,169 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
return; return;
} }
/* Fused TUNE2 value is the higher nibble only */ /* Fused TUNE1/2 value is the higher nibble only */
qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4); if (cfg->update_tune1_with_efuse)
qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
val[0] << 0x4);
else
qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2],
val[0] << 0x4);
} }
static int qusb2_phy_poweron(struct phy *phy) static int qusb2_phy_set_mode(struct phy *phy, enum phy_mode mode)
{ {
struct qusb2_phy *qphy = phy_get_drvdata(phy); struct qusb2_phy *qphy = phy_get_drvdata(phy);
int num = ARRAY_SIZE(qphy->vregs);
qphy->mode = mode;
return 0;
}
static int __maybe_unused qusb2_phy_runtime_suspend(struct device *dev)
{
struct qusb2_phy *qphy = dev_get_drvdata(dev);
const struct qusb2_phy_cfg *cfg = qphy->cfg;
u32 intr_mask;
dev_vdbg(dev, "Suspending QUSB2 Phy, mode:%d\n", qphy->mode);
if (!qphy->phy_initialized) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
/*
* Enable DP/DM interrupts to detect line state changes based on current
* speed. In other words, enable the triggers _opposite_ of what the
* current D+/D- levels are e.g. if currently D+ high, D- low
* (HS 'J'/Suspend), configure the mask to trigger on D+ low OR D- high
*/
intr_mask = DPSE_INTR_EN | DMSE_INTR_EN;
switch (qphy->mode) {
case PHY_MODE_USB_HOST_HS:
case PHY_MODE_USB_HOST_FS:
case PHY_MODE_USB_DEVICE_HS:
case PHY_MODE_USB_DEVICE_FS:
intr_mask |= DMSE_INTR_HIGH_SEL;
break;
case PHY_MODE_USB_HOST_LS:
case PHY_MODE_USB_DEVICE_LS:
intr_mask |= DPSE_INTR_HIGH_SEL;
break;
default:
/* No device connected, enable both DP/DM high interrupt */
intr_mask |= DMSE_INTR_HIGH_SEL;
intr_mask |= DPSE_INTR_HIGH_SEL;
break;
}
writel(intr_mask, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
/* hold core PLL into reset */
if (cfg->has_pll_override) {
qusb2_setbits(qphy->base,
cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
CORE_PLL_EN_FROM_RESET | CORE_RESET |
CORE_RESET_MUX);
}
/* enable phy auto-resume only if device is connected on bus */
if (qphy->mode != PHY_MODE_INVALID) {
qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
cfg->autoresume_en);
/* Autoresume bit has to be toggled in order to enable it */
qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
cfg->autoresume_en);
}
if (!qphy->has_se_clk_scheme)
clk_disable_unprepare(qphy->ref_clk);
clk_disable_unprepare(qphy->cfg_ahb_clk);
clk_disable_unprepare(qphy->iface_clk);
return 0;
}
static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev)
{
struct qusb2_phy *qphy = dev_get_drvdata(dev);
const struct qusb2_phy_cfg *cfg = qphy->cfg;
int ret; int ret;
dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__); dev_vdbg(dev, "Resuming QUSB2 phy, mode:%d\n", qphy->mode);
if (!qphy->phy_initialized) {
dev_vdbg(dev, "PHY not initialized, bailing out\n");
return 0;
}
ret = clk_prepare_enable(qphy->iface_clk);
if (ret) {
dev_err(dev, "failed to enable iface_clk, %d\n", ret);
return ret;
}
ret = clk_prepare_enable(qphy->cfg_ahb_clk);
if (ret) {
dev_err(dev, "failed to enable cfg ahb clock, %d\n", ret);
goto disable_iface_clk;
}
if (!qphy->has_se_clk_scheme) {
clk_prepare_enable(qphy->ref_clk);
if (ret) {
dev_err(dev, "failed to enable ref clk, %d\n", ret);
goto disable_ahb_clk;
}
}
writel(0x0, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
/* bring core PLL out of reset */
if (cfg->has_pll_override) {
qusb2_clrbits(qphy->base,
cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
CORE_RESET | CORE_RESET_MUX);
}
return 0;
disable_ahb_clk:
clk_disable_unprepare(qphy->cfg_ahb_clk);
disable_iface_clk:
clk_disable_unprepare(qphy->iface_clk);
return ret;
}
static int qusb2_phy_init(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
const struct qusb2_phy_cfg *cfg = qphy->cfg;
unsigned int val = 0;
unsigned int clk_scheme;
int ret;
dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__);
/* turn on regulator supplies */ /* turn on regulator supplies */
ret = regulator_bulk_enable(num, qphy->vregs); ret = regulator_bulk_enable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
if (ret) if (ret)
return ret; return ret;
ret = clk_prepare_enable(qphy->iface_clk); ret = clk_prepare_enable(qphy->iface_clk);
if (ret) { if (ret) {
dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret); dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
regulator_bulk_disable(num, qphy->vregs); goto poweroff_phy;
return ret;
} }
return 0;
}
static int qusb2_phy_poweroff(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
clk_disable_unprepare(qphy->iface_clk);
regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
return 0;
}
static int qusb2_phy_init(struct phy *phy)
{
struct qusb2_phy *qphy = phy_get_drvdata(phy);
unsigned int val;
unsigned int clk_scheme;
int ret;
dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__);
/* enable ahb interface clock to program phy */ /* enable ahb interface clock to program phy */
ret = clk_prepare_enable(qphy->cfg_ahb_clk); ret = clk_prepare_enable(qphy->cfg_ahb_clk);
if (ret) { if (ret) {
dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
return ret; goto disable_iface_clk;
} }
/* Perform phy reset */ /* Perform phy reset */
@ -262,20 +510,23 @@ static int qusb2_phy_init(struct phy *phy)
} }
/* Disable the PHY */ /* Disable the PHY */
qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN],
CLAMP_N_EN | FREEZIO_N | POWER_DOWN); qphy->cfg->disable_ctrl);
/* save reset value to override reference clock scheme later */ if (cfg->has_pll_test) {
val = readl(qphy->base + QUSB2PHY_PLL_TEST); /* save reset value to override reference clock scheme later */
val = readl(qphy->base + QUSB2PHY_PLL_TEST);
}
qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl, qcom_qusb2_phy_configure(qphy->base, cfg->regs, cfg->tbl,
qphy->cfg->tbl_num); cfg->tbl_num);
/* Set efuse value for tuning the PHY */ /* Set efuse value for tuning the PHY */
qusb2_phy_set_tune2_param(qphy); qusb2_phy_set_tune2_param(qphy);
/* Enable the PHY */ /* Enable the PHY */
qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN); qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_POWERDOWN],
POWER_DOWN);
/* Required to get phy pll lock successfully */ /* Required to get phy pll lock successfully */
usleep_range(150, 160); usleep_range(150, 160);
@ -308,32 +559,37 @@ static int qusb2_phy_init(struct phy *phy)
} }
if (!qphy->has_se_clk_scheme) { if (!qphy->has_se_clk_scheme) {
val &= ~CLK_REF_SEL;
ret = clk_prepare_enable(qphy->ref_clk); ret = clk_prepare_enable(qphy->ref_clk);
if (ret) { if (ret) {
dev_err(&phy->dev, "failed to enable ref clk, %d\n", dev_err(&phy->dev, "failed to enable ref clk, %d\n",
ret); ret);
goto assert_phy_reset; goto assert_phy_reset;
} }
} else {
val |= CLK_REF_SEL;
} }
writel(val, qphy->base + QUSB2PHY_PLL_TEST); if (cfg->has_pll_test) {
if (!qphy->has_se_clk_scheme)
val &= ~CLK_REF_SEL;
else
val |= CLK_REF_SEL;
/* ensure above write is through */ writel(val, qphy->base + QUSB2PHY_PLL_TEST);
readl(qphy->base + QUSB2PHY_PLL_TEST);
/* ensure above write is through */
readl(qphy->base + QUSB2PHY_PLL_TEST);
}
/* Required to get phy pll lock successfully */ /* Required to get phy pll lock successfully */
usleep_range(100, 110); usleep_range(100, 110);
val = readb(qphy->base + QUSB2PHY_PLL_STATUS); val = readb(qphy->base + cfg->regs[QUSB2PHY_PLL_STATUS]);
if (!(val & PLL_LOCKED)) { if (!(val & cfg->mask_core_ready)) {
dev_err(&phy->dev, dev_err(&phy->dev,
"QUSB2PHY pll lock failed: status reg = %x\n", val); "QUSB2PHY pll lock failed: status reg = %x\n", val);
ret = -EBUSY; ret = -EBUSY;
goto disable_ref_clk; goto disable_ref_clk;
} }
qphy->phy_initialized = true;
return 0; return 0;
@ -344,6 +600,11 @@ assert_phy_reset:
reset_control_assert(qphy->phy_reset); reset_control_assert(qphy->phy_reset);
disable_ahb_clk: disable_ahb_clk:
clk_disable_unprepare(qphy->cfg_ahb_clk); clk_disable_unprepare(qphy->cfg_ahb_clk);
disable_iface_clk:
clk_disable_unprepare(qphy->iface_clk);
poweroff_phy:
regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
return ret; return ret;
} }
@ -352,8 +613,8 @@ static int qusb2_phy_exit(struct phy *phy)
struct qusb2_phy *qphy = phy_get_drvdata(phy); struct qusb2_phy *qphy = phy_get_drvdata(phy);
/* Disable the PHY */ /* Disable the PHY */
qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, qusb2_setbits(qphy->base, qphy->cfg->regs[QUSB2PHY_PORT_POWERDOWN],
CLAMP_N_EN | FREEZIO_N | POWER_DOWN); qphy->cfg->disable_ctrl);
if (!qphy->has_se_clk_scheme) if (!qphy->has_se_clk_scheme)
clk_disable_unprepare(qphy->ref_clk); clk_disable_unprepare(qphy->ref_clk);
@ -361,6 +622,11 @@ static int qusb2_phy_exit(struct phy *phy)
reset_control_assert(qphy->phy_reset); reset_control_assert(qphy->phy_reset);
clk_disable_unprepare(qphy->cfg_ahb_clk); clk_disable_unprepare(qphy->cfg_ahb_clk);
clk_disable_unprepare(qphy->iface_clk);
regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
qphy->phy_initialized = false;
return 0; return 0;
} }
@ -368,8 +634,7 @@ static int qusb2_phy_exit(struct phy *phy)
static const struct phy_ops qusb2_phy_gen_ops = { static const struct phy_ops qusb2_phy_gen_ops = {
.init = qusb2_phy_init, .init = qusb2_phy_init,
.exit = qusb2_phy_exit, .exit = qusb2_phy_exit,
.power_on = qusb2_phy_poweron, .set_mode = qusb2_phy_set_mode,
.power_off = qusb2_phy_poweroff,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
@ -377,11 +642,19 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
{ {
.compatible = "qcom,msm8996-qusb2-phy", .compatible = "qcom,msm8996-qusb2-phy",
.data = &msm8996_phy_cfg, .data = &msm8996_phy_cfg,
}, {
.compatible = "qcom,qusb2-v2-phy",
.data = &qusb2_v2_phy_cfg,
}, },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table); MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
static const struct dev_pm_ops qusb2_phy_pm_ops = {
SET_RUNTIME_PM_OPS(qusb2_phy_runtime_suspend,
qusb2_phy_runtime_resume, NULL)
};
static int qusb2_phy_probe(struct platform_device *pdev) static int qusb2_phy_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -459,11 +732,19 @@ static int qusb2_phy_probe(struct platform_device *pdev)
qphy->cell = NULL; qphy->cell = NULL;
dev_dbg(dev, "failed to lookup tune2 hstx trim value\n"); dev_dbg(dev, "failed to lookup tune2 hstx trim value\n");
} }
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
/*
* Prevent runtime pm from being ON by default. Users can enable
* it using power/control in sysfs.
*/
pm_runtime_forbid(dev);
generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops); generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_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 phy, %d\n", ret); dev_err(dev, "failed to create phy, %d\n", ret);
pm_runtime_disable(dev);
return ret; return ret;
} }
qphy->phy = generic_phy; qphy->phy = generic_phy;
@ -474,6 +755,8 @@ static int qusb2_phy_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (!IS_ERR(phy_provider)) if (!IS_ERR(phy_provider))
dev_info(dev, "Registered Qcom-QUSB2 phy\n"); dev_info(dev, "Registered Qcom-QUSB2 phy\n");
else
pm_runtime_disable(dev);
return PTR_ERR_OR_ZERO(phy_provider); return PTR_ERR_OR_ZERO(phy_provider);
} }
@ -482,6 +765,7 @@ static struct platform_driver qusb2_phy_driver = {
.probe = qusb2_phy_probe, .probe = qusb2_phy_probe,
.driver = { .driver = {
.name = "qcom-qusb2-phy", .name = "qcom-qusb2-phy",
.pm = &qusb2_phy_pm_ops,
.of_match_table = qusb2_phy_of_match_table, .of_match_table = qusb2_phy_of_match_table,
}, },
}; };

View File

@ -4,6 +4,7 @@
config PHY_RALINK_USB config PHY_RALINK_USB
tristate "Ralink USB PHY driver" tristate "Ralink USB PHY driver"
depends on RALINK || COMPILE_TEST depends on RALINK || COMPILE_TEST
depends on HAS_IOMEM
select GENERIC_PHY select GENERIC_PHY
select MFD_SYSCON select MFD_SYSCON
help help

View File

@ -396,6 +396,10 @@ static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
.compatible = "renesas,usb2-phy-r8a7796", .compatible = "renesas,usb2-phy-r8a7796",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS, .data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
}, },
{
.compatible = "renesas,usb2-phy-r8a77965",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{ {
.compatible = "renesas,rcar-gen3-usb2-phy", .compatible = "renesas,rcar-gen3-usb2-phy",
}, },

View File

@ -29,6 +29,7 @@ config PHY_ROCKCHIP_INNO_USB2
config PHY_ROCKCHIP_PCIE config PHY_ROCKCHIP_PCIE
tristate "Rockchip PCIe PHY Driver" tristate "Rockchip PCIe PHY Driver"
depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
depends on HAS_IOMEM
select GENERIC_PHY select GENERIC_PHY
select MFD_SYSCON select MFD_SYSCON
help help

View File

@ -76,6 +76,13 @@
#define PHYCTRL_OTAPDLYSEL_MASK 0xf #define PHYCTRL_OTAPDLYSEL_MASK 0xf
#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 #define PHYCTRL_OTAPDLYSEL_SHIFT 0x7
#define PHYCTRL_IS_CALDONE(x) \
((((x) >> PHYCTRL_CALDONE_SHIFT) & \
PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE)
#define PHYCTRL_IS_DLLRDY(x) \
((((x) >> PHYCTRL_DLLRDY_SHIFT) & \
PHYCTRL_DLLRDY_MASK) == PHYCTRL_DLLRDY_DONE)
struct rockchip_emmc_phy { struct rockchip_emmc_phy {
unsigned int reg_offset; unsigned int reg_offset;
struct regmap *reg_base; struct regmap *reg_base;
@ -89,7 +96,7 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
unsigned int dllrdy; unsigned int dllrdy;
unsigned int freqsel = PHYCTRL_FREQSEL_200M; unsigned int freqsel = PHYCTRL_FREQSEL_200M;
unsigned long rate; unsigned long rate;
unsigned long timeout; int ret;
/* /*
* Keep phyctrl_pdb and phyctrl_endll low to allow * Keep phyctrl_pdb and phyctrl_endll low to allow
@ -160,17 +167,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
PHYCTRL_PDB_SHIFT)); PHYCTRL_PDB_SHIFT));
/* /*
* According to the user manual, it asks driver to * According to the user manual, it asks driver to wait 5us for
* wait 5us for calpad busy trimming * calpad busy trimming. However it is documented that this value is
* PVT(A.K.A process,voltage and temperature) relevant, so some
* failure cases are found which indicates we should be more tolerant
* to calpad busy trimming.
*/ */
udelay(5); ret = regmap_read_poll_timeout(rk_phy->reg_base,
regmap_read(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
rk_phy->reg_offset + GRF_EMMCPHY_STATUS, caldone, PHYCTRL_IS_CALDONE(caldone),
&caldone); 0, 50);
caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; if (ret) {
if (caldone != PHYCTRL_CALDONE_DONE) { pr_err("%s: caldone failed, ret=%d\n", __func__, ret);
pr_err("rockchip_emmc_phy_power: caldone timeout.\n"); return ret;
return -ETIMEDOUT;
} }
/* Set the frequency of the DLL operation */ /* Set the frequency of the DLL operation */
@ -210,28 +219,15 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
* NOTE: There appear to be corner cases where the DLL seems to take * NOTE: There appear to be corner cases where the DLL seems to take
* extra long to lock for reasons that aren't understood. In some * extra long to lock for reasons that aren't understood. In some
* extreme cases we've seen it take up to over 10ms (!). We'll be * extreme cases we've seen it take up to over 10ms (!). We'll be
* generous and give it 50ms. We still busy wait here because: * generous and give it 50ms.
* - In most cases it should be super fast.
* - This is not called lots during normal operation so it shouldn't
* be a power or performance problem to busy wait. We expect it
* only at boot / resume. In both cases, eMMC is probably on the
* critical path so busy waiting a little extra time should be OK.
*/ */
timeout = jiffies + msecs_to_jiffies(50); ret = regmap_read_poll_timeout(rk_phy->reg_base,
do { rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
udelay(1); dllrdy, PHYCTRL_IS_DLLRDY(dllrdy),
0, 50 * USEC_PER_MSEC);
regmap_read(rk_phy->reg_base, if (ret) {
rk_phy->reg_offset + GRF_EMMCPHY_STATUS, pr_err("%s: dllrdy failed. ret=%d\n", __func__, ret);
&dllrdy); return ret;
dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
if (dllrdy == PHYCTRL_DLLRDY_DONE)
break;
} while (!time_after(jiffies, timeout));
if (dllrdy != PHYCTRL_DLLRDY_DONE) {
pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
return -ETIMEDOUT;
} }
return 0; return 0;

View File

@ -355,11 +355,26 @@ struct usb3phy_reg {
u32 write_enable; u32 write_enable;
}; };
/**
* struct rockchip_usb3phy_port_cfg: usb3-phy port configuration.
* @reg: the base address for usb3-phy config.
* @typec_conn_dir: the register of type-c connector direction.
* @usb3tousb2_en: the register of type-c force usb2 to usb2 enable.
* @external_psm: the register of type-c phy external psm clock.
* @pipe_status: the register of type-c phy pipe status.
* @usb3_host_disable: the register of type-c usb3 host disable.
* @usb3_host_port: the register of type-c usb3 host port.
* @uphy_dp_sel: the register of type-c phy DP select control.
*/
struct rockchip_usb3phy_port_cfg { struct rockchip_usb3phy_port_cfg {
unsigned int reg;
struct usb3phy_reg typec_conn_dir; struct usb3phy_reg typec_conn_dir;
struct usb3phy_reg usb3tousb2_en; struct usb3phy_reg usb3tousb2_en;
struct usb3phy_reg external_psm; struct usb3phy_reg external_psm;
struct usb3phy_reg pipe_status; struct usb3phy_reg pipe_status;
struct usb3phy_reg usb3_host_disable;
struct usb3phy_reg usb3_host_port;
struct usb3phy_reg uphy_dp_sel;
}; };
struct rockchip_typec_phy { struct rockchip_typec_phy {
@ -372,7 +387,7 @@ struct rockchip_typec_phy {
struct reset_control *uphy_rst; struct reset_control *uphy_rst;
struct reset_control *pipe_rst; struct reset_control *pipe_rst;
struct reset_control *tcphy_rst; struct reset_control *tcphy_rst;
struct rockchip_usb3phy_port_cfg port_cfgs; const struct rockchip_usb3phy_port_cfg *port_cfgs;
/* mutex to protect access to individual PHYs */ /* mutex to protect access to individual PHYs */
struct mutex lock; struct mutex lock;
@ -424,6 +439,30 @@ struct phy_reg dp_pll_cfg[] = {
{ 0x4, CMN_DIAG_PLL1_INCLK_CTRL }, { 0x4, CMN_DIAG_PLL1_INCLK_CTRL },
}; };
static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
{
.reg = 0xff7c0000,
.typec_conn_dir = { 0xe580, 0, 16 },
.usb3tousb2_en = { 0xe580, 3, 19 },
.external_psm = { 0xe588, 14, 30 },
.pipe_status = { 0xe5c0, 0, 0 },
.usb3_host_disable = { 0x2434, 0, 16 },
.usb3_host_port = { 0x2434, 12, 28 },
.uphy_dp_sel = { 0x6268, 19, 19 },
},
{
.reg = 0xff800000,
.typec_conn_dir = { 0xe58c, 0, 16 },
.usb3tousb2_en = { 0xe58c, 3, 19 },
.external_psm = { 0xe594, 14, 30 },
.pipe_status = { 0xe5c0, 16, 16 },
.usb3_host_disable = { 0x2444, 0, 16 },
.usb3_host_port = { 0x2444, 12, 28 },
.uphy_dp_sel = { 0x6268, 3, 19 },
},
{ /* sentinel */ }
};
static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy) static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
{ {
u32 i, rdata; u32 i, rdata;
@ -691,7 +730,7 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
{ {
struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
int ret, i; int ret, i;
u32 val; u32 val;
@ -782,6 +821,9 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
u8 mode; u8 mode;
int ret; int ret;
if (!edev)
return MODE_DFP_USB;
ufp = extcon_get_state(edev, EXTCON_USB); ufp = extcon_get_state(edev, EXTCON_USB);
dp = extcon_get_state(edev, EXTCON_DISP_DP); dp = extcon_get_state(edev, EXTCON_DISP_DP);
@ -818,10 +860,22 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
return mode; return mode;
} }
static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
bool value)
{
const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
property_enable(tcphy, &cfg->usb3tousb2_en, value);
property_enable(tcphy, &cfg->usb3_host_disable, value);
property_enable(tcphy, &cfg->usb3_host_port, !value);
return 0;
}
static int rockchip_usb3_phy_power_on(struct phy *phy) static int rockchip_usb3_phy_power_on(struct phy *phy)
{ {
struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
const struct usb3phy_reg *reg = &cfg->pipe_status; const struct usb3phy_reg *reg = &cfg->pipe_status;
int timeout, new_mode, ret = 0; int timeout, new_mode, ret = 0;
u32 val; u32 val;
@ -835,8 +889,10 @@ static int rockchip_usb3_phy_power_on(struct phy *phy)
} }
/* DP-only mode; fall back to USB2 */ /* DP-only mode; fall back to USB2 */
if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) {
tcphy_cfg_usb3_to_usb2_only(tcphy, true);
goto unlock_ret; goto unlock_ret;
}
if (tcphy->mode == new_mode) if (tcphy->mode == new_mode)
goto unlock_ret; goto unlock_ret;
@ -852,6 +908,9 @@ static int rockchip_usb3_phy_power_on(struct phy *phy)
regmap_read(tcphy->grf_regs, reg->offset, &val); regmap_read(tcphy->grf_regs, reg->offset, &val);
if (!(val & BIT(reg->enable_bit))) { if (!(val & BIT(reg->enable_bit))) {
tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB); tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB);
/* enable usb3 host */
tcphy_cfg_usb3_to_usb2_only(tcphy, false);
goto unlock_ret; goto unlock_ret;
} }
usleep_range(10, 20); usleep_range(10, 20);
@ -872,6 +931,7 @@ static int rockchip_usb3_phy_power_off(struct phy *phy)
struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
mutex_lock(&tcphy->lock); mutex_lock(&tcphy->lock);
tcphy_cfg_usb3_to_usb2_only(tcphy, false);
if (tcphy->mode == MODE_DISCONNECT) if (tcphy->mode == MODE_DISCONNECT)
goto unlock; goto unlock;
@ -894,6 +954,7 @@ static const struct phy_ops rockchip_usb3_phy_ops = {
static int rockchip_dp_phy_power_on(struct phy *phy) static int rockchip_dp_phy_power_on(struct phy *phy)
{ {
struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
int new_mode, ret = 0; int new_mode, ret = 0;
u32 val; u32 val;
@ -926,6 +987,8 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
if (ret) if (ret)
goto unlock_ret; goto unlock_ret;
property_enable(tcphy, &cfg->uphy_dp_sel, 1);
ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
val, val & DP_MODE_A2, 1000, val, val & DP_MODE_A2, 1000,
PHY_MODE_SET_TIMEOUT); PHY_MODE_SET_TIMEOUT);
@ -984,51 +1047,9 @@ static const struct phy_ops rockchip_dp_phy_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int tcphy_get_param(struct device *dev,
struct usb3phy_reg *reg,
const char *name)
{
u32 buffer[3];
int ret;
ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
if (ret) {
dev_err(dev, "Can not parse %s\n", name);
return ret;
}
reg->offset = buffer[0];
reg->enable_bit = buffer[1];
reg->write_enable = buffer[2];
return 0;
}
static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy, static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
struct device *dev) struct device *dev)
{ {
struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
int ret;
ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
"rockchip,typec-conn-dir");
if (ret)
return ret;
ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
"rockchip,usb3tousb2-en");
if (ret)
return ret;
ret = tcphy_get_param(dev, &cfg->external_psm,
"rockchip,external-psm");
if (ret)
return ret;
ret = tcphy_get_param(dev, &cfg->pipe_status,
"rockchip,pipe-status");
if (ret)
return ret;
tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node, tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf"); "rockchip,grf");
if (IS_ERR(tcphy->grf_regs)) { if (IS_ERR(tcphy->grf_regs)) {
@ -1071,7 +1092,7 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
{ {
struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
reset_control_assert(tcphy->tcphy_rst); reset_control_assert(tcphy->tcphy_rst);
reset_control_assert(tcphy->uphy_rst); reset_control_assert(tcphy->uphy_rst);
@ -1092,17 +1113,43 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
struct rockchip_typec_phy *tcphy; struct rockchip_typec_phy *tcphy;
struct phy_provider *phy_provider; struct phy_provider *phy_provider;
struct resource *res; struct resource *res;
int ret; const struct rockchip_usb3phy_port_cfg *phy_cfgs;
const struct of_device_id *match;
int index, ret;
tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL); tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
if (!tcphy) if (!tcphy)
return -ENOMEM; return -ENOMEM;
match = of_match_device(dev->driver->of_match_table, dev);
if (!match || !match->data) {
dev_err(dev, "phy configs are not assigned!\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tcphy->base = devm_ioremap_resource(dev, res); tcphy->base = devm_ioremap_resource(dev, res);
if (IS_ERR(tcphy->base)) if (IS_ERR(tcphy->base))
return PTR_ERR(tcphy->base); return PTR_ERR(tcphy->base);
phy_cfgs = match->data;
/* find out a proper config which can be matched with dt. */
index = 0;
while (phy_cfgs[index].reg) {
if (phy_cfgs[index].reg == res->start) {
tcphy->port_cfgs = &phy_cfgs[index];
break;
}
++index;
}
if (!tcphy->port_cfgs) {
dev_err(dev, "no phy-config can be matched with %s node\n",
np->name);
return -EINVAL;
}
ret = tcphy_parse_dt(tcphy, dev); ret = tcphy_parse_dt(tcphy, dev);
if (ret) if (ret)
return ret; return ret;
@ -1115,9 +1162,13 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
tcphy->extcon = extcon_get_edev_by_phandle(dev, 0); tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
if (IS_ERR(tcphy->extcon)) { if (IS_ERR(tcphy->extcon)) {
if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER) if (PTR_ERR(tcphy->extcon) == -ENODEV) {
dev_err(dev, "Invalid or missing extcon\n"); tcphy->extcon = NULL;
return PTR_ERR(tcphy->extcon); } else {
if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
dev_err(dev, "Invalid or missing extcon\n");
return PTR_ERR(tcphy->extcon);
}
} }
pm_runtime_enable(dev); pm_runtime_enable(dev);
@ -1162,8 +1213,11 @@ static int rockchip_typec_phy_remove(struct platform_device *pdev)
} }
static const struct of_device_id rockchip_typec_phy_dt_ids[] = { static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
{ .compatible = "rockchip,rk3399-typec-phy" }, {
{} .compatible = "rockchip,rk3399-typec-phy",
.data = &rk3399_usb3phy_port_cfgs
},
{ /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids); MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);

View File

@ -49,7 +49,7 @@ config PHY_EXYNOS4210_USB2
config PHY_EXYNOS4X12_USB2 config PHY_EXYNOS4X12_USB2
bool bool
depends on PHY_SAMSUNG_USB2 depends on PHY_SAMSUNG_USB2
default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412 default SOC_EXYNOS3250 || SOC_EXYNOS4412
config PHY_EXYNOS5250_USB2 config PHY_EXYNOS5250_USB2
bool bool

View File

@ -31,3 +31,17 @@ config PHY_STIH407_USB
help help
Enable this support to enable the picoPHY device used by USB2 Enable this support to enable the picoPHY device used by USB2
and USB3 controllers on STMicroelectronics STiH407 SoC families. and USB3 controllers on STMicroelectronics STiH407 SoC families.
config PHY_STM32_USBPHYC
tristate "STMicroelectronics STM32 USB HS PHY Controller driver"
depends on ARCH_STM32 || COMPILE_TEST
select GENERIC_PHY
help
Enable this to support the High-Speed USB transceivers that are part
of some STMicroelectronics STM32 SoCs.
This driver controls the entire USB PHY block: the USB PHY controller
(USBPHYC) and the two 8-bit wide UTMI+ interfaces. First interface is
used by an HS USB Host controller, and the second one is shared
between an HS USB OTG controller and an HS USB Host controller,
selected by a USB switch.

View File

@ -2,3 +2,4 @@ obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
obj-$(CONFIG_PHY_STM32_USBPHYC) += phy-stm32-usbphyc.o

View File

@ -0,0 +1,461 @@
// SPDX-Licence-Identifier: GPL-2.0
/*
* STMicroelectronics STM32 USB PHY Controller driver
*
* Copyright (C) 2018 STMicroelectronics
* Author(s): Amelie Delaunay <amelie.delaunay@st.com>.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/reset.h>
#define STM32_USBPHYC_PLL 0x0
#define STM32_USBPHYC_MISC 0x8
#define STM32_USBPHYC_VERSION 0x3F4
/* STM32_USBPHYC_PLL bit fields */
#define PLLNDIV GENMASK(6, 0)
#define PLLFRACIN GENMASK(25, 10)
#define PLLEN BIT(26)
#define PLLSTRB BIT(27)
#define PLLSTRBYP BIT(28)
#define PLLFRACCTL BIT(29)
#define PLLDITHEN0 BIT(30)
#define PLLDITHEN1 BIT(31)
/* STM32_USBPHYC_MISC bit fields */
#define SWITHOST BIT(0)
/* STM32_USBPHYC_VERSION bit fields */
#define MINREV GENMASK(3, 0)
#define MAJREV GENMASK(7, 4)
static const char * const supplies_names[] = {
"vdda1v1", /* 1V1 */
"vdda1v8", /* 1V8 */
};
#define NUM_SUPPLIES ARRAY_SIZE(supplies_names)
#define PLL_LOCK_TIME_US 100
#define PLL_PWR_DOWN_TIME_US 5
#define PLL_FVCO_MHZ 2880
#define PLL_INFF_MIN_RATE_HZ 19200000
#define PLL_INFF_MAX_RATE_HZ 38400000
#define HZ_PER_MHZ 1000000L
struct pll_params {
u8 ndiv;
u16 frac;
};
struct stm32_usbphyc_phy {
struct phy *phy;
struct stm32_usbphyc *usbphyc;
struct regulator_bulk_data supplies[NUM_SUPPLIES];
u32 index;
bool active;
};
struct stm32_usbphyc {
struct device *dev;
void __iomem *base;
struct clk *clk;
struct reset_control *rst;
struct stm32_usbphyc_phy **phys;
int nphys;
int switch_setup;
bool pll_enabled;
};
static inline void stm32_usbphyc_set_bits(void __iomem *reg, u32 bits)
{
writel_relaxed(readl_relaxed(reg) | bits, reg);
}
static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits)
{
writel_relaxed(readl_relaxed(reg) & ~bits, reg);
}
static void stm32_usbphyc_get_pll_params(u32 clk_rate, struct pll_params *pll_params)
{
unsigned long long fvco, ndiv, frac;
/* _
* | FVCO = INFF*2*(NDIV + FRACT/2^16) when DITHER_DISABLE[1] = 1
* | FVCO = 2880MHz
* <
* | NDIV = integer part of input bits to set the LDF
* |_FRACT = fractional part of input bits to set the LDF
* => PLLNDIV = integer part of (FVCO / (INFF*2))
* => PLLFRACIN = fractional part of(FVCO / INFF*2) * 2^16
* <=> PLLFRACIN = ((FVCO / (INFF*2)) - PLLNDIV) * 2^16
*/
fvco = (unsigned long long)PLL_FVCO_MHZ * HZ_PER_MHZ;
ndiv = fvco;
do_div(ndiv, (clk_rate * 2));
pll_params->ndiv = (u8)ndiv;
frac = fvco * (1 << 16);
do_div(frac, (clk_rate * 2));
frac = frac - (ndiv * (1 << 16));
pll_params->frac = (u16)frac;
}
static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc)
{
struct pll_params pll_params;
u32 clk_rate = clk_get_rate(usbphyc->clk);
u32 ndiv, frac;
u32 usbphyc_pll;
if ((clk_rate < PLL_INFF_MIN_RATE_HZ) ||
(clk_rate > PLL_INFF_MAX_RATE_HZ)) {
dev_err(usbphyc->dev, "input clk freq (%dHz) out of range\n",
clk_rate);
return -EINVAL;
}
stm32_usbphyc_get_pll_params(clk_rate, &pll_params);
ndiv = FIELD_PREP(PLLNDIV, pll_params.ndiv);
frac = FIELD_PREP(PLLFRACIN, pll_params.frac);
usbphyc_pll = PLLDITHEN1 | PLLDITHEN0 | PLLSTRBYP | ndiv;
if (pll_params.frac)
usbphyc_pll |= PLLFRACCTL | frac;
writel_relaxed(usbphyc_pll, usbphyc->base + STM32_USBPHYC_PLL);
dev_dbg(usbphyc->dev, "input clk freq=%dHz, ndiv=%lu, frac=%lu\n",
clk_rate, FIELD_GET(PLLNDIV, usbphyc_pll),
FIELD_GET(PLLFRACIN, usbphyc_pll));
return 0;
}
static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc)
{
int i;
for (i = 0; i < usbphyc->nphys; i++)
if (usbphyc->phys[i]->active)
return true;
return false;
}
static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc)
{
void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
bool pllen = (readl_relaxed(pll_reg) & PLLEN);
int ret;
/* Check if one phy port has already configured the pll */
if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc))
return 0;
if (pllen) {
stm32_usbphyc_clr_bits(pll_reg, PLLEN);
/* Wait for minimum width of powerdown pulse (ENABLE = Low) */
udelay(PLL_PWR_DOWN_TIME_US);
}
ret = stm32_usbphyc_pll_init(usbphyc);
if (ret)
return ret;
stm32_usbphyc_set_bits(pll_reg, PLLEN);
/* Wait for maximum lock time */
udelay(PLL_LOCK_TIME_US);
if (!(readl_relaxed(pll_reg) & PLLEN)) {
dev_err(usbphyc->dev, "PLLEN not set\n");
return -EIO;
}
return 0;
}
static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc)
{
void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL;
/* Check if other phy port active */
if (stm32_usbphyc_has_one_phy_active(usbphyc))
return 0;
stm32_usbphyc_clr_bits(pll_reg, PLLEN);
/* Wait for minimum width of powerdown pulse (ENABLE = Low) */
udelay(PLL_PWR_DOWN_TIME_US);
if (readl_relaxed(pll_reg) & PLLEN) {
dev_err(usbphyc->dev, "PLL not reset\n");
return -EIO;
}
return 0;
}
static int stm32_usbphyc_phy_init(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
int ret;
ret = stm32_usbphyc_pll_enable(usbphyc);
if (ret)
return ret;
usbphyc_phy->active = true;
return 0;
}
static int stm32_usbphyc_phy_exit(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
struct stm32_usbphyc *usbphyc = usbphyc_phy->usbphyc;
usbphyc_phy->active = false;
return stm32_usbphyc_pll_disable(usbphyc);
}
static int stm32_usbphyc_phy_power_on(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies);
}
static int stm32_usbphyc_phy_power_off(struct phy *phy)
{
struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy);
return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies);
}
static const struct phy_ops stm32_usbphyc_phy_ops = {
.init = stm32_usbphyc_phy_init,
.exit = stm32_usbphyc_phy_exit,
.power_on = stm32_usbphyc_phy_power_on,
.power_off = stm32_usbphyc_phy_power_off,
.owner = THIS_MODULE,
};
static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc,
u32 utmi_switch)
{
if (!utmi_switch)
stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_MISC,
SWITHOST);
else
stm32_usbphyc_set_bits(usbphyc->base + STM32_USBPHYC_MISC,
SWITHOST);
usbphyc->switch_setup = utmi_switch;
}
static struct phy *stm32_usbphyc_of_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev);
struct stm32_usbphyc_phy *usbphyc_phy = NULL;
struct device_node *phynode = args->np;
int port = 0;
for (port = 0; port < usbphyc->nphys; port++) {
if (phynode == usbphyc->phys[port]->phy->dev.of_node) {
usbphyc_phy = usbphyc->phys[port];
break;
}
}
if (!usbphyc_phy) {
dev_err(dev, "failed to find phy\n");
return ERR_PTR(-EINVAL);
}
if (((usbphyc_phy->index == 0) && (args->args_count != 0)) ||
((usbphyc_phy->index == 1) && (args->args_count != 1))) {
dev_err(dev, "invalid number of cells for phy port%d\n",
usbphyc_phy->index);
return ERR_PTR(-EINVAL);
}
/* Configure the UTMI switch for PHY port#2 */
if (usbphyc_phy->index == 1) {
if (usbphyc->switch_setup < 0) {
stm32_usbphyc_switch_setup(usbphyc, args->args[0]);
} else {
if (args->args[0] != usbphyc->switch_setup) {
dev_err(dev, "phy port1 already used\n");
return ERR_PTR(-EBUSY);
}
}
}
return usbphyc_phy->phy;
}
static int stm32_usbphyc_probe(struct platform_device *pdev)
{
struct stm32_usbphyc *usbphyc;
struct device *dev = &pdev->dev;
struct device_node *child, *np = dev->of_node;
struct resource *res;
struct phy_provider *phy_provider;
u32 version;
int ret, port = 0;
usbphyc = devm_kzalloc(dev, sizeof(*usbphyc), GFP_KERNEL);
if (!usbphyc)
return -ENOMEM;
usbphyc->dev = dev;
dev_set_drvdata(dev, usbphyc);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
usbphyc->base = devm_ioremap_resource(dev, res);
if (IS_ERR(usbphyc->base))
return PTR_ERR(usbphyc->base);
usbphyc->clk = devm_clk_get(dev, 0);
if (IS_ERR(usbphyc->clk)) {
ret = PTR_ERR(usbphyc->clk);
dev_err(dev, "clk get failed: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(usbphyc->clk);
if (ret) {
dev_err(dev, "clk enable failed: %d\n", ret);
return ret;
}
usbphyc->rst = devm_reset_control_get(dev, 0);
if (!IS_ERR(usbphyc->rst)) {
reset_control_assert(usbphyc->rst);
udelay(2);
reset_control_deassert(usbphyc->rst);
}
usbphyc->switch_setup = -EINVAL;
usbphyc->nphys = of_get_child_count(np);
usbphyc->phys = devm_kcalloc(dev, usbphyc->nphys,
sizeof(*usbphyc->phys), GFP_KERNEL);
if (!usbphyc->phys) {
ret = -ENOMEM;
goto clk_disable;
}
for_each_child_of_node(np, child) {
struct stm32_usbphyc_phy *usbphyc_phy;
struct phy *phy;
u32 index;
int i;
phy = devm_phy_create(dev, child, &stm32_usbphyc_phy_ops);
if (IS_ERR(phy)) {
ret = PTR_ERR(phy);
if (ret != -EPROBE_DEFER)
dev_err(dev,
"failed to create phy%d: %d\n", i, ret);
goto put_child;
}
usbphyc_phy = devm_kzalloc(dev, sizeof(*usbphyc_phy),
GFP_KERNEL);
if (!usbphyc_phy) {
ret = -ENOMEM;
goto put_child;
}
for (i = 0; i < NUM_SUPPLIES; i++)
usbphyc_phy->supplies[i].supply = supplies_names[i];
ret = devm_regulator_bulk_get(&phy->dev, NUM_SUPPLIES,
usbphyc_phy->supplies);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&phy->dev,
"failed to get regulators: %d\n", ret);
goto put_child;
}
ret = of_property_read_u32(child, "reg", &index);
if (ret || index > usbphyc->nphys) {
dev_err(&phy->dev, "invalid reg property: %d\n", ret);
goto put_child;
}
usbphyc->phys[port] = usbphyc_phy;
phy_set_bus_width(phy, 8);
phy_set_drvdata(phy, usbphyc_phy);
usbphyc->phys[port]->phy = phy;
usbphyc->phys[port]->usbphyc = usbphyc;
usbphyc->phys[port]->index = index;
usbphyc->phys[port]->active = false;
port++;
}
phy_provider = devm_of_phy_provider_register(dev,
stm32_usbphyc_of_xlate);
if (IS_ERR(phy_provider)) {
ret = PTR_ERR(phy_provider);
dev_err(dev, "failed to register phy provider: %d\n", ret);
goto clk_disable;
}
version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION);
dev_info(dev, "registered rev:%lu.%lu\n",
FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version));
return 0;
put_child:
of_node_put(child);
clk_disable:
clk_disable_unprepare(usbphyc->clk);
return ret;
}
static int stm32_usbphyc_remove(struct platform_device *pdev)
{
struct stm32_usbphyc *usbphyc = dev_get_drvdata(&pdev->dev);
clk_disable_unprepare(usbphyc->clk);
return 0;
}
static const struct of_device_id stm32_usbphyc_of_match[] = {
{ .compatible = "st,stm32mp1-usbphyc", },
{ },
};
MODULE_DEVICE_TABLE(of, stm32_usbphyc_of_match);
static struct platform_driver stm32_usbphyc_driver = {
.probe = stm32_usbphyc_probe,
.remove = stm32_usbphyc_remove,
.driver = {
.of_match_table = stm32_usbphyc_of_match,
.name = "stm32-usbphyc",
}
};
module_platform_driver(stm32_usbphyc_driver);
MODULE_DESCRIPTION("STMicroelectronics STM32 USBPHYC driver");
MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
MODULE_LICENSE("GPL v2");

View File

@ -169,6 +169,7 @@
#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN (1 << 0) #define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN (1 << 0)
#define XUSB_PADCTL_UPHY_PLL_P0_CTL4 0x36c #define XUSB_PADCTL_UPHY_PLL_P0_CTL4 0x36c
#define XUSB_PADCTL_UPHY_PLL_CTL4_XDIGCLK_EN (1 << 19)
#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN (1 << 15) #define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN (1 << 15)
#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT 12 #define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT 12
#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK 0x3 #define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK 0x3
@ -537,11 +538,8 @@ static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb)
value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SATA_VAL << value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SATA_VAL <<
XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT); XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT);
/* XXX PLL0_XDIGCLK_EN */ value &= ~XUSB_PADCTL_UPHY_PLL_CTL4_XDIGCLK_EN;
/*
value &= ~(1 << 19);
padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL4); padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL4);
*/
value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1); value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
value &= ~((XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK << value &= ~((XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK <<

View File

@ -418,7 +418,7 @@ tegra_xusb_port_find_lane(struct tegra_xusb_port *port,
{ {
struct tegra_xusb_lane *lane, *match = ERR_PTR(-ENODEV); struct tegra_xusb_lane *lane, *match = ERR_PTR(-ENODEV);
for (map = map; map->type; map++) { for (; map->type; map++) {
if (port->index != map->port) if (port->index != map->port)
continue; continue;

View File

@ -25,7 +25,15 @@ struct phy;
enum phy_mode { enum phy_mode {
PHY_MODE_INVALID, PHY_MODE_INVALID,
PHY_MODE_USB_HOST, PHY_MODE_USB_HOST,
PHY_MODE_USB_HOST_LS,
PHY_MODE_USB_HOST_FS,
PHY_MODE_USB_HOST_HS,
PHY_MODE_USB_HOST_SS,
PHY_MODE_USB_DEVICE, PHY_MODE_USB_DEVICE,
PHY_MODE_USB_DEVICE_LS,
PHY_MODE_USB_DEVICE_FS,
PHY_MODE_USB_DEVICE_HS,
PHY_MODE_USB_DEVICE_SS,
PHY_MODE_USB_OTG, PHY_MODE_USB_OTG,
PHY_MODE_SGMII, PHY_MODE_SGMII,
PHY_MODE_10GKR, PHY_MODE_10GKR,
@ -61,6 +69,7 @@ struct phy_ops {
*/ */
struct phy_attrs { struct phy_attrs {
u32 bus_width; u32 bus_width;
enum phy_mode mode;
}; };
/** /**
@ -72,7 +81,8 @@ struct phy_attrs {
* @mutex: mutex to protect phy_ops * @mutex: mutex to protect phy_ops
* @init_count: used to protect when the PHY is used by multiple consumers * @init_count: used to protect when the PHY is used by multiple consumers
* @power_count: used to protect when the PHY is used by multiple consumers * @power_count: used to protect when the PHY is used by multiple consumers
* @phy_attrs: used to specify PHY specific attributes * @attrs: used to specify PHY specific attributes
* @pwr: power regulator associated with the phy
*/ */
struct phy { struct phy {
struct device dev; struct device dev;
@ -88,9 +98,10 @@ struct phy {
/** /**
* struct phy_provider - represents the phy provider * struct phy_provider - represents the phy provider
* @dev: phy provider device * @dev: phy provider device
* @children: can be used to override the default (dev->of_node) child node
* @owner: the module owner having of_xlate * @owner: the module owner having of_xlate
* @of_xlate: function pointer to obtain phy instance from phy pointer
* @list: to maintain a linked list of PHY providers * @list: to maintain a linked list of PHY providers
* @of_xlate: function pointer to obtain phy instance from phy pointer
*/ */
struct phy_provider { struct phy_provider {
struct device *dev; struct device *dev;
@ -101,6 +112,13 @@ struct phy_provider {
struct of_phandle_args *args); struct of_phandle_args *args);
}; };
/**
* struct phy_lookup - PHY association in list of phys managed by the phy driver
* @node: list node
* @dev_id: the device of the association
* @con_id: connection ID string on device
* @phy: the phy of the association
*/
struct phy_lookup { struct phy_lookup {
struct list_head node; struct list_head node;
const char *dev_id; const char *dev_id;
@ -144,6 +162,10 @@ int phy_exit(struct phy *phy);
int phy_power_on(struct phy *phy); int phy_power_on(struct phy *phy);
int phy_power_off(struct phy *phy); int phy_power_off(struct phy *phy);
int phy_set_mode(struct phy *phy, enum phy_mode mode); int phy_set_mode(struct phy *phy, enum phy_mode mode);
static inline enum phy_mode phy_get_mode(struct phy *phy)
{
return phy->attrs.mode;
}
int phy_reset(struct phy *phy); int phy_reset(struct phy *phy);
int phy_calibrate(struct phy *phy); int phy_calibrate(struct phy *phy);
static inline int phy_get_bus_width(struct phy *phy) static inline int phy_get_bus_width(struct phy *phy)
@ -260,6 +282,11 @@ static inline int phy_set_mode(struct phy *phy, enum phy_mode mode)
return -ENOSYS; return -ENOSYS;
} }
static inline enum phy_mode phy_get_mode(struct phy *phy)
{
return PHY_MODE_INVALID;
}
static inline int phy_reset(struct phy *phy) static inline int phy_reset(struct phy *phy)
{ {
if (!phy) if (!phy)