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:
commit
85a09bf492
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
|
};
|
|
@ -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)>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
|
@ -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>;
|
||||||
|
};
|
|
@ -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.
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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 = <®11>;
|
||||||
|
vdda1v8-supply = <®18>
|
||||||
|
#phy-cells = <0>;
|
||||||
|
};
|
||||||
|
|
||||||
|
usbphyc_port1: usb-phy@1 {
|
||||||
|
reg = <1>;
|
||||||
|
phy-supply = <&vdd_usb>;
|
||||||
|
vdda1v1-supply = <®11>;
|
||||||
|
vdda1v8-supply = <®18>
|
||||||
|
#phy-cells = <1>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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},
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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");
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
|
@ -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 <<
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue