phy-for-6.10

- New Support
   - Support for Embedded DisplayPort and DisplayPort submodes and driver
     support on Qualcomm X1E80100 edp driver
   - Qualcomm QMP UFS PHY for SM8475, QMP USB phy for QDU1000/QRU1000 and
     eusb2-repeater for SMB2360
   - Samsung HDMI PHY for i.MX8MP, gs101 UFS phy
   - Mediatek XFI T-PHY support for mt7988
   - Rockchip usbdp combo phy driver
 
 - Updates
   - Qualcomm x4 lane EP support for sa8775p, v4 ad v6 support for X1E80100,
     SM8650 tables for UFS Gear 4 & 5 and correct voltage swing tables
   - Freescale imx8m-pci pcie link-up updates
   - Rockchip rx-common-refclk-mode support
   - More platform remove callback returning void conversions
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+vs47OPLdNbVcHzyfBQHDyUjg0cFAmZMSswACgkQfBQHDyUj
 g0cFBQ//ZJIbYXFfCqiC0jfpqAtO6f3yxScNvn7xOXTHqe2ErzAiG08Sj/fdKwFT
 UCMY8jSm+AQq7VQYUqGqJypRIBgdKrWXtBTemT5Xa5VeB5cgVoFpNv5U+1rL7MS7
 IK3U5jKIYXUBh7z2gmZr4ftrIEXJXgNhJn4Ntelh5mQYL/QZBcCoIfteJIe6pDCv
 /mbLT4jAaju2/b4P7/Q96JsMeOXW0ewBuveM/mmFZgqDNDB0nBvHvh+Cn2RHRzR0
 /xXEZ1L4TXAQ5txtv0dIPdQp8vnM7OQj6xY256Os19bhxr+zcRgl7aRDvJdgJgrA
 np6H14hh5H/XiAFJqg8ZTTaGFhnyUDI9bndMUxj5Zku+lmwLGleu8tfiR0kS7v1l
 exgHsTMYNkgwDQ8KmU0z+hDTDCM0nVnpI/GcKruc79e0ZuEh28N1S9d8CGAYx1Of
 vmfF9A6hhzSDY6RvyQT4vvm+JLhesuox1CxE9IDMIeIiydyR4ZLlGBvv6Opk1E9U
 v0FtMnF9HGv6SiGTBsA5VB94o9Z91Ad3G2sMwkTZTjR8KgM9pcO9hlHF+Cec8ua0
 y4mcxDrcuzUNvZ9qzwCUTyxuv0600tAlsaebAeAI7LQAMFeompOzhqdr7Jc2MGiQ
 10nnB86pOdxh16migt65aL7zglqHo0T8zpvMBOJqNeW/D32fJG8=
 =og1j
 -----END PGP SIGNATURE-----

Merge tag 'phy-for-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy

Pull generic phy updates from Vinod Koul:
 "New HW Support:
   - Support for Embedded DisplayPort and DisplayPort submodes and
     driver support on Qualcomm X1E80100 edp driver
   - Qualcomm QMP UFS PHY for SM8475, QMP USB phy for QDU1000/QRU1000
     and eusb2-repeater for SMB2360
   - Samsung HDMI PHY for i.MX8MP, gs101 UFS phy
   - Mediatek XFI T-PHY support for mt7988
   - Rockchip usbdp combo phy driver

  Updates:
   - Qualcomm x4 lane EP support for sa8775p, v4 ad v6 support for
     X1E80100, SM8650 tables for UFS Gear 4 & 5 and correct voltage
     swing tables
   - Freescale imx8m-pci pcie link-up updates
   - Rockchip rx-common-refclk-mode support
   - More platform remove callback returning void conversions"

* tag 'phy-for-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (43 commits)
  dt-bindings: phy: qcom,usb-snps-femto-v2: use correct fallback for sc8180x
  dt-bindings: phy: qcom,sc8280xp-qmp-ufs-phy: fix msm899[68] power-domains
  dt-bindings: phy: qcom,sc8280xp-qmp-pcie-phy: fix x1e80100-gen3x2 schema
  phy: qcpm-qmp-usb: Add support for QDU1000/QRU1000
  dt-bindings: phy: qcom,qmp-usb: Add QDU1000 USB3 PHY
  dt-bindings: phy: qcom,usb-snps-femto-v2: Add bindings for QDU1000
  phy: qcom-qmp-pcie: add x4 lane EP support for sa8775p
  phy: samsung-ufs: ufs: exit on first reported error
  phy: samsung-ufs: ufs: remove superfluous mfd/syscon.h header
  phy: rockchip: fix CONFIG_TYPEC dependency
  phy: rockchip: usbdp: fix uninitialized variable
  phy: rockchip-snps-pcie3: add support for rockchip,rx-common-refclk-mode
  dt-bindings: phy: rockchip,pcie3-phy: add rockchip,rx-common-refclk-mode
  phy: rockchip: add usbdp combo phy driver
  dt-bindings: phy: add rockchip usbdp combo phy document
  phy: add driver for MediaTek XFI T-PHY
  dt-bindings: phy: mediatek,mt7988-xfi-tphy: add new bindings
  phy: freescale: fsl-samsung-hdmi: Convert to platform remove callback returning void
  phy: qcom: qmp-ufs: update SM8650 tables for Gear 4 & 5
  MAINTAINERS: Add phy-gs101-ufs file to Tensor GS101.
  ...
This commit is contained in:
Linus Torvalds 2024-05-21 11:19:18 -07:00
commit 8053d2ffc4
41 changed files with 4101 additions and 131 deletions

View file

@ -0,0 +1,62 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/fsl,imx8mp-hdmi-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale i.MX8MP HDMI PHY
maintainers:
- Lucas Stach <l.stach@pengutronix.de>
properties:
compatible:
enum:
- fsl,imx8mp-hdmi-phy
reg:
maxItems: 1
"#clock-cells":
const: 0
clocks:
maxItems: 2
clock-names:
items:
- const: apb
- const: ref
"#phy-cells":
const: 0
power-domains:
maxItems: 1
required:
- compatible
- reg
- "#clock-cells"
- clocks
- clock-names
- "#phy-cells"
- power-domains
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/imx8mp-clock.h>
#include <dt-bindings/power/imx8mp-power.h>
phy@32fdff00 {
compatible = "fsl,imx8mp-hdmi-phy";
reg = <0x32fdff00 0x100>;
clocks = <&clk IMX8MP_CLK_HDMI_APB>,
<&clk IMX8MP_CLK_HDMI_24M>;
clock-names = "apb", "ref";
power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_HDMI_TX_PHY>;
#clock-cells = <0>;
#phy-cells = <0>;
};

View file

@ -0,0 +1,80 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/mediatek,mt7988-xfi-tphy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek MT7988 XFI T-PHY
maintainers:
- Daniel Golle <daniel@makrotopia.org>
description:
The MediaTek XFI SerDes T-PHY provides the physical SerDes lanes
used by the (10G/5G) USXGMII PCS and (1G/2.5G) LynxI PCS found in
MediaTek's 10G-capabale MT7988 SoC.
In MediaTek's SDK sources, this unit is referred to as "pextp".
properties:
compatible:
const: mediatek,mt7988-xfi-tphy
reg:
maxItems: 1
clocks:
items:
- description: XFI PHY clock
- description: XFI register clock
clock-names:
items:
- const: xfipll
- const: topxtal
resets:
items:
- description: Reset controller corresponding to the phy instance.
mediatek,usxgmii-performance-errata:
$ref: /schemas/types.yaml#/definitions/flag
description:
One instance of the T-PHY on MT7988 suffers from a performance
problem in 10GBase-R mode which needs a work-around in the driver.
This flag enables a work-around ajusting an analog phy setting and
is required for XFI Port0 of the MT7988 SoC to be in compliance with
the SFP specification.
"#phy-cells":
const: 0
required:
- compatible
- reg
- clocks
- clock-names
- resets
- "#phy-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/mediatek,mt7988-clk.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
phy@11f20000 {
compatible = "mediatek,mt7988-xfi-tphy";
reg = <0 0x11f20000 0 0x10000>;
clocks = <&xfi_pll CLK_XFIPLL_PLL_EN>,
<&topckgen CLK_TOP_XFI_PHY_0_XTAL_SEL>;
clock-names = "xfipll", "topxtal";
resets = <&watchdog 14>;
mediatek,usxgmii-performance-errata;
#phy-cells = <0>;
};
};
...

View file

@ -0,0 +1,148 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/phy/phy-rockchip-usbdp.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Rockchip USBDP Combo PHY with Samsung IP block
maintainers:
- Frank Wang <frank.wang@rock-chips.com>
- Zhang Yubing <yubing.zhang@rock-chips.com>
properties:
compatible:
enum:
- rockchip,rk3588-usbdp-phy
reg:
maxItems: 1
"#phy-cells":
description: |
Cell allows setting the type of the PHY. Possible values are:
- PHY_TYPE_USB3
- PHY_TYPE_DP
const: 1
clocks:
maxItems: 4
clock-names:
items:
- const: refclk
- const: immortal
- const: pclk
- const: utmi
resets:
maxItems: 5
reset-names:
items:
- const: init
- const: cmn
- const: lane
- const: pcs_apb
- const: pma_apb
rockchip,dp-lane-mux:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 2
maxItems: 4
items:
maximum: 3
description:
An array of physical Type-C lanes indexes. Position of an entry
determines the DisplayPort (DP) lane index, while the value of an entry
indicates physical Type-C lane. The supported DP lanes number are 2 or 4.
e.g. for 2 lanes DP lanes map, we could have "rockchip,dp-lane-mux = <2,
3>;", assuming DP lane0 on Type-C phy lane2, DP lane1 on Type-C phy
lane3. For 4 lanes DP lanes map, we could have "rockchip,dp-lane-mux =
<0, 1, 2, 3>;", assuming DP lane0 on Type-C phy lane0, DP lane1 on Type-C
phy lane1, DP lane2 on Type-C phy lane2, DP lane3 on Type-C phy lane3. If
DP lanes are mapped by DisplayPort Alt mode, this property is not needed.
rockchip,u2phy-grf:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the syscon managing the 'usb2 phy general register files'.
rockchip,usb-grf:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the syscon managing the 'usb general register files'.
rockchip,usbdpphy-grf:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the syscon managing the 'usbdp phy general register files'.
rockchip,vo-grf:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to the syscon managing the 'video output general register files'.
When select the DP lane mapping will request its phandle.
sbu1-dc-gpios:
description:
GPIO connected to the SBU1 line of the USB-C connector via a big resistor
(~100K) to apply a DC offset for signalling the connector orientation.
maxItems: 1
sbu2-dc-gpios:
description:
GPIO connected to the SBU2 line of the USB-C connector via a big resistor
(~100K) to apply a DC offset for signalling the connector orientation.
maxItems: 1
orientation-switch:
description: Flag the port as possible handler of orientation switching
type: boolean
mode-switch:
description: Flag the port as possible handler of altmode switching
type: boolean
port:
$ref: /schemas/graph.yaml#/properties/port
description:
A port node to link the PHY to a TypeC controller for the purpose of
handling orientation switching.
required:
- compatible
- reg
- clocks
- clock-names
- resets
- reset-names
- "#phy-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
#include <dt-bindings/reset/rockchip,rk3588-cru.h>
usbdp_phy0: phy@fed80000 {
compatible = "rockchip,rk3588-usbdp-phy";
reg = <0xfed80000 0x10000>;
#phy-cells = <1>;
clocks = <&cru CLK_USBDPPHY_MIPIDCPPHY_REF>,
<&cru CLK_USBDP_PHY0_IMMORTAL>,
<&cru PCLK_USBDPPHY0>,
<&u2phy0>;
clock-names = "refclk", "immortal", "pclk", "utmi";
resets = <&cru SRST_USBDP_COMBO_PHY0_INIT>,
<&cru SRST_USBDP_COMBO_PHY0_CMN>,
<&cru SRST_USBDP_COMBO_PHY0_LANE>,
<&cru SRST_USBDP_COMBO_PHY0_PCS>,
<&cru SRST_P_USBDPPHY0>;
reset-names = "init", "cmn", "lane", "pcs_apb", "pma_apb";
rockchip,u2phy-grf = <&usb2phy0_grf>;
rockchip,usb-grf = <&usb_grf>;
rockchip,usbdpphy-grf = <&usbdpphy0_grf>;
rockchip,vo-grf = <&vo0_grf>;
};

View file

@ -21,6 +21,7 @@ properties:
- qcom,sc8180x-edp-phy
- qcom,sc8280xp-dp-phy
- qcom,sc8280xp-edp-phy
- qcom,x1e80100-dp-phy
reg:
items:

View file

@ -88,11 +88,11 @@ properties:
- description: offset of PCIe 4-lane configuration register
- description: offset of configuration bit for this PHY
"#clock-cells":
const: 0
"#clock-cells": true
clock-output-names:
maxItems: 1
minItems: 1
maxItems: 2
"#phy-cells":
const: 0
@ -198,7 +198,6 @@ allOf:
enum:
- qcom,sm8550-qmp-gen4x2-pcie-phy
- qcom,sm8650-qmp-gen4x2-pcie-phy
- qcom,x1e80100-qmp-gen3x2-pcie-phy
- qcom,x1e80100-qmp-gen4x2-pcie-phy
then:
properties:
@ -213,6 +212,27 @@ allOf:
reset-names:
maxItems: 1
- if:
properties:
compatible:
contains:
enum:
- qcom,sm8450-qmp-gen4x2-pcie-phy
- qcom,sm8550-qmp-gen4x2-pcie-phy
- qcom,sm8650-qmp-gen4x2-pcie-phy
then:
properties:
clock-output-names:
minItems: 2
"#clock-cells":
const: 1
else:
properties:
clock-output-names:
maxItems: 1
"#clock-cells":
const: 0
examples:
- |
#include <dt-bindings/clock/qcom,gcc-sc8280xp.h>

View file

@ -32,6 +32,7 @@ properties:
- qcom,sm8250-qmp-ufs-phy
- qcom,sm8350-qmp-ufs-phy
- qcom,sm8450-qmp-ufs-phy
- qcom,sm8475-qmp-ufs-phy
- qcom,sm8550-qmp-ufs-phy
- qcom,sm8650-qmp-ufs-phy
@ -71,7 +72,6 @@ required:
- reg
- clocks
- clock-names
- power-domains
- resets
- reset-names
- vdda-phy-supply
@ -86,6 +86,7 @@ allOf:
enum:
- qcom,msm8998-qmp-ufs-phy
- qcom,sa8775p-qmp-ufs-phy
- qcom,sc7180-qmp-ufs-phy
- qcom,sc7280-qmp-ufs-phy
- qcom,sc8180x-qmp-ufs-phy
- qcom,sc8280xp-qmp-ufs-phy
@ -98,6 +99,7 @@ allOf:
- qcom,sm8250-qmp-ufs-phy
- qcom,sm8350-qmp-ufs-phy
- qcom,sm8450-qmp-ufs-phy
- qcom,sm8475-qmp-ufs-phy
- qcom,sm8550-qmp-ufs-phy
- qcom,sm8650-qmp-ufs-phy
then:
@ -127,6 +129,21 @@ allOf:
- const: ref
- const: qref
- if:
properties:
compatible:
contains:
enum:
- qcom,msm8996-qmp-ufs-phy
- qcom,msm8998-qmp-ufs-phy
then:
properties:
power-domains:
false
else:
required:
- power-domains
additionalProperties: false
examples:

View file

@ -20,6 +20,7 @@ properties:
- qcom,ipq8074-qmp-usb3-phy
- qcom,ipq9574-qmp-usb3-phy
- qcom,msm8996-qmp-usb3-phy
- com,qdu1000-qmp-usb3-uni-phy
- qcom,sa8775p-qmp-usb3-uni-phy
- qcom,sc8280xp-qmp-usb3-uni-phy
- qcom,sdm845-qmp-usb3-uni-phy
@ -109,6 +110,7 @@ allOf:
compatible:
contains:
enum:
- qcom,qdu1000-qmp-usb3-uni-phy
- qcom,sa8775p-qmp-usb3-uni-phy
- qcom,sc8280xp-qmp-usb3-uni-phy
- qcom,sm8150-qmp-usb3-uni-phy

View file

@ -20,7 +20,9 @@ properties:
- enum:
- qcom,pm7550ba-eusb2-repeater
- const: qcom,pm8550b-eusb2-repeater
- const: qcom,pm8550b-eusb2-repeater
- enum:
- qcom,pm8550b-eusb2-repeater
- qcom,smb2360-eusb2-repeater
reg:
maxItems: 1

View file

@ -15,9 +15,6 @@ description: |
properties:
compatible:
oneOf:
- enum:
- qcom,sc8180x-usb-hs-phy
- qcom,usb-snps-femto-v2-phy
- items:
- enum:
- qcom,sa8775p-usb-hs-phy
@ -25,7 +22,9 @@ properties:
- const: qcom,usb-snps-hs-5nm-phy
- items:
- enum:
- qcom,qdu1000-usb-hs-phy
- qcom,sc7280-usb-hs-phy
- qcom,sc8180x-usb-hs-phy
- qcom,sdx55-usb-hs-phy
- qcom,sdx65-usb-hs-phy
- qcom,sm6375-usb-hs-phy

View file

@ -54,6 +54,16 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle
description: phandle to the syscon managing the pipe "general register files"
rockchip,rx-common-refclk-mode:
description: which lanes (by position) should be configured to run in
RX common reference clock mode. 0 means disabled, 1 means enabled.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 16
items:
minimum: 0
maximum: 1
required:
- compatible
- reg

View file

@ -15,6 +15,7 @@ properties:
compatible:
enum:
- google,gs101-ufs-phy
- samsung,exynos7-ufs-phy
- samsung,exynosautov9-ufs-phy
- tesla,fsd-ufs-phy

View file

@ -9312,6 +9312,7 @@ S: Maintained
F: Documentation/devicetree/bindings/clock/google,gs101-clock.yaml
F: arch/arm64/boot/dts/exynos/google/
F: drivers/clk/samsung/clk-gs101.c
F: drivers/phy/samsung/phy-gs101-ufs.c
F: include/dt-bindings/clock/google,gs101.h
K: [gG]oogle.?[tT]ensor
@ -13987,6 +13988,7 @@ L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/phy/mediatek-ge-soc.c
F: drivers/net/phy/mediatek-ge.c
F: drivers/phy/mediatek/phy-mtk-xfi-tphy.c
MEDIATEK I2C CONTROLLER DRIVER
M: Qii Wang <qii.wang@mediatek.com>

View file

@ -35,6 +35,12 @@ config PHY_FSL_IMX8M_PCIE
Enable this to add support for the PCIE PHY as found on
i.MX8M family of SOCs.
config PHY_FSL_SAMSUNG_HDMI_PHY
tristate "Samsung HDMI PHY support"
depends on OF && HAS_IOMEM && COMMON_CLK
help
Enable this to add support for the Samsung HDMI PHY in i.MX8MP.
endif
config PHY_FSL_LYNX_28G

View file

@ -4,3 +4,4 @@ obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o
obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o
obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o
obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o
obj-$(CONFIG_PHY_FSL_SAMSUNG_HDMI_PHY) += phy-fsl-samsung-hdmi.o

View file

@ -0,0 +1,718 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2020 NXP
* Copyright 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#define PHY_REG_00 0x00
#define PHY_REG_01 0x04
#define PHY_REG_02 0x08
#define PHY_REG_08 0x20
#define PHY_REG_09 0x24
#define PHY_REG_10 0x28
#define PHY_REG_11 0x2c
#define PHY_REG_12 0x30
#define REG12_CK_DIV_MASK GENMASK(5, 4)
#define PHY_REG_13 0x34
#define REG13_TG_CODE_LOW_MASK GENMASK(7, 0)
#define PHY_REG_14 0x38
#define REG14_TOL_MASK GENMASK(7, 4)
#define REG14_RP_CODE_MASK GENMASK(3, 1)
#define REG14_TG_CODE_HIGH_MASK GENMASK(0, 0)
#define PHY_REG_15 0x3c
#define PHY_REG_16 0x40
#define PHY_REG_17 0x44
#define PHY_REG_18 0x48
#define PHY_REG_19 0x4c
#define PHY_REG_20 0x50
#define PHY_REG_21 0x54
#define REG21_SEL_TX_CK_INV BIT(7)
#define REG21_PMS_S_MASK GENMASK(3, 0)
#define PHY_REG_22 0x58
#define PHY_REG_23 0x5c
#define PHY_REG_24 0x60
#define PHY_REG_25 0x64
#define PHY_REG_26 0x68
#define PHY_REG_27 0x6c
#define PHY_REG_28 0x70
#define PHY_REG_29 0x74
#define PHY_REG_30 0x78
#define PHY_REG_31 0x7c
#define PHY_REG_32 0x80
/*
* REG33 does not match the ref manual. According to Sandor Yu from NXP,
* "There is a doc issue on the i.MX8MP latest RM"
* REG33 is being used per guidance from Sandor
*/
#define PHY_REG_33 0x84
#define REG33_MODE_SET_DONE BIT(7)
#define REG33_FIX_DA BIT(1)
#define PHY_REG_34 0x88
#define REG34_PHY_READY BIT(7)
#define REG34_PLL_LOCK BIT(6)
#define REG34_PHY_CLK_READY BIT(5)
#define PHY_REG_35 0x8c
#define PHY_REG_36 0x90
#define PHY_REG_37 0x94
#define PHY_REG_38 0x98
#define PHY_REG_39 0x9c
#define PHY_REG_40 0xa0
#define PHY_REG_41 0xa4
#define PHY_REG_42 0xa8
#define PHY_REG_43 0xac
#define PHY_REG_44 0xb0
#define PHY_REG_45 0xb4
#define PHY_REG_46 0xb8
#define PHY_REG_47 0xbc
#define PHY_PLL_DIV_REGS_NUM 6
struct phy_config {
u32 pixclk;
u8 pll_div_regs[PHY_PLL_DIV_REGS_NUM];
};
static const struct phy_config phy_pll_cfg[] = {
{
.pixclk = 22250000,
.pll_div_regs = { 0x4b, 0xf1, 0x89, 0x88, 0x80, 0x40 },
}, {
.pixclk = 23750000,
.pll_div_regs = { 0x50, 0xf1, 0x86, 0x85, 0x80, 0x40 },
}, {
.pixclk = 24000000,
.pll_div_regs = { 0x50, 0xf0, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 24024000,
.pll_div_regs = { 0x50, 0xf1, 0x99, 0x02, 0x80, 0x40 },
}, {
.pixclk = 25175000,
.pll_div_regs = { 0x54, 0xfc, 0xcc, 0x91, 0x80, 0x40 },
}, {
.pixclk = 25200000,
.pll_div_regs = { 0x54, 0xf0, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 26750000,
.pll_div_regs = { 0x5a, 0xf2, 0x89, 0x88, 0x80, 0x40 },
}, {
.pixclk = 27000000,
.pll_div_regs = { 0x5a, 0xf0, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 27027000,
.pll_div_regs = { 0x5a, 0xf2, 0xfd, 0x0c, 0x80, 0x40 },
}, {
.pixclk = 29500000,
.pll_div_regs = { 0x62, 0xf4, 0x95, 0x08, 0x80, 0x40 },
}, {
.pixclk = 30750000,
.pll_div_regs = { 0x66, 0xf4, 0x82, 0x01, 0x88, 0x45 },
}, {
.pixclk = 30888000,
.pll_div_regs = { 0x66, 0xf4, 0x99, 0x18, 0x88, 0x45 },
}, {
.pixclk = 33750000,
.pll_div_regs = { 0x70, 0xf4, 0x82, 0x01, 0x80, 0x40 },
}, {
.pixclk = 35000000,
.pll_div_regs = { 0x58, 0xb8, 0x8b, 0x88, 0x80, 0x40 },
}, {
.pixclk = 36000000,
.pll_div_regs = { 0x5a, 0xb0, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 36036000,
.pll_div_regs = { 0x5a, 0xb2, 0xfd, 0x0c, 0x80, 0x40 },
}, {
.pixclk = 40000000,
.pll_div_regs = { 0x64, 0xb0, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 43200000,
.pll_div_regs = { 0x5a, 0x90, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 43243200,
.pll_div_regs = { 0x5a, 0x92, 0xfd, 0x0c, 0x80, 0x40 },
}, {
.pixclk = 44500000,
.pll_div_regs = { 0x5c, 0x92, 0x98, 0x11, 0x84, 0x41 },
}, {
.pixclk = 47000000,
.pll_div_regs = { 0x62, 0x94, 0x95, 0x82, 0x80, 0x40 },
}, {
.pixclk = 47500000,
.pll_div_regs = { 0x63, 0x96, 0xa1, 0x82, 0x80, 0x40 },
}, {
.pixclk = 50349650,
.pll_div_regs = { 0x54, 0x7c, 0xc3, 0x8f, 0x80, 0x40 },
}, {
.pixclk = 50400000,
.pll_div_regs = { 0x54, 0x70, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 53250000,
.pll_div_regs = { 0x58, 0x72, 0x84, 0x03, 0x82, 0x41 },
}, {
.pixclk = 53500000,
.pll_div_regs = { 0x5a, 0x72, 0x89, 0x88, 0x80, 0x40 },
}, {
.pixclk = 54000000,
.pll_div_regs = { 0x5a, 0x70, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 54054000,
.pll_div_regs = { 0x5a, 0x72, 0xfd, 0x0c, 0x80, 0x40 },
}, {
.pixclk = 59000000,
.pll_div_regs = { 0x62, 0x74, 0x95, 0x08, 0x80, 0x40 },
}, {
.pixclk = 59340659,
.pll_div_regs = { 0x62, 0x74, 0xdb, 0x52, 0x88, 0x47 },
}, {
.pixclk = 59400000,
.pll_div_regs = { 0x63, 0x70, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 61500000,
.pll_div_regs = { 0x66, 0x74, 0x82, 0x01, 0x88, 0x45 },
}, {
.pixclk = 63500000,
.pll_div_regs = { 0x69, 0x74, 0x89, 0x08, 0x80, 0x40 },
}, {
.pixclk = 67500000,
.pll_div_regs = { 0x54, 0x52, 0x87, 0x03, 0x80, 0x40 },
}, {
.pixclk = 70000000,
.pll_div_regs = { 0x58, 0x58, 0x8b, 0x88, 0x80, 0x40 },
}, {
.pixclk = 72000000,
.pll_div_regs = { 0x5a, 0x50, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 72072000,
.pll_div_regs = { 0x5a, 0x52, 0xfd, 0x0c, 0x80, 0x40 },
}, {
.pixclk = 74176000,
.pll_div_regs = { 0x5d, 0x58, 0xdb, 0xA2, 0x88, 0x41 },
}, {
.pixclk = 74250000,
.pll_div_regs = { 0x5c, 0x52, 0x90, 0x0d, 0x84, 0x41 },
}, {
.pixclk = 78500000,
.pll_div_regs = { 0x62, 0x54, 0x87, 0x01, 0x80, 0x40 },
}, {
.pixclk = 80000000,
.pll_div_regs = { 0x64, 0x50, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 82000000,
.pll_div_regs = { 0x66, 0x54, 0x82, 0x01, 0x88, 0x45 },
}, {
.pixclk = 82500000,
.pll_div_regs = { 0x67, 0x54, 0x88, 0x01, 0x90, 0x49 },
}, {
.pixclk = 89000000,
.pll_div_regs = { 0x70, 0x54, 0x84, 0x83, 0x80, 0x40 },
}, {
.pixclk = 90000000,
.pll_div_regs = { 0x70, 0x54, 0x82, 0x01, 0x80, 0x40 },
}, {
.pixclk = 94000000,
.pll_div_regs = { 0x4e, 0x32, 0xa7, 0x10, 0x80, 0x40 },
}, {
.pixclk = 95000000,
.pll_div_regs = { 0x50, 0x31, 0x86, 0x85, 0x80, 0x40 },
}, {
.pixclk = 98901099,
.pll_div_regs = { 0x52, 0x3a, 0xdb, 0x4c, 0x88, 0x47 },
}, {
.pixclk = 99000000,
.pll_div_regs = { 0x52, 0x32, 0x82, 0x01, 0x88, 0x47 },
}, {
.pixclk = 100699300,
.pll_div_regs = { 0x54, 0x3c, 0xc3, 0x8f, 0x80, 0x40 },
}, {
.pixclk = 100800000,
.pll_div_regs = { 0x54, 0x30, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 102500000,
.pll_div_regs = { 0x55, 0x32, 0x8c, 0x05, 0x90, 0x4b },
}, {
.pixclk = 104750000,
.pll_div_regs = { 0x57, 0x32, 0x98, 0x07, 0x90, 0x49 },
}, {
.pixclk = 106500000,
.pll_div_regs = { 0x58, 0x32, 0x84, 0x03, 0x82, 0x41 },
}, {
.pixclk = 107000000,
.pll_div_regs = { 0x5a, 0x32, 0x89, 0x88, 0x80, 0x40 },
}, {
.pixclk = 108000000,
.pll_div_regs = { 0x5a, 0x30, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 108108000,
.pll_div_regs = { 0x5a, 0x32, 0xfd, 0x0c, 0x80, 0x40 },
}, {
.pixclk = 118000000,
.pll_div_regs = { 0x62, 0x34, 0x95, 0x08, 0x80, 0x40 },
}, {
.pixclk = 118800000,
.pll_div_regs = { 0x63, 0x30, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 123000000,
.pll_div_regs = { 0x66, 0x34, 0x82, 0x01, 0x88, 0x45 },
}, {
.pixclk = 127000000,
.pll_div_regs = { 0x69, 0x34, 0x89, 0x08, 0x80, 0x40 },
}, {
.pixclk = 135000000,
.pll_div_regs = { 0x70, 0x34, 0x82, 0x01, 0x80, 0x40 },
}, {
.pixclk = 135580000,
.pll_div_regs = { 0x71, 0x39, 0xe9, 0x82, 0x9c, 0x5b },
}, {
.pixclk = 137520000,
.pll_div_regs = { 0x72, 0x38, 0x99, 0x10, 0x85, 0x41 },
}, {
.pixclk = 138750000,
.pll_div_regs = { 0x73, 0x35, 0x88, 0x05, 0x90, 0x4d },
}, {
.pixclk = 140000000,
.pll_div_regs = { 0x75, 0x36, 0xa7, 0x90, 0x80, 0x40 },
}, {
.pixclk = 144000000,
.pll_div_regs = { 0x78, 0x30, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 148352000,
.pll_div_regs = { 0x7b, 0x35, 0xdb, 0x39, 0x90, 0x45 },
}, {
.pixclk = 148500000,
.pll_div_regs = { 0x7b, 0x35, 0x84, 0x03, 0x90, 0x45 },
}, {
.pixclk = 154000000,
.pll_div_regs = { 0x40, 0x18, 0x83, 0x01, 0x00, 0x40 },
}, {
.pixclk = 157000000,
.pll_div_regs = { 0x41, 0x11, 0xa7, 0x14, 0x80, 0x40 },
}, {
.pixclk = 160000000,
.pll_div_regs = { 0x42, 0x12, 0xa1, 0x20, 0x80, 0x40 },
}, {
.pixclk = 162000000,
.pll_div_regs = { 0x43, 0x18, 0x8b, 0x08, 0x96, 0x55 },
}, {
.pixclk = 164000000,
.pll_div_regs = { 0x45, 0x11, 0x83, 0x82, 0x90, 0x4b },
}, {
.pixclk = 165000000,
.pll_div_regs = { 0x45, 0x11, 0x84, 0x81, 0x90, 0x4b },
}, {
.pixclk = 180000000,
.pll_div_regs = { 0x4b, 0x10, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 185625000,
.pll_div_regs = { 0x4e, 0x12, 0x9a, 0x95, 0x80, 0x40 },
}, {
.pixclk = 188000000,
.pll_div_regs = { 0x4e, 0x12, 0xa7, 0x10, 0x80, 0x40 },
}, {
.pixclk = 198000000,
.pll_div_regs = { 0x52, 0x12, 0x82, 0x01, 0x88, 0x47 },
}, {
.pixclk = 205000000,
.pll_div_regs = { 0x55, 0x12, 0x8c, 0x05, 0x90, 0x4b },
}, {
.pixclk = 209500000,
.pll_div_regs = { 0x57, 0x12, 0x98, 0x07, 0x90, 0x49 },
}, {
.pixclk = 213000000,
.pll_div_regs = { 0x58, 0x12, 0x84, 0x03, 0x82, 0x41 },
}, {
.pixclk = 216000000,
.pll_div_regs = { 0x5a, 0x10, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 216216000,
.pll_div_regs = { 0x5a, 0x12, 0xfd, 0x0c, 0x80, 0x40 },
}, {
.pixclk = 237600000,
.pll_div_regs = { 0x63, 0x10, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 254000000,
.pll_div_regs = { 0x69, 0x14, 0x89, 0x08, 0x80, 0x40 },
}, {
.pixclk = 277500000,
.pll_div_regs = { 0x73, 0x15, 0x88, 0x05, 0x90, 0x4d },
}, {
.pixclk = 288000000,
.pll_div_regs = { 0x78, 0x10, 0x00, 0x00, 0x80, 0x00 },
}, {
.pixclk = 297000000,
.pll_div_regs = { 0x7b, 0x15, 0x84, 0x03, 0x90, 0x45 },
},
};
struct reg_settings {
u8 reg;
u8 val;
};
static const struct reg_settings common_phy_cfg[] = {
{ PHY_REG_00, 0x00 }, { PHY_REG_01, 0xd1 },
{ PHY_REG_08, 0x4f }, { PHY_REG_09, 0x30 },
{ PHY_REG_10, 0x33 }, { PHY_REG_11, 0x65 },
/* REG12 pixclk specific */
/* REG13 pixclk specific */
/* REG14 pixclk specific */
{ PHY_REG_15, 0x80 }, { PHY_REG_16, 0x6c },
{ PHY_REG_17, 0xf2 }, { PHY_REG_18, 0x67 },
{ PHY_REG_19, 0x00 }, { PHY_REG_20, 0x10 },
/* REG21 pixclk specific */
{ PHY_REG_22, 0x30 }, { PHY_REG_23, 0x32 },
{ PHY_REG_24, 0x60 }, { PHY_REG_25, 0x8f },
{ PHY_REG_26, 0x00 }, { PHY_REG_27, 0x00 },
{ PHY_REG_28, 0x08 }, { PHY_REG_29, 0x00 },
{ PHY_REG_30, 0x00 }, { PHY_REG_31, 0x00 },
{ PHY_REG_32, 0x00 }, { PHY_REG_33, 0x80 },
{ PHY_REG_34, 0x00 }, { PHY_REG_35, 0x00 },
{ PHY_REG_36, 0x00 }, { PHY_REG_37, 0x00 },
{ PHY_REG_38, 0x00 }, { PHY_REG_39, 0x00 },
{ PHY_REG_40, 0x00 }, { PHY_REG_41, 0xe0 },
{ PHY_REG_42, 0x83 }, { PHY_REG_43, 0x0f },
{ PHY_REG_44, 0x3E }, { PHY_REG_45, 0xf8 },
{ PHY_REG_46, 0x00 }, { PHY_REG_47, 0x00 }
};
struct fsl_samsung_hdmi_phy {
struct device *dev;
void __iomem *regs;
struct clk *apbclk;
struct clk *refclk;
/* clk provider */
struct clk_hw hw;
const struct phy_config *cur_cfg;
};
static inline struct fsl_samsung_hdmi_phy *
to_fsl_samsung_hdmi_phy(struct clk_hw *hw)
{
return container_of(hw, struct fsl_samsung_hdmi_phy, hw);
}
static void
fsl_samsung_hdmi_phy_configure_pixclk(struct fsl_samsung_hdmi_phy *phy,
const struct phy_config *cfg)
{
u8 div = 0x1;
switch (cfg->pixclk) {
case 22250000 ... 33750000:
div = 0xf;
break;
case 35000000 ... 40000000:
div = 0xb;
break;
case 43200000 ... 47500000:
div = 0x9;
break;
case 50349650 ... 63500000:
div = 0x7;
break;
case 67500000 ... 90000000:
div = 0x5;
break;
case 94000000 ... 148500000:
div = 0x3;
break;
case 154000000 ... 297000000:
div = 0x1;
break;
}
writeb(REG21_SEL_TX_CK_INV | FIELD_PREP(REG21_PMS_S_MASK, div),
phy->regs + PHY_REG_21);
}
static void
fsl_samsung_hdmi_phy_configure_pll_lock_det(struct fsl_samsung_hdmi_phy *phy,
const struct phy_config *cfg)
{
u32 pclk = cfg->pixclk;
u32 fld_tg_code;
u32 pclk_khz;
u8 div = 1;
switch (cfg->pixclk) {
case 22250000 ... 47500000:
div = 1;
break;
case 50349650 ... 99000000:
div = 2;
break;
case 100699300 ... 198000000:
div = 4;
break;
case 205000000 ... 297000000:
div = 8;
break;
}
writeb(FIELD_PREP(REG12_CK_DIV_MASK, ilog2(div)), phy->regs + PHY_REG_12);
/*
* Calculation for the frequency lock detector target code (fld_tg_code)
* is based on reference manual register description of PHY_REG13
* (13.10.3.1.14.2):
* 1st) Calculate int_pllclk which is determinded by FLD_CK_DIV
* 2nd) Increase resolution to avoid rounding issues
* 3th) Do the div (256 / Freq. of int_pllclk) * 24
* 4th) Reduce the resolution and always round up since the NXP
* settings rounding up always too. TODO: Check if that is
* correct.
*/
pclk /= div;
pclk_khz = pclk / 1000;
fld_tg_code = 256 * 1000 * 1000 / pclk_khz * 24;
fld_tg_code = DIV_ROUND_UP(fld_tg_code, 1000);
/* FLD_TOL and FLD_RP_CODE taken from downstream driver */
writeb(FIELD_PREP(REG13_TG_CODE_LOW_MASK, fld_tg_code),
phy->regs + PHY_REG_13);
writeb(FIELD_PREP(REG14_TOL_MASK, 2) |
FIELD_PREP(REG14_RP_CODE_MASK, 2) |
FIELD_PREP(REG14_TG_CODE_HIGH_MASK, fld_tg_code >> 8),
phy->regs + PHY_REG_14);
}
static int fsl_samsung_hdmi_phy_configure(struct fsl_samsung_hdmi_phy *phy,
const struct phy_config *cfg)
{
int i, ret;
u8 val;
/* HDMI PHY init */
writeb(REG33_FIX_DA, phy->regs + PHY_REG_33);
/* common PHY registers */
for (i = 0; i < ARRAY_SIZE(common_phy_cfg); i++)
writeb(common_phy_cfg[i].val, phy->regs + common_phy_cfg[i].reg);
/* set individual PLL registers PHY_REG2 ... PHY_REG7 */
for (i = 0; i < PHY_PLL_DIV_REGS_NUM; i++)
writeb(cfg->pll_div_regs[i], phy->regs + PHY_REG_02 + i * 4);
fsl_samsung_hdmi_phy_configure_pixclk(phy, cfg);
fsl_samsung_hdmi_phy_configure_pll_lock_det(phy, cfg);
writeb(REG33_FIX_DA | REG33_MODE_SET_DONE, phy->regs + PHY_REG_33);
ret = readb_poll_timeout(phy->regs + PHY_REG_34, val,
val & REG34_PLL_LOCK, 50, 20000);
if (ret)
dev_err(phy->dev, "PLL failed to lock\n");
return ret;
}
static unsigned long phy_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
if (!phy->cur_cfg)
return 74250000;
return phy->cur_cfg->pixclk;
}
static long phy_clk_round_rate(struct clk_hw *hw,
unsigned long rate, unsigned long *parent_rate)
{
int i;
for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--)
if (phy_pll_cfg[i].pixclk <= rate)
return phy_pll_cfg[i].pixclk;
return -EINVAL;
}
static int phy_clk_set_rate(struct clk_hw *hw,
unsigned long rate, unsigned long parent_rate)
{
struct fsl_samsung_hdmi_phy *phy = to_fsl_samsung_hdmi_phy(hw);
int i;
for (i = ARRAY_SIZE(phy_pll_cfg) - 1; i >= 0; i--)
if (phy_pll_cfg[i].pixclk <= rate)
break;
if (i < 0)
return -EINVAL;
phy->cur_cfg = &phy_pll_cfg[i];
return fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
}
static const struct clk_ops phy_clk_ops = {
.recalc_rate = phy_clk_recalc_rate,
.round_rate = phy_clk_round_rate,
.set_rate = phy_clk_set_rate,
};
static int phy_clk_register(struct fsl_samsung_hdmi_phy *phy)
{
struct device *dev = phy->dev;
struct device_node *np = dev->of_node;
struct clk_init_data init;
const char *parent_name;
struct clk *phyclk;
int ret;
parent_name = __clk_get_name(phy->refclk);
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = 0;
init.name = "hdmi_pclk";
init.ops = &phy_clk_ops;
phy->hw.init = &init;
phyclk = devm_clk_register(dev, &phy->hw);
if (IS_ERR(phyclk))
return dev_err_probe(dev, PTR_ERR(phyclk),
"failed to register clock\n");
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, phyclk);
if (ret)
return dev_err_probe(dev, ret,
"failed to register clock provider\n");
return 0;
}
static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
{
struct fsl_samsung_hdmi_phy *phy;
int ret;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy)
return -ENOMEM;
platform_set_drvdata(pdev, phy);
phy->dev = &pdev->dev;
phy->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy->regs))
return PTR_ERR(phy->regs);
phy->apbclk = devm_clk_get(phy->dev, "apb");
if (IS_ERR(phy->apbclk))
return dev_err_probe(phy->dev, PTR_ERR(phy->apbclk),
"failed to get apb clk\n");
phy->refclk = devm_clk_get(phy->dev, "ref");
if (IS_ERR(phy->refclk))
return dev_err_probe(phy->dev, PTR_ERR(phy->refclk),
"failed to get ref clk\n");
ret = clk_prepare_enable(phy->apbclk);
if (ret) {
dev_err(phy->dev, "failed to enable apbclk\n");
return ret;
}
pm_runtime_get_noresume(phy->dev);
pm_runtime_set_active(phy->dev);
pm_runtime_enable(phy->dev);
ret = phy_clk_register(phy);
if (ret) {
dev_err(&pdev->dev, "register clk failed\n");
goto register_clk_failed;
}
pm_runtime_put(phy->dev);
return 0;
register_clk_failed:
clk_disable_unprepare(phy->apbclk);
return ret;
}
static void fsl_samsung_hdmi_phy_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
}
static int __maybe_unused fsl_samsung_hdmi_phy_suspend(struct device *dev)
{
struct fsl_samsung_hdmi_phy *phy = dev_get_drvdata(dev);
clk_disable_unprepare(phy->apbclk);
return 0;
}
static int __maybe_unused fsl_samsung_hdmi_phy_resume(struct device *dev)
{
struct fsl_samsung_hdmi_phy *phy = dev_get_drvdata(dev);
int ret = 0;
ret = clk_prepare_enable(phy->apbclk);
if (ret) {
dev_err(phy->dev, "failed to enable apbclk\n");
return ret;
}
if (phy->cur_cfg)
ret = fsl_samsung_hdmi_phy_configure(phy, phy->cur_cfg);
return ret;
}
static DEFINE_RUNTIME_DEV_PM_OPS(fsl_samsung_hdmi_phy_pm_ops,
fsl_samsung_hdmi_phy_suspend,
fsl_samsung_hdmi_phy_resume, NULL);
static const struct of_device_id fsl_samsung_hdmi_phy_of_match[] = {
{
.compatible = "fsl,imx8mp-hdmi-phy",
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, fsl_samsung_hdmi_phy_of_match);
static struct platform_driver fsl_samsung_hdmi_phy_driver = {
.probe = fsl_samsung_hdmi_phy_probe,
.remove_new = fsl_samsung_hdmi_phy_remove,
.driver = {
.name = "fsl-samsung-hdmi-phy",
.of_match_table = fsl_samsung_hdmi_phy_of_match,
.pm = pm_ptr(&fsl_samsung_hdmi_phy_pm_ops),
},
};
module_platform_driver(fsl_samsung_hdmi_phy_driver);
MODULE_AUTHOR("Sandor Yu <Sandor.yu@nxp.com>");
MODULE_DESCRIPTION("SAMSUNG HDMI 2.0 Transmitter PHY Driver");
MODULE_LICENSE("GPL");

View file

@ -13,6 +13,17 @@ config PHY_MTK_PCIE
callback for PCIe GEN3 port, it supports software efuse
initialization.
config PHY_MTK_XFI_TPHY
tristate "MediaTek 10GE SerDes XFI T-PHY driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on OF
select GENERIC_PHY
help
Say 'Y' here to add support for MediaTek XFI T-PHY driver.
The driver provides access to the Ethernet SerDes T-PHY supporting
1GE and 2.5GE modes via the LynxI PCS, and 5GE and 10GE modes
via the USXGMII PCS found in MediaTek SoCs with 10G Ethernet.
config PHY_MTK_TPHY
tristate "MediaTek T-PHY Driver"
depends on ARCH_MEDIATEK || COMPILE_TEST

View file

@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-pcie.o
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o
obj-$(CONFIG_PHY_MTK_XFI_TPHY) += phy-mtk-xfi-tphy.o
phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o
phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o

View file

@ -0,0 +1,451 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MediaTek 10GE SerDes XFI T-PHY driver
*
* Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
* Bc-bocun Chen <bc-bocun.chen@mediatek.com>
* based on mtk_usxgmii.c and mtk_sgmii.c found in MediaTek's SDK (GPL-2.0)
* Copyright (c) 2022 MediaTek Inc.
* Author: Henry Yen <henry.yen@mediatek.com>
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
#include "phy-mtk-io.h"
#define MTK_XFI_TPHY_NUM_CLOCKS 2
#define REG_DIG_GLB_70 0x0070
#define XTP_PCS_RX_EQ_IN_PROGRESS(x) FIELD_PREP(GENMASK(25, 24), (x))
#define XTP_PCS_MODE_MASK GENMASK(17, 16)
#define XTP_PCS_MODE(x) FIELD_PREP(GENMASK(17, 16), (x))
#define XTP_PCS_RST_B BIT(15)
#define XTP_FRC_PCS_RST_B BIT(14)
#define XTP_PCS_PWD_SYNC_MASK GENMASK(13, 12)
#define XTP_PCS_PWD_SYNC(x) FIELD_PREP(XTP_PCS_PWD_SYNC_MASK, (x))
#define XTP_PCS_PWD_ASYNC_MASK GENMASK(11, 10)
#define XTP_PCS_PWD_ASYNC(x) FIELD_PREP(XTP_PCS_PWD_ASYNC_MASK, (x))
#define XTP_FRC_PCS_PWD_ASYNC BIT(8)
#define XTP_PCS_UPDT BIT(4)
#define XTP_PCS_IN_FR_RG BIT(0)
#define REG_DIG_GLB_F4 0x00f4
#define XFI_DPHY_PCS_SEL BIT(0)
#define XFI_DPHY_PCS_SEL_SGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 1)
#define XFI_DPHY_PCS_SEL_USXGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 0)
#define XFI_DPHY_AD_SGDT_FRC_EN BIT(5)
#define REG_DIG_LN_TRX_40 0x3040
#define XTP_LN_FRC_TX_DATA_EN BIT(29)
#define XTP_LN_TX_DATA_EN BIT(28)
#define REG_DIG_LN_TRX_B0 0x30b0
#define XTP_LN_FRC_TX_MACCK_EN BIT(5)
#define XTP_LN_TX_MACCK_EN BIT(4)
#define REG_ANA_GLB_D0 0x90d0
#define XTP_GLB_USXGMII_SEL_MASK GENMASK(3, 1)
#define XTP_GLB_USXGMII_SEL(x) FIELD_PREP(GENMASK(3, 1), (x))
#define XTP_GLB_USXGMII_EN BIT(0)
/**
* struct mtk_xfi_tphy - run-time data of the XFI phy instance
* @base: IO memory area to access phy registers.
* @dev: Kernel device used to output prefixed debug info.
* @reset: Reset control corresponding to the phy instance.
* @clocks: All clocks required for the phy to operate.
* @da_war: Enables work-around for 10GBase-R mode.
*/
struct mtk_xfi_tphy {
void __iomem *base;
struct device *dev;
struct reset_control *reset;
struct clk_bulk_data clocks[MTK_XFI_TPHY_NUM_CLOCKS];
bool da_war;
};
/**
* mtk_xfi_tphy_setup() - Setup phy for specified interface mode.
* @xfi_tphy: XFI phy instance.
* @interface: Ethernet interface mode
*
* The setup function is the condensed result of combining the 5 functions which
* setup the phy in MediaTek's GPL licensed public SDK sources. They can be found
* in mtk_sgmii.c[1] as well as mtk_usxgmii.c[2].
*
* Many magic values have been replaced by register and bit definitions, however,
* that has not been possible in all cases. While the vendor driver uses a
* sequence of 32-bit writes, here we try to only modify the actually required
* bits.
*
* [1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/b72d6cba92bf9e29fb035c03052fa1e86664a25b/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_sgmii.c
*
* [2]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/dec96a1d9b82cdcda4a56453fd0b453d4cab4b85/21.02/files/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
*/
static void mtk_xfi_tphy_setup(struct mtk_xfi_tphy *xfi_tphy,
phy_interface_t interface)
{
bool is_1g, is_2p5g, is_5g, is_10g, da_war, use_lynxi_pcs;
/* shorthands for specific clock speeds depending on interface mode */
is_1g = interface == PHY_INTERFACE_MODE_1000BASEX ||
interface == PHY_INTERFACE_MODE_SGMII;
is_2p5g = interface == PHY_INTERFACE_MODE_2500BASEX;
is_5g = interface == PHY_INTERFACE_MODE_5GBASER;
is_10g = interface == PHY_INTERFACE_MODE_10GBASER ||
interface == PHY_INTERFACE_MODE_USXGMII;
/* Is overriding 10GBase-R tuning value required? */
da_war = xfi_tphy->da_war && (interface == PHY_INTERFACE_MODE_10GBASER);
/* configure input mux to either
* - USXGMII PCS (64b/66b coding) for 5G/10G
* - LynxI PCS (8b/10b coding) for 1G/2.5G
*/
use_lynxi_pcs = is_1g || is_2p5g;
dev_dbg(xfi_tphy->dev, "setting up for mode %s\n", phy_modes(interface));
/* Setup PLL setting */
mtk_phy_update_bits(xfi_tphy->base + 0x9024, 0x100000, is_10g ? 0x0 : 0x100000);
mtk_phy_update_bits(xfi_tphy->base + 0x2020, 0x202000, is_5g ? 0x202000 : 0x0);
mtk_phy_update_bits(xfi_tphy->base + 0x2030, 0x500, is_1g ? 0x0 : 0x500);
mtk_phy_update_bits(xfi_tphy->base + 0x2034, 0xa00, is_1g ? 0x0 : 0xa00);
mtk_phy_update_bits(xfi_tphy->base + 0x2040, 0x340000, is_1g ? 0x200000 : 0x140000);
/* Setup RXFE BW setting */
mtk_phy_update_bits(xfi_tphy->base + 0x50f0, 0xc10, is_1g ? 0x410 : is_5g ? 0x800 : 0x400);
mtk_phy_update_bits(xfi_tphy->base + 0x50e0, 0x4000, is_5g ? 0x0 : 0x4000);
/* Setup RX CDR setting */
mtk_phy_update_bits(xfi_tphy->base + 0x506c, 0x30000, is_5g ? 0x0 : 0x30000);
mtk_phy_update_bits(xfi_tphy->base + 0x5070, 0x670000, is_5g ? 0x620000 : 0x50000);
mtk_phy_update_bits(xfi_tphy->base + 0x5074, 0x180000, is_5g ? 0x180000 : 0x0);
mtk_phy_update_bits(xfi_tphy->base + 0x5078, 0xf000400, is_5g ? 0x8000000 :
0x7000400);
mtk_phy_update_bits(xfi_tphy->base + 0x507c, 0x5000500, is_5g ? 0x4000400 :
0x1000100);
mtk_phy_update_bits(xfi_tphy->base + 0x5080, 0x1410, is_1g ? 0x400 : is_5g ? 0x1010 : 0x0);
mtk_phy_update_bits(xfi_tphy->base + 0x5084, 0x30300, is_1g ? 0x30300 :
is_5g ? 0x30100 :
0x100);
mtk_phy_update_bits(xfi_tphy->base + 0x5088, 0x60200, is_1g ? 0x20200 :
is_5g ? 0x40000 :
0x20000);
/* Setting RXFE adaptation range setting */
mtk_phy_update_bits(xfi_tphy->base + 0x50e4, 0xc0000, is_5g ? 0x0 : 0xc0000);
mtk_phy_update_bits(xfi_tphy->base + 0x50e8, 0x40000, is_5g ? 0x0 : 0x40000);
mtk_phy_update_bits(xfi_tphy->base + 0x50ec, 0xa00, is_1g ? 0x200 : 0x800);
mtk_phy_update_bits(xfi_tphy->base + 0x50a8, 0xee0000, is_5g ? 0x800000 :
0x6e0000);
mtk_phy_update_bits(xfi_tphy->base + 0x6004, 0x190000, is_5g ? 0x0 : 0x190000);
if (is_10g)
writel(0x01423342, xfi_tphy->base + 0x00f8);
else if (is_5g)
writel(0x00a132a1, xfi_tphy->base + 0x00f8);
else if (is_2p5g)
writel(0x009c329c, xfi_tphy->base + 0x00f8);
else
writel(0x00fa32fa, xfi_tphy->base + 0x00f8);
/* Force SGDT_OUT off and select PCS */
mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_F4,
XFI_DPHY_AD_SGDT_FRC_EN | XFI_DPHY_PCS_SEL,
XFI_DPHY_AD_SGDT_FRC_EN |
(use_lynxi_pcs ? XFI_DPHY_PCS_SEL_SGMII :
XFI_DPHY_PCS_SEL_USXGMII));
/* Force GLB_CKDET_OUT */
mtk_phy_set_bits(xfi_tphy->base + 0x0030, 0xc00);
/* Force AEQ on */
writel(XTP_PCS_RX_EQ_IN_PROGRESS(2) | XTP_PCS_PWD_SYNC(2) | XTP_PCS_PWD_ASYNC(2),
xfi_tphy->base + REG_DIG_GLB_70);
usleep_range(1, 5);
writel(XTP_LN_FRC_TX_DATA_EN, xfi_tphy->base + REG_DIG_LN_TRX_40);
/* Setup TX DA default value */
mtk_phy_update_bits(xfi_tphy->base + 0x30b0, 0x30, 0x20);
writel(0x00008a01, xfi_tphy->base + 0x3028);
writel(0x0000a884, xfi_tphy->base + 0x302c);
writel(0x00083002, xfi_tphy->base + 0x3024);
/* Setup RG default value */
if (use_lynxi_pcs) {
writel(0x00011110, xfi_tphy->base + 0x3010);
writel(0x40704000, xfi_tphy->base + 0x3048);
} else {
writel(0x00022220, xfi_tphy->base + 0x3010);
writel(0x0f020a01, xfi_tphy->base + 0x5064);
writel(0x06100600, xfi_tphy->base + 0x50b4);
if (interface == PHY_INTERFACE_MODE_USXGMII)
writel(0x40704000, xfi_tphy->base + 0x3048);
else
writel(0x47684100, xfi_tphy->base + 0x3048);
}
if (is_1g)
writel(0x0000c000, xfi_tphy->base + 0x3064);
/* Setup RX EQ initial value */
mtk_phy_update_bits(xfi_tphy->base + 0x3050, 0xa8000000,
(interface != PHY_INTERFACE_MODE_10GBASER) ? 0xa8000000 : 0x0);
mtk_phy_update_bits(xfi_tphy->base + 0x3054, 0xaa,
(interface != PHY_INTERFACE_MODE_10GBASER) ? 0xaa : 0x0);
if (!use_lynxi_pcs)
writel(0x00000f00, xfi_tphy->base + 0x306c);
else if (is_2p5g)
writel(0x22000f00, xfi_tphy->base + 0x306c);
else
writel(0x20200f00, xfi_tphy->base + 0x306c);
mtk_phy_update_bits(xfi_tphy->base + 0xa008, 0x10000, da_war ? 0x10000 : 0x0);
mtk_phy_update_bits(xfi_tphy->base + 0xa060, 0x50000, use_lynxi_pcs ? 0x50000 : 0x40000);
/* Setup PHYA speed */
mtk_phy_update_bits(xfi_tphy->base + REG_ANA_GLB_D0,
XTP_GLB_USXGMII_SEL_MASK | XTP_GLB_USXGMII_EN,
is_10g ? XTP_GLB_USXGMII_SEL(0) :
is_5g ? XTP_GLB_USXGMII_SEL(1) :
is_2p5g ? XTP_GLB_USXGMII_SEL(2) :
XTP_GLB_USXGMII_SEL(3));
mtk_phy_set_bits(xfi_tphy->base + REG_ANA_GLB_D0, XTP_GLB_USXGMII_EN);
/* Release reset */
mtk_phy_set_bits(xfi_tphy->base + REG_DIG_GLB_70,
XTP_PCS_RST_B | XTP_FRC_PCS_RST_B);
usleep_range(150, 500);
/* Switch to P0 */
mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70,
XTP_PCS_IN_FR_RG |
XTP_FRC_PCS_PWD_ASYNC |
XTP_PCS_PWD_ASYNC_MASK |
XTP_PCS_PWD_SYNC_MASK |
XTP_PCS_UPDT,
XTP_PCS_IN_FR_RG |
XTP_FRC_PCS_PWD_ASYNC |
XTP_PCS_UPDT);
usleep_range(1, 5);
mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_70, XTP_PCS_UPDT);
usleep_range(15, 50);
if (use_lynxi_pcs) {
/* Switch to Gen2 */
mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70,
XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
XTP_PCS_MODE(1) | XTP_PCS_UPDT);
} else {
/* Switch to Gen3 */
mtk_phy_update_bits(xfi_tphy->base + REG_DIG_GLB_70,
XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
XTP_PCS_MODE(2) | XTP_PCS_UPDT);
}
usleep_range(1, 5);
mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_70, XTP_PCS_UPDT);
usleep_range(100, 500);
/* Enable MAC CK */
mtk_phy_set_bits(xfi_tphy->base + REG_DIG_LN_TRX_B0, XTP_LN_TX_MACCK_EN);
mtk_phy_clear_bits(xfi_tphy->base + REG_DIG_GLB_F4, XFI_DPHY_AD_SGDT_FRC_EN);
/* Enable TX data */
mtk_phy_set_bits(xfi_tphy->base + REG_DIG_LN_TRX_40,
XTP_LN_FRC_TX_DATA_EN | XTP_LN_TX_DATA_EN);
usleep_range(400, 1000);
}
/**
* mtk_xfi_tphy_set_mode() - Setup phy for specified interface mode.
*
* @phy: Phy instance.
* @mode: Only PHY_MODE_ETHERNET is supported.
* @submode: An Ethernet interface mode.
*
* Validate selected mode and call function mtk_xfi_tphy_setup().
*
* Return:
* * %0 - OK
* * %-EINVAL - invalid mode
*/
static int mtk_xfi_tphy_set_mode(struct phy *phy, enum phy_mode mode, int
submode)
{
struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
if (mode != PHY_MODE_ETHERNET)
return -EINVAL;
switch (submode) {
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_5GBASER:
case PHY_INTERFACE_MODE_10GBASER:
case PHY_INTERFACE_MODE_USXGMII:
mtk_xfi_tphy_setup(xfi_tphy, submode);
return 0;
default:
return -EINVAL;
}
}
/**
* mtk_xfi_tphy_reset() - Reset the phy.
*
* @phy: Phy instance.
*
* Reset the phy using the external reset controller.
*
* Return:
* %0 - OK
*/
static int mtk_xfi_tphy_reset(struct phy *phy)
{
struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
reset_control_assert(xfi_tphy->reset);
usleep_range(100, 500);
reset_control_deassert(xfi_tphy->reset);
usleep_range(1, 10);
return 0;
}
/**
* mtk_xfi_tphy_power_on() - Power-on the phy.
*
* @phy: Phy instance.
*
* Prepare and enable all clocks required for the phy to operate.
*
* Return:
* See clk_bulk_prepare_enable().
*/
static int mtk_xfi_tphy_power_on(struct phy *phy)
{
struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
return clk_bulk_prepare_enable(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
}
/**
* mtk_xfi_tphy_power_off() - Power-off the phy.
*
* @phy: Phy instance.
*
* Disable and unprepare all clocks previously enabled.
*
* Return:
* See clk_bulk_prepare_disable().
*/
static int mtk_xfi_tphy_power_off(struct phy *phy)
{
struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
clk_bulk_disable_unprepare(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
return 0;
}
static const struct phy_ops mtk_xfi_tphy_ops = {
.power_on = mtk_xfi_tphy_power_on,
.power_off = mtk_xfi_tphy_power_off,
.set_mode = mtk_xfi_tphy_set_mode,
.reset = mtk_xfi_tphy_reset,
.owner = THIS_MODULE,
};
/**
* mtk_xfi_tphy_probe() - Probe phy instance from Device Tree.
* @pdev: Matching platform device.
*
* The probe function gets IO resource, clocks, reset controller and
* whether the DA work-around for 10GBase-R is required from Device Tree and
* allocates memory for holding that information in a struct mtk_xfi_tphy.
*
* Return:
* * %0 - OK
* * %-ENODEV - Missing associated Device Tree node (should never happen).
* * %-ENOMEM - Out of memory.
* * Any error value which devm_platform_ioremap_resource(),
* devm_clk_bulk_get(), devm_reset_control_get_exclusive(),
* devm_phy_create() or devm_of_phy_provider_register() may return.
*/
static int mtk_xfi_tphy_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct phy_provider *phy_provider;
struct mtk_xfi_tphy *xfi_tphy;
struct phy *phy;
int ret;
if (!np)
return -ENODEV;
xfi_tphy = devm_kzalloc(&pdev->dev, sizeof(*xfi_tphy), GFP_KERNEL);
if (!xfi_tphy)
return -ENOMEM;
xfi_tphy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(xfi_tphy->base))
return PTR_ERR(xfi_tphy->base);
xfi_tphy->dev = &pdev->dev;
xfi_tphy->clocks[0].id = "topxtal";
xfi_tphy->clocks[1].id = "xfipll";
ret = devm_clk_bulk_get(&pdev->dev, MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
if (ret)
return ret;
xfi_tphy->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(xfi_tphy->reset))
return PTR_ERR(xfi_tphy->reset);
xfi_tphy->da_war = of_property_read_bool(np, "mediatek,usxgmii-performance-errata");
phy = devm_phy_create(&pdev->dev, NULL, &mtk_xfi_tphy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, xfi_tphy);
phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id mtk_xfi_tphy_match[] = {
{ .compatible = "mediatek,mt7988-xfi-tphy", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_xfi_tphy_match);
static struct platform_driver mtk_xfi_tphy_driver = {
.probe = mtk_xfi_tphy_probe,
.driver = {
.name = "mtk-xfi-tphy",
.of_match_table = mtk_xfi_tphy_match,
},
};
module_platform_driver(mtk_xfi_tphy_driver);
MODULE_DESCRIPTION("MediaTek 10GE SerDes XFI T-PHY driver");
MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
MODULE_AUTHOR("Bc-bocun Chen <bc-bocun.chen@mediatek.com>");
MODULE_LICENSE("GPL");

View file

@ -20,7 +20,12 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
static struct class *phy_class;
static void phy_release(struct device *dev);
static const struct class phy_class = {
.name = "phy",
.dev_release = phy_release,
};
static struct dentry *phy_debugfs_root;
static DEFINE_MUTEX(phy_provider_mutex);
static LIST_HEAD(phy_provider_list);
@ -753,7 +758,7 @@ struct phy *of_phy_simple_xlate(struct device *dev,
struct phy *phy;
struct class_dev_iter iter;
class_dev_iter_init(&iter, phy_class, NULL, NULL);
class_dev_iter_init(&iter, &phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev);
if (args->np != phy->dev.of_node)
@ -1016,7 +1021,7 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
device_initialize(&phy->dev);
mutex_init(&phy->mutex);
phy->dev.class = phy_class;
phy->dev.class = &phy_class;
phy->dev.parent = dev;
phy->dev.of_node = node ?: dev->of_node;
phy->id = id;
@ -1285,14 +1290,13 @@ static void phy_release(struct device *dev)
static int __init phy_core_init(void)
{
phy_class = class_create("phy");
if (IS_ERR(phy_class)) {
pr_err("failed to create phy class --> %ld\n",
PTR_ERR(phy_class));
return PTR_ERR(phy_class);
}
int err;
phy_class->dev_release = phy_release;
err = class_register(&phy_class);
if (err) {
pr_err("failed to register phy class");
return err;
}
phy_debugfs_root = debugfs_create_dir("phy", NULL);
@ -1303,6 +1307,6 @@ device_initcall(phy_core_init);
static void __exit phy_core_exit(void)
{
debugfs_remove_recursive(phy_debugfs_root);
class_destroy(phy_class);
class_unregister(&phy_class);
}
module_exit(phy_core_exit);

View file

@ -24,6 +24,7 @@
#include "phy-qcom-qmp-dp-phy.h"
#include "phy-qcom-qmp-qserdes-com-v4.h"
#include "phy-qcom-qmp-qserdes-com-v6.h"
/* EDP_PHY registers */
#define DP_PHY_CFG 0x0010
@ -77,9 +78,20 @@ struct qcom_edp_swing_pre_emph_cfg {
const u8 (*pre_emphasis_hbr3_hbr2)[4][4];
};
struct qcom_edp;
struct phy_ver_ops {
int (*com_power_on)(const struct qcom_edp *edp);
int (*com_resetsm_cntrl)(const struct qcom_edp *edp);
int (*com_bias_en_clkbuflr)(const struct qcom_edp *edp);
int (*com_configure_pll)(const struct qcom_edp *edp);
int (*com_configure_ssc)(const struct qcom_edp *edp);
};
struct qcom_edp_phy_cfg {
bool is_edp;
const struct qcom_edp_swing_pre_emph_cfg *swing_pre_emph_cfg;
const struct phy_ver_ops *ver_ops;
};
struct qcom_edp {
@ -174,18 +186,6 @@ static const struct qcom_edp_swing_pre_emph_cfg edp_phy_swing_pre_emph_cfg = {
.pre_emphasis_hbr3_hbr2 = &edp_pre_emp_hbr2_hbr3,
};
static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = {
};
static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = {
.swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
};
static const struct qcom_edp_phy_cfg sc8280xp_edp_phy_cfg = {
.is_edp = true,
.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
};
static int qcom_edp_phy_init(struct phy *phy)
{
struct qcom_edp *edp = phy_get_drvdata(phy);
@ -204,8 +204,9 @@ static int qcom_edp_phy_init(struct phy *phy)
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
edp->edp + DP_PHY_PD_CTL);
/* Turn on BIAS current for PHY/PLL */
writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN);
ret = edp->cfg->ver_ops->com_bias_en_clkbuflr(edp);
if (ret)
return ret;
writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL);
msleep(20);
@ -312,6 +313,84 @@ static int qcom_edp_phy_configure(struct phy *phy, union phy_configure_opts *opt
}
static int qcom_edp_configure_ssc(const struct qcom_edp *edp)
{
return edp->cfg->ver_ops->com_configure_ssc(edp);
}
static int qcom_edp_configure_pll(const struct qcom_edp *edp)
{
return edp->cfg->ver_ops->com_configure_pll(edp);
}
static int qcom_edp_set_vco_div(const struct qcom_edp *edp, unsigned long *pixel_freq)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 vco_div;
switch (dp_opts->link_rate) {
case 1620:
vco_div = 0x1;
*pixel_freq = 1620000000UL / 2;
break;
case 2700:
vco_div = 0x1;
*pixel_freq = 2700000000UL / 2;
break;
case 5400:
vco_div = 0x2;
*pixel_freq = 5400000000UL / 4;
break;
case 8100:
vco_div = 0x0;
*pixel_freq = 8100000000UL / 6;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(vco_div, edp->edp + DP_PHY_VCO_DIV);
return 0;
}
static int qcom_edp_phy_power_on_v4(const struct qcom_edp *edp)
{
u32 val;
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
edp->edp + DP_PHY_PD_CTL);
writel(0xfc, edp->edp + DP_PHY_MODE);
return readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS,
val, val & BIT(7), 5, 200);
}
static int qcom_edp_phy_com_resetsm_cntrl_v4(const struct qcom_edp *edp)
{
u32 val;
writel(0x20, edp->pll + QSERDES_V4_COM_RESETSM_CNTRL);
return readl_poll_timeout(edp->pll + QSERDES_V4_COM_C_READY_STATUS,
val, val & BIT(0), 500, 10000);
}
static int qcom_edp_com_bias_en_clkbuflr_v4(const struct qcom_edp *edp)
{
/* Turn on BIAS current for PHY/PLL */
writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN);
return 0;
}
static int qcom_edp_com_configure_ssc_v4(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 step1;
@ -345,7 +424,7 @@ static int qcom_edp_configure_ssc(const struct qcom_edp *edp)
return 0;
}
static int qcom_edp_configure_pll(const struct qcom_edp *edp)
static int qcom_edp_com_configure_pll_v4(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 div_frac_start2_mode0;
@ -431,52 +510,32 @@ static int qcom_edp_configure_pll(const struct qcom_edp *edp)
return 0;
}
static int qcom_edp_set_vco_div(const struct qcom_edp *edp, unsigned long *pixel_freq)
static const struct phy_ver_ops qcom_edp_phy_ops_v4 = {
.com_power_on = qcom_edp_phy_power_on_v4,
.com_resetsm_cntrl = qcom_edp_phy_com_resetsm_cntrl_v4,
.com_bias_en_clkbuflr = qcom_edp_com_bias_en_clkbuflr_v4,
.com_configure_pll = qcom_edp_com_configure_pll_v4,
.com_configure_ssc = qcom_edp_com_configure_ssc_v4,
};
static const struct qcom_edp_phy_cfg sc7280_dp_phy_cfg = {
.ver_ops = &qcom_edp_phy_ops_v4,
};
static const struct qcom_edp_phy_cfg sc8280xp_dp_phy_cfg = {
.swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
.ver_ops = &qcom_edp_phy_ops_v4,
};
static const struct qcom_edp_phy_cfg sc8280xp_edp_phy_cfg = {
.is_edp = true,
.swing_pre_emph_cfg = &edp_phy_swing_pre_emph_cfg,
.ver_ops = &qcom_edp_phy_ops_v4,
};
static int qcom_edp_phy_power_on_v6(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 vco_div;
switch (dp_opts->link_rate) {
case 1620:
vco_div = 0x1;
*pixel_freq = 1620000000UL / 2;
break;
case 2700:
vco_div = 0x1;
*pixel_freq = 2700000000UL / 2;
break;
case 5400:
vco_div = 0x2;
*pixel_freq = 5400000000UL / 4;
break;
case 8100:
vco_div = 0x0;
*pixel_freq = 8100000000UL / 6;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(vco_div, edp->edp + DP_PHY_VCO_DIV);
return 0;
}
static int qcom_edp_phy_power_on(struct phy *phy)
{
const struct qcom_edp *edp = phy_get_drvdata(phy);
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
unsigned long pixel_freq;
u8 ldo_config = 0x0;
int timeout;
int ret;
u32 val;
u8 cfg1;
writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN |
@ -484,11 +543,187 @@ static int qcom_edp_phy_power_on(struct phy *phy)
edp->edp + DP_PHY_PD_CTL);
writel(0xfc, edp->edp + DP_PHY_MODE);
timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS,
return readl_poll_timeout(edp->pll + QSERDES_V6_COM_CMN_STATUS,
val, val & BIT(7), 5, 200);
if (timeout)
return timeout;
}
static int qcom_edp_phy_com_resetsm_cntrl_v6(const struct qcom_edp *edp)
{
u32 val;
writel(0x20, edp->pll + QSERDES_V6_COM_RESETSM_CNTRL);
return readl_poll_timeout(edp->pll + QSERDES_V6_COM_C_READY_STATUS,
val, val & BIT(0), 500, 10000);
}
static int qcom_edp_com_bias_en_clkbuflr_v6(const struct qcom_edp *edp)
{
/* Turn on BIAS current for PHY/PLL */
writel(0x1f, edp->pll + QSERDES_V6_COM_PLL_BIAS_EN_CLK_BUFLR_EN);
return 0;
}
static int qcom_edp_com_configure_ssc_v6(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 step1;
u32 step2;
switch (dp_opts->link_rate) {
case 1620:
case 2700:
case 8100:
step1 = 0x92;
step2 = 0x01;
break;
case 5400:
step1 = 0x18;
step2 = 0x02;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(0x01, edp->pll + QSERDES_V6_COM_SSC_EN_CENTER);
writel(0x00, edp->pll + QSERDES_V6_COM_SSC_ADJ_PER1);
writel(0x36, edp->pll + QSERDES_V6_COM_SSC_PER1);
writel(0x01, edp->pll + QSERDES_V6_COM_SSC_PER2);
writel(step1, edp->pll + QSERDES_V6_COM_SSC_STEP_SIZE1_MODE0);
writel(step2, edp->pll + QSERDES_V6_COM_SSC_STEP_SIZE2_MODE0);
return 0;
}
static int qcom_edp_com_configure_pll_v6(const struct qcom_edp *edp)
{
const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts;
u32 div_frac_start2_mode0;
u32 div_frac_start3_mode0;
u32 dec_start_mode0;
u32 lock_cmp1_mode0;
u32 lock_cmp2_mode0;
u32 code1_mode0;
u32 code2_mode0;
u32 hsclk_sel;
switch (dp_opts->link_rate) {
case 1620:
hsclk_sel = 0x5;
dec_start_mode0 = 0x34;
div_frac_start2_mode0 = 0xc0;
div_frac_start3_mode0 = 0x0b;
lock_cmp1_mode0 = 0x37;
lock_cmp2_mode0 = 0x04;
code1_mode0 = 0x71;
code2_mode0 = 0x0c;
break;
case 2700:
hsclk_sel = 0x3;
dec_start_mode0 = 0x34;
div_frac_start2_mode0 = 0xc0;
div_frac_start3_mode0 = 0x0b;
lock_cmp1_mode0 = 0x07;
lock_cmp2_mode0 = 0x07;
code1_mode0 = 0x71;
code2_mode0 = 0x0c;
break;
case 5400:
hsclk_sel = 0x1;
dec_start_mode0 = 0x46;
div_frac_start2_mode0 = 0x00;
div_frac_start3_mode0 = 0x05;
lock_cmp1_mode0 = 0x0f;
lock_cmp2_mode0 = 0x0e;
code1_mode0 = 0x97;
code2_mode0 = 0x10;
break;
case 8100:
hsclk_sel = 0x0;
dec_start_mode0 = 0x34;
div_frac_start2_mode0 = 0xc0;
div_frac_start3_mode0 = 0x0b;
lock_cmp1_mode0 = 0x17;
lock_cmp2_mode0 = 0x15;
code1_mode0 = 0x71;
code2_mode0 = 0x0c;
break;
default:
/* Other link rates aren't supported */
return -EINVAL;
}
writel(0x01, edp->pll + QSERDES_V6_COM_SVS_MODE_CLK_SEL);
writel(0x0b, edp->pll + QSERDES_V6_COM_SYSCLK_EN_SEL);
writel(0x02, edp->pll + QSERDES_V6_COM_SYS_CLK_CTRL);
writel(0x0c, edp->pll + QSERDES_V6_COM_CLK_ENABLE1);
writel(0x06, edp->pll + QSERDES_V6_COM_SYSCLK_BUF_ENABLE);
writel(0x30, edp->pll + QSERDES_V6_COM_CLK_SELECT);
writel(hsclk_sel, edp->pll + QSERDES_V6_COM_HSCLK_SEL_1);
writel(0x07, edp->pll + QSERDES_V6_COM_PLL_IVCO);
writel(0x08, edp->pll + QSERDES_V6_COM_LOCK_CMP_EN);
writel(0x36, edp->pll + QSERDES_V6_COM_PLL_CCTRL_MODE0);
writel(0x16, edp->pll + QSERDES_V6_COM_PLL_RCTRL_MODE0);
writel(0x06, edp->pll + QSERDES_V6_COM_CP_CTRL_MODE0);
writel(dec_start_mode0, edp->pll + QSERDES_V6_COM_DEC_START_MODE0);
writel(0x00, edp->pll + QSERDES_V6_COM_DIV_FRAC_START1_MODE0);
writel(div_frac_start2_mode0, edp->pll + QSERDES_V6_COM_DIV_FRAC_START2_MODE0);
writel(div_frac_start3_mode0, edp->pll + QSERDES_V6_COM_DIV_FRAC_START3_MODE0);
writel(0x12, edp->pll + QSERDES_V6_COM_CMN_CONFIG_1);
writel(0x3f, edp->pll + QSERDES_V6_COM_INTEGLOOP_GAIN0_MODE0);
writel(0x00, edp->pll + QSERDES_V6_COM_INTEGLOOP_GAIN1_MODE0);
writel(0x00, edp->pll + QSERDES_V6_COM_VCO_TUNE_MAP);
writel(lock_cmp1_mode0, edp->pll + QSERDES_V6_COM_LOCK_CMP1_MODE0);
writel(lock_cmp2_mode0, edp->pll + QSERDES_V6_COM_LOCK_CMP2_MODE0);
writel(0x0a, edp->pll + QSERDES_V6_COM_BG_TIMER);
writel(0x14, edp->pll + QSERDES_V6_COM_PLL_CORE_CLK_DIV_MODE0);
writel(0x00, edp->pll + QSERDES_V6_COM_VCO_TUNE_CTRL);
writel(0x1f, edp->pll + QSERDES_V6_COM_PLL_BIAS_EN_CLK_BUFLR_EN);
writel(0x0f, edp->pll + QSERDES_V6_COM_CORE_CLK_EN);
writel(0xa0, edp->pll + QSERDES_V6_COM_VCO_TUNE1_MODE0);
writel(0x03, edp->pll + QSERDES_V6_COM_VCO_TUNE2_MODE0);
writel(code1_mode0, edp->pll + QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0);
writel(code2_mode0, edp->pll + QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0);
return 0;
}
static const struct phy_ver_ops qcom_edp_phy_ops_v6 = {
.com_power_on = qcom_edp_phy_power_on_v6,
.com_resetsm_cntrl = qcom_edp_phy_com_resetsm_cntrl_v6,
.com_bias_en_clkbuflr = qcom_edp_com_bias_en_clkbuflr_v6,
.com_configure_pll = qcom_edp_com_configure_pll_v6,
.com_configure_ssc = qcom_edp_com_configure_ssc_v6,
};
static struct qcom_edp_phy_cfg x1e80100_phy_cfg = {
.swing_pre_emph_cfg = &dp_phy_swing_pre_emph_cfg,
.ver_ops = &qcom_edp_phy_ops_v6,
};
static int qcom_edp_phy_power_on(struct phy *phy)
{
const struct qcom_edp *edp = phy_get_drvdata(phy);
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
unsigned long pixel_freq;
u8 ldo_config = 0x0;
int ret;
u32 val;
u8 cfg1;
ret = edp->cfg->ver_ops->com_power_on(edp);
if (ret)
return ret;
if (edp->cfg->swing_pre_emph_cfg && !edp->is_edp)
ldo_config = 0x1;
@ -535,12 +770,9 @@ static int qcom_edp_phy_power_on(struct phy *phy)
writel(0x01, edp->edp + DP_PHY_CFG);
writel(0x09, edp->edp + DP_PHY_CFG);
writel(0x20, edp->pll + QSERDES_V4_COM_RESETSM_CNTRL);
timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_C_READY_STATUS,
val, val & BIT(0), 500, 10000);
if (timeout)
return timeout;
ret = edp->cfg->ver_ops->com_resetsm_cntrl(edp);
if (ret)
return ret;
writel(0x19, edp->edp + DP_PHY_CFG);
writel(0x1f, edp->tx0 + TXn_HIGHZ_DRVR_EN);
@ -880,6 +1112,7 @@ static const struct of_device_id qcom_edp_phy_match_table[] = {
{ .compatible = "qcom,sc8180x-edp-phy", .data = &sc7280_dp_phy_cfg, },
{ .compatible = "qcom,sc8280xp-dp-phy", .data = &sc8280xp_dp_phy_cfg, },
{ .compatible = "qcom,sc8280xp-edp-phy", .data = &sc8280xp_edp_phy_cfg, },
{ .compatible = "qcom,x1e80100-dp-phy", .data = &x1e80100_phy_cfg, },
{ }
};
MODULE_DEVICE_TABLE(of, qcom_edp_phy_match_table);

View file

@ -88,6 +88,12 @@ static const u32 pm8550b_init_tbl[NUM_TUNE_FIELDS] = {
[TUNE_USB2_PREEM] = 0x5,
};
static const u32 smb2360_init_tbl[NUM_TUNE_FIELDS] = {
[TUNE_IUSB2] = 0x5,
[TUNE_SQUELCH_U] = 0x3,
[TUNE_USB2_PREEM] = 0x2,
};
static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = {
.init_tbl = pm8550b_init_tbl,
.init_tbl_num = ARRAY_SIZE(pm8550b_init_tbl),
@ -95,6 +101,13 @@ static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = {
.num_vregs = ARRAY_SIZE(pm8550b_vreg_l),
};
static const struct eusb2_repeater_cfg smb2360_eusb2_cfg = {
.init_tbl = smb2360_init_tbl,
.init_tbl_num = ARRAY_SIZE(smb2360_init_tbl),
.vreg_list = pm8550b_vreg_l,
.num_vregs = ARRAY_SIZE(pm8550b_vreg_l),
};
static int eusb2_repeater_init_vregs(struct eusb2_repeater *rptr)
{
int num = rptr->cfg->num_vregs;
@ -271,6 +284,10 @@ static const struct of_device_id eusb2_repeater_of_match_table[] = {
.compatible = "qcom,pm8550b-eusb2-repeater",
.data = &pm8550b_eusb2_cfg,
},
{
.compatible = "qcom,smb2360-eusb2-repeater",
.data = &smb2360_eusb2_cfg,
},
{ },
};
MODULE_DEVICE_TABLE(of, eusb2_repeater_of_match_table);

View file

@ -1382,6 +1382,13 @@ static const u8 qmp_dp_v5_voltage_swing_hbr_rbr[4][4] = {
{ 0x3f, 0xff, 0xff, 0xff }
};
static const u8 qmp_dp_v6_voltage_swing_hbr_rbr[4][4] = {
{ 0x27, 0x2f, 0x36, 0x3f },
{ 0x31, 0x3e, 0x3f, 0xff },
{ 0x36, 0x3f, 0xff, 0xff },
{ 0x3f, 0xff, 0xff, 0xff }
};
static const u8 qmp_dp_v6_pre_emphasis_hbr_rbr[4][4] = {
{ 0x20, 0x2d, 0x34, 0x3a },
{ 0x20, 0x2e, 0x35, 0xff },
@ -2001,6 +2008,51 @@ static const struct qmp_phy_cfg sm8550_usb3dpphy_cfg = {
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
};
static const struct qmp_phy_cfg sm8650_usb3dpphy_cfg = {
.offsets = &qmp_combo_offsets_v3,
.serdes_tbl = sm8550_usb3_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(sm8550_usb3_serdes_tbl),
.tx_tbl = sm8550_usb3_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(sm8550_usb3_tx_tbl),
.rx_tbl = sm8550_usb3_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(sm8550_usb3_rx_tbl),
.pcs_tbl = sm8550_usb3_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_tbl),
.pcs_usb_tbl = sm8550_usb3_pcs_usb_tbl,
.pcs_usb_tbl_num = ARRAY_SIZE(sm8550_usb3_pcs_usb_tbl),
.dp_serdes_tbl = qmp_v6_dp_serdes_tbl,
.dp_serdes_tbl_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl),
.dp_tx_tbl = qmp_v6_dp_tx_tbl,
.dp_tx_tbl_num = ARRAY_SIZE(qmp_v6_dp_tx_tbl),
.serdes_tbl_rbr = qmp_v6_dp_serdes_tbl_rbr,
.serdes_tbl_rbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_rbr),
.serdes_tbl_hbr = qmp_v6_dp_serdes_tbl_hbr,
.serdes_tbl_hbr_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr),
.serdes_tbl_hbr2 = qmp_v6_dp_serdes_tbl_hbr2,
.serdes_tbl_hbr2_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr2),
.serdes_tbl_hbr3 = qmp_v6_dp_serdes_tbl_hbr3,
.serdes_tbl_hbr3_num = ARRAY_SIZE(qmp_v6_dp_serdes_tbl_hbr3),
.swing_hbr_rbr = &qmp_dp_v6_voltage_swing_hbr_rbr,
.pre_emphasis_hbr_rbr = &qmp_dp_v6_pre_emphasis_hbr_rbr,
.swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2,
.pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2,
.dp_aux_init = qmp_v4_dp_aux_init,
.configure_dp_tx = qmp_v4_configure_dp_tx,
.configure_dp_phy = qmp_v4_configure_dp_phy,
.calibrate_dp_phy = qmp_v4_calibrate_dp_phy,
.regs = qmp_v6_usb3phy_regs_layout,
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
};
static int qmp_combo_dp_serdes_init(struct qmp_combo *qmp)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
@ -2437,8 +2489,6 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp)
writel(0x20, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]);
return 0;
return 0;
}
/*
@ -3631,7 +3681,7 @@ static const struct of_device_id qmp_combo_of_match_table[] = {
},
{
.compatible = "qcom,sm8650-qmp-usb3-dp-phy",
.data = &sm8550_usb3dpphy_cfg,
.data = &sm8650_usb3dpphy_cfg,
},
{
.compatible = "qcom,x1e80100-qmp-usb3-dp-phy",

View file

@ -22,6 +22,8 @@
#include <linux/reset.h>
#include <linux/slab.h>
#include <dt-bindings/phy/phy-qcom-qmp.h>
#include "phy-qcom-qmp-common.h"
#include "phy-qcom-qmp.h"
@ -2246,6 +2248,7 @@ static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_pcs_alt_tbl[] = {
};
static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_serdes_alt_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36),
@ -2272,7 +2275,6 @@ static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_rc_serdes_alt_tbl[]
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x97),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06),
@ -2389,6 +2391,9 @@ struct qmp_phy_cfg {
/* QMP PHY pipe clock interface rate */
unsigned long pipe_clock_rate;
/* QMP PHY AUX clock interface rate */
unsigned long aux_clock_rate;
};
struct qmp_pcie {
@ -2420,6 +2425,7 @@ struct qmp_pcie {
int mode;
struct clk_fixed_rate pipe_clk_fixed;
struct clk_fixed_rate aux_clk_fixed;
};
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
@ -3135,6 +3141,9 @@ static const struct qmp_phy_cfg sm8450_qmp_gen4x2_pciephy_cfg = {
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
.phy_status = PHYSTATUS_4_20,
/* 20MHz PHY AUX Clock */
.aux_clock_rate = 20000000,
};
static const struct qmp_phy_cfg sm8550_qmp_gen3x2_pciephy_cfg = {
@ -3192,6 +3201,9 @@ static const struct qmp_phy_cfg sm8550_qmp_gen4x2_pciephy_cfg = {
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
.phy_status = PHYSTATUS_4_20,
.has_nocsr_reset = true,
/* 20MHz PHY AUX Clock */
.aux_clock_rate = 20000000,
};
static const struct qmp_phy_cfg sm8650_qmp_gen4x2_pciephy_cfg = {
@ -3222,6 +3234,9 @@ static const struct qmp_phy_cfg sm8650_qmp_gen4x2_pciephy_cfg = {
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
.phy_status = PHYSTATUS_4_20,
.has_nocsr_reset = true,
/* 20MHz PHY AUX Clock */
.aux_clock_rate = 20000000,
};
static const struct qmp_phy_cfg sa8775p_qmp_gen4x2_pciephy_cfg = {
@ -3291,6 +3306,13 @@ static const struct qmp_phy_cfg sa8775p_qmp_gen4x4_pciephy_cfg = {
.pcs_misc_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl),
},
.tbls_ep = &(const struct qmp_phy_cfg_tbls) {
.serdes = sa8775p_qmp_gen4x2_pcie_ep_serdes_alt_tbl,
.serdes_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_ep_serdes_alt_tbl),
.pcs_misc = sm8450_qmp_gen4x2_pcie_ep_pcs_misc_tbl,
.pcs_misc_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_ep_pcs_misc_tbl),
},
.reset_list = sdm845_pciephy_reset_l,
.num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
.vreg_list = qmp_phy_vreg_l,
@ -3664,7 +3686,7 @@ static int phy_pipe_clk_register(struct qmp_pcie *qmp, struct device_node *np)
struct clk_init_data init = { };
int ret;
ret = of_property_read_string(np, "clock-output-names", &init.name);
ret = of_property_read_string_index(np, "clock-output-names", 0, &init.name);
if (ret) {
dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
return ret;
@ -3683,13 +3705,86 @@ static int phy_pipe_clk_register(struct qmp_pcie *qmp, struct device_node *np)
fixed->hw.init = &init;
ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
return devm_clk_hw_register(qmp->dev, &fixed->hw);
}
/*
* Register a fixed rate PHY aux clock.
*
* The <s>_phy_aux_clksrc generated by PHY goes to the GCC that gate
* controls it. The <s>_phy_aux_clk coming out of the GCC is requested
* by the PHY driver for its operations.
* We register the <s>_phy_aux_clksrc here. The gcc driver takes care
* of assigning this <s>_phy_aux_clksrc as parent to <s>_phy_aux_clk.
* Below picture shows this relationship.
*
* +---------------+
* | PHY block |<<---------------------------------------------+
* | | |
* | +-------+ | +-----+ |
* I/P---^-->| PLL |---^--->phy_aux_clksrc--->| GCC |--->phy_aux_clk---+
* clk | +-------+ | +-----+
* +---------------+
*/
static int phy_aux_clk_register(struct qmp_pcie *qmp, struct device_node *np)
{
struct clk_fixed_rate *fixed = &qmp->aux_clk_fixed;
struct clk_init_data init = { };
int ret;
ret = of_property_read_string_index(np, "clock-output-names", 1, &init.name);
if (ret) {
dev_err(qmp->dev, "%pOFn: No clock-output-names index 1\n", np);
return ret;
}
init.ops = &clk_fixed_rate_ops;
fixed->fixed_rate = qmp->cfg->aux_clock_rate;
fixed->hw.init = &init;
return devm_clk_hw_register(qmp->dev, &fixed->hw);
}
static struct clk_hw *qmp_pcie_clk_hw_get(struct of_phandle_args *clkspec, void *data)
{
struct qmp_pcie *qmp = data;
/* Support legacy bindings */
if (!clkspec->args_count)
return &qmp->pipe_clk_fixed.hw;
switch (clkspec->args[0]) {
case QMP_PCIE_PIPE_CLK:
return &qmp->pipe_clk_fixed.hw;
case QMP_PCIE_PHY_AUX_CLK:
return &qmp->aux_clk_fixed.hw;
}
return ERR_PTR(-EINVAL);
}
static int qmp_pcie_register_clocks(struct qmp_pcie *qmp, struct device_node *np)
{
int ret;
ret = phy_pipe_clk_register(qmp, np);
if (ret)
return ret;
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
if (ret)
return ret;
if (qmp->cfg->aux_clock_rate) {
ret = phy_aux_clk_register(qmp, np);
if (ret)
return ret;
ret = of_clk_add_hw_provider(np, qmp_pcie_clk_hw_get, qmp);
if (ret)
return ret;
} else {
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &qmp->pipe_clk_fixed.hw);
if (ret)
return ret;
}
/*
* Roll a devm action because the clock provider is the child node, but
@ -3899,7 +3994,7 @@ static int qmp_pcie_probe(struct platform_device *pdev)
if (ret)
goto err_node_put;
ret = phy_pipe_clk_register(qmp, np);
ret = qmp_pcie_register_clocks(qmp, np);
if (ret)
goto err_node_put;

View file

@ -30,5 +30,9 @@
#define QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1 0x1f4
#define QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1 0x1fc
#define QPHY_V6_PCS_UFS_RX_HSG5_SYNC_WAIT_TIME 0x220
#define QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S4 0x240
#define QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S5 0x244
#define QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S6 0x248
#define QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S7 0x24c
#endif

View file

@ -25,12 +25,15 @@
#define QSERDES_UFS_V6_RX_UCDR_SO_GAIN_RATE4 0xf0
#define QSERDES_UFS_V6_RX_UCDR_PI_CONTROLS 0xf4
#define QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL 0x178
#define QSERDES_UFS_V6_RX_RX_EQU_ADAPTOR_CNTRL4 0x1ac
#define QSERDES_UFS_V6_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x1bc
#define QSERDES_UFS_V6_RX_INTERFACE_MODE 0x1e0
#define QSERDES_UFS_V6_RX_OFFSET_ADAPTOR_CNTRL3 0x1c4
#define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0 0x208
#define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1 0x20c
#define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B2 0x210
#define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B3 0x214
#define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B4 0x218
#define QSERDES_UFS_V6_RX_MODE_RATE_0_1_B6 0x220
#define QSERDES_UFS_V6_RX_MODE_RATE2_B3 0x238
#define QSERDES_UFS_V6_RX_MODE_RATE2_B6 0x244
@ -38,6 +41,9 @@
#define QSERDES_UFS_V6_RX_MODE_RATE3_B4 0x260
#define QSERDES_UFS_V6_RX_MODE_RATE3_B5 0x264
#define QSERDES_UFS_V6_RX_MODE_RATE3_B8 0x270
#define QSERDES_UFS_V6_RX_MODE_RATE4_B0 0x274
#define QSERDES_UFS_V6_RX_MODE_RATE4_B1 0x278
#define QSERDES_UFS_V6_RX_MODE_RATE4_B2 0x27c
#define QSERDES_UFS_V6_RX_MODE_RATE4_B3 0x280
#define QSERDES_UFS_V6_RX_MODE_RATE4_B4 0x284
#define QSERDES_UFS_V6_RX_MODE_RATE4_B6 0x28c

View file

@ -722,6 +722,38 @@ static const struct qmp_phy_init_tbl sm8350_ufsphy_g4_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_UFS_BIST_FIXED_PAT_CTRL, 0x0a),
};
static const struct qmp_phy_init_tbl sm8475_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x82),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0xff),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x0c),
};
static const struct qmp_phy_init_tbl sm8475_ufsphy_g4_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x98),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x32),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x0f),
};
static const struct qmp_phy_init_tbl sm8475_ufsphy_g4_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x0b),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04),
};
static const struct qmp_phy_init_tbl sm8550_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
@ -830,17 +862,20 @@ static const struct qmp_phy_init_tbl sm8650_ufsphy_serdes[] = {
QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x44),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO_MODE1, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IETRIM, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x17),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_INITVAL2, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x7f),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x4c),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x99),
@ -848,17 +883,28 @@ static const struct qmp_phy_init_tbl sm8650_ufsphy_serdes[] = {
};
static const struct qmp_phy_init_tbl sm8650_ufsphy_tx[] = {
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_LANE_MODE_1, 0x05),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_LANE_MODE_1, 0x01),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_TX, 0x07),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_TX_RES_CODE_LANE_OFFSET_RX, 0x0e),
};
static const struct qmp_phy_init_tbl sm8650_ufsphy_rx[] = {
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE2, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE4, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL, 0x0e),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0, 0xc2),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1, 0xc2),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FO_GAIN_RATE4, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_SO_GAIN_RATE4, 0x04),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x14),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_PI_CONTROLS, 0x07),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_OFFSET_ADAPTOR_CNTRL3, 0x0e),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4, 0x02),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FASTLOCK_FO_GAIN_RATE4, 0x1c),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_FASTLOCK_SO_GAIN_RATE4, 0x06),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_VGA_CAL_MAN_VAL, 0x3e),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B0, 0xce),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B1, 0xce),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B2, 0x18),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B3, 0x1a),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B4, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE_0_1_B6, 0x60),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B3, 0x9e),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE2_B6, 0x60),
@ -866,23 +912,41 @@ static const struct qmp_phy_init_tbl sm8650_ufsphy_rx[] = {
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B4, 0x0e),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B5, 0x36),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE3_B8, 0x02),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B0, 0x24),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B1, 0x24),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B2, 0x20),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B3, 0xb9),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B6, 0xff),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_MODE_RATE4_B4, 0x4f),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_SO_SATURATION, 0x1f),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_UCDR_PI_CTRL1, 0x94),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_RX_TERM_BW_CTRL0, 0xfa),
QMP_PHY_INIT_CFG(QSERDES_UFS_V6_RX_DLL0_FTUNE_CTRL, 0x30),
};
static const struct qmp_phy_init_tbl sm8650_ufsphy_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x00),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PCS_CTRL1, 0xc1),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x33),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x68),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S4, 0x0e),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S5, 0x12),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S6, 0x15),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S7, 0x19),
};
static const struct qmp_phy_init_tbl sm8650_ufsphy_g4_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x13),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x69),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
};
static const struct qmp_phy_init_tbl sm8650_ufsphy_g5_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x33),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x05),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x05),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HS_G5_SYNC_LENGTH_CAPABILITY, 0x4d),
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSG5_SYNC_WAIT_TIME, 0x9e),
};
struct qmp_ufs_offsets {
@ -1346,6 +1410,42 @@ static const struct qmp_phy_cfg sm8450_ufsphy_cfg = {
.regs = ufsphy_v5_regs_layout,
};
static const struct qmp_phy_cfg sm8475_ufsphy_cfg = {
.lanes = 2,
.offsets = &qmp_ufs_offsets_v6,
.max_supported_gear = UFS_HS_G4,
.tbls = {
.serdes = sm8475_ufsphy_serdes,
.serdes_num = ARRAY_SIZE(sm8475_ufsphy_serdes),
.tx = sm8550_ufsphy_tx,
.tx_num = ARRAY_SIZE(sm8550_ufsphy_tx),
.rx = sm8550_ufsphy_rx,
.rx_num = ARRAY_SIZE(sm8550_ufsphy_rx),
.pcs = sm8550_ufsphy_pcs,
.pcs_num = ARRAY_SIZE(sm8550_ufsphy_pcs),
},
.tbls_hs_b = {
.serdes = sm8550_ufsphy_hs_b_serdes,
.serdes_num = ARRAY_SIZE(sm8550_ufsphy_hs_b_serdes),
},
.tbls_hs_overlay[0] = {
.serdes = sm8475_ufsphy_g4_serdes,
.serdes_num = ARRAY_SIZE(sm8475_ufsphy_g4_serdes),
.tx = sm8550_ufsphy_g4_tx,
.tx_num = ARRAY_SIZE(sm8550_ufsphy_g4_tx),
.rx = sm8550_ufsphy_g4_rx,
.rx_num = ARRAY_SIZE(sm8550_ufsphy_g4_rx),
.pcs = sm8475_ufsphy_g4_pcs,
.pcs_num = ARRAY_SIZE(sm8475_ufsphy_g4_pcs),
.max_gear = UFS_HS_G4,
},
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = ufsphy_v6_regs_layout,
};
static const struct qmp_phy_cfg sm8550_ufsphy_cfg = {
.lanes = 2,
@ -1407,6 +1507,17 @@ static const struct qmp_phy_cfg sm8650_ufsphy_cfg = {
.pcs = sm8650_ufsphy_pcs,
.pcs_num = ARRAY_SIZE(sm8650_ufsphy_pcs),
},
.tbls_hs_overlay[0] = {
.pcs = sm8650_ufsphy_g4_pcs,
.pcs_num = ARRAY_SIZE(sm8650_ufsphy_g4_pcs),
.max_gear = UFS_HS_G4,
},
.tbls_hs_overlay[1] = {
.pcs = sm8650_ufsphy_g5_pcs,
.pcs_num = ARRAY_SIZE(sm8650_ufsphy_g5_pcs),
.max_gear = UFS_HS_G5,
},
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = ufsphy_v6_regs_layout,
@ -1941,6 +2052,9 @@ static const struct of_device_id qmp_ufs_of_match_table[] = {
}, {
.compatible = "qcom,sm8450-qmp-ufs-phy",
.data = &sm8450_ufsphy_cfg,
}, {
.compatible = "qcom,sm8475-qmp-ufs-phy",
.data = &sm8475_ufsphy_cfg,
}, {
.compatible = "qcom,sm8550-qmp-ufs-phy",
.data = &sm8550_ufsphy_cfg,

View file

@ -337,6 +337,29 @@ static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG2, 0x08),
};
static const struct qmp_phy_init_tbl qdu1000_usb3_uniphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xc4),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x89),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
};
static const struct qmp_phy_init_tbl qdu1000_usb3_uniphy_pcs_usb_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1, 0x6f),
};
static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
@ -1400,6 +1423,27 @@ static const struct qmp_phy_cfg msm8996_usb3phy_cfg = {
.regs = qmp_v2_usb3phy_regs_layout,
};
static const struct qmp_phy_cfg qdu1000_usb3_uniphy_cfg = {
.offsets = &qmp_usb_offsets_v5,
.serdes_tbl = sm8150_usb3_uniphy_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl),
.tx_tbl = sm8350_usb3_uniphy_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_tx_tbl),
.rx_tbl = sm8350_usb3_uniphy_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(sm8350_usb3_uniphy_rx_tbl),
.pcs_tbl = qdu1000_usb3_uniphy_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(qdu1000_usb3_uniphy_pcs_tbl),
.pcs_usb_tbl = qdu1000_usb3_uniphy_pcs_usb_tbl,
.pcs_usb_tbl_num = ARRAY_SIZE(qdu1000_usb3_uniphy_pcs_usb_tbl),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = qmp_v4_usb3phy_regs_layout,
.pcs_usb_offset = 0x1000,
.has_pwrdn_delay = true,
};
static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = {
.offsets = &qmp_usb_offsets_v5,
@ -2202,6 +2246,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = {
}, {
.compatible = "qcom,msm8996-qmp-usb3-phy",
.data = &msm8996_usb3phy_cfg,
}, {
.compatible = "qcom,qdu1000-qmp-usb3-uni-phy",
.data = &qdu1000_usb3_uniphy_cfg,
}, {
.compatible = "qcom,sa8775p-qmp-usb3-uni-phy",
.data = &sa8775p_usb3_uniphy_cfg,

View file

@ -116,3 +116,15 @@ config PHY_ROCKCHIP_USB
select GENERIC_PHY
help
Enable this to support the Rockchip USB 2.0 PHY.
config PHY_ROCKCHIP_USBDP
tristate "Rockchip USBDP COMBO PHY Driver"
depends on ARCH_ROCKCHIP && OF
depends on TYPEC
select GENERIC_PHY
help
Enable this to support the Rockchip USB3.0/DP combo PHY with
Samsung IP block. This is required for USB3 support on RK3588.
To compile this driver as a module, choose M here: the module
will be called phy-rockchip-usbdp

View file

@ -12,3 +12,4 @@ obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o
obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
obj-$(CONFIG_PHY_ROCKCHIP_USBDP) += phy-rockchip-usbdp.o

View file

@ -248,7 +248,7 @@ static int rockchip_combphy_exit(struct phy *phy)
return 0;
}
static const struct phy_ops rochchip_combphy_ops = {
static const struct phy_ops rockchip_combphy_ops = {
.init = rockchip_combphy_init,
.exit = rockchip_combphy_exit,
.owner = THIS_MODULE,
@ -364,7 +364,7 @@ static int rockchip_combphy_probe(struct platform_device *pdev)
return ret;
}
priv->phy = devm_phy_create(dev, NULL, &rochchip_combphy_ops);
priv->phy = devm_phy_create(dev, NULL, &rockchip_combphy_ops);
if (IS_ERR(priv->phy)) {
dev_err(dev, "failed to create combphy\n");
return PTR_ERR(priv->phy);

View file

@ -35,11 +35,17 @@
#define RK3588_PCIE3PHY_GRF_CMN_CON0 0x0
#define RK3588_PCIE3PHY_GRF_PHY0_STATUS1 0x904
#define RK3588_PCIE3PHY_GRF_PHY1_STATUS1 0xa04
#define RK3588_PCIE3PHY_GRF_PHY0_LN0_CON1 0x1004
#define RK3588_PCIE3PHY_GRF_PHY0_LN1_CON1 0x1104
#define RK3588_PCIE3PHY_GRF_PHY1_LN0_CON1 0x2004
#define RK3588_PCIE3PHY_GRF_PHY1_LN1_CON1 0x2104
#define RK3588_SRAM_INIT_DONE(reg) (reg & BIT(0))
#define RK3588_BIFURCATION_LANE_0_1 BIT(0)
#define RK3588_BIFURCATION_LANE_2_3 BIT(1)
#define RK3588_LANE_AGGREGATION BIT(2)
#define RK3588_RX_CMN_REFCLK_MODE_EN ((BIT(7) << 16) | BIT(7))
#define RK3588_RX_CMN_REFCLK_MODE_DIS (BIT(7) << 16)
#define RK3588_PCIE1LN_SEL_EN (GENMASK(1, 0) << 16)
#define RK3588_PCIE30_PHY_MODE_EN (GENMASK(2, 0) << 16)
@ -60,6 +66,7 @@ struct rockchip_p3phy_priv {
int num_clks;
int num_lanes;
u32 lanes[4];
u32 rx_cmn_refclk_mode[4];
};
struct rockchip_p3phy_ops {
@ -137,6 +144,19 @@ static int rockchip_p3phy_rk3588_init(struct rockchip_p3phy_priv *priv)
u8 mode = RK3588_LANE_AGGREGATION; /* default */
int ret;
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_LN0_CON1,
priv->rx_cmn_refclk_mode[0] ? RK3588_RX_CMN_REFCLK_MODE_EN :
RK3588_RX_CMN_REFCLK_MODE_DIS);
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY0_LN1_CON1,
priv->rx_cmn_refclk_mode[1] ? RK3588_RX_CMN_REFCLK_MODE_EN :
RK3588_RX_CMN_REFCLK_MODE_DIS);
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY1_LN0_CON1,
priv->rx_cmn_refclk_mode[2] ? RK3588_RX_CMN_REFCLK_MODE_EN :
RK3588_RX_CMN_REFCLK_MODE_DIS);
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_PHY1_LN1_CON1,
priv->rx_cmn_refclk_mode[3] ? RK3588_RX_CMN_REFCLK_MODE_EN :
RK3588_RX_CMN_REFCLK_MODE_DIS);
/* Deassert PCIe PMA output clamp mode */
regmap_write(priv->phy_grf, RK3588_PCIE3PHY_GRF_CMN_CON0, BIT(8) | BIT(24));
@ -182,7 +202,7 @@ static const struct rockchip_p3phy_ops rk3588_ops = {
.phy_init = rockchip_p3phy_rk3588_init,
};
static int rochchip_p3phy_init(struct phy *phy)
static int rockchip_p3phy_init(struct phy *phy)
{
struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
int ret;
@ -205,7 +225,7 @@ static int rochchip_p3phy_init(struct phy *phy)
return ret;
}
static int rochchip_p3phy_exit(struct phy *phy)
static int rockchip_p3phy_exit(struct phy *phy)
{
struct rockchip_p3phy_priv *priv = phy_get_drvdata(phy);
@ -214,9 +234,9 @@ static int rochchip_p3phy_exit(struct phy *phy)
return 0;
}
static const struct phy_ops rochchip_p3phy_ops = {
.init = rochchip_p3phy_init,
.exit = rochchip_p3phy_exit,
static const struct phy_ops rockchip_p3phy_ops = {
.init = rockchip_p3phy_init,
.exit = rockchip_p3phy_exit,
.set_mode = rockchip_p3phy_set_mode,
.owner = THIS_MODULE,
};
@ -275,7 +295,24 @@ static int rockchip_p3phy_probe(struct platform_device *pdev)
return priv->num_lanes;
}
priv->phy = devm_phy_create(dev, NULL, &rochchip_p3phy_ops);
ret = of_property_read_variable_u32_array(dev->of_node,
"rockchip,rx-common-refclk-mode",
priv->rx_cmn_refclk_mode, 1,
ARRAY_SIZE(priv->rx_cmn_refclk_mode));
/*
* if no rockchip,rx-common-refclk-mode, assume enabled for all lanes in
* order to be DT backwards compatible. (Since HW reset val is enabled.)
*/
if (ret == -EINVAL) {
for (int i = 0; i < ARRAY_SIZE(priv->rx_cmn_refclk_mode); i++)
priv->rx_cmn_refclk_mode[i] = 1;
} else if (ret < 0) {
dev_err(dev, "failed to read rockchip,rx-common-refclk-mode property %d\n",
ret);
return ret;
}
priv->phy = devm_phy_create(dev, NULL, &rockchip_p3phy_ops);
if (IS_ERR(priv->phy)) {
dev_err(dev, "failed to create combphy\n");
return PTR_ERR(priv->phy);

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o
obj-$(CONFIG_PHY_SAMSUNG_UFS) += phy-exynos-ufs.o
phy-exynos-ufs-y += phy-gs101-ufs.o
phy-exynos-ufs-y += phy-samsung-ufs.o
phy-exynos-ufs-y += phy-exynos7-ufs.o
phy-exynos-ufs-y += phy-exynosautov9-ufs.o

View file

@ -82,4 +82,5 @@ const struct samsung_ufs_phy_drvdata exynos7_ufs_phy = {
.clk_list = exynos7_ufs_phy_clks,
.num_clks = ARRAY_SIZE(exynos7_ufs_phy_clks),
.cdr_lock_status_offset = EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
.wait_for_cdr = samsung_ufs_phy_wait_for_lock_acq,
};

View file

@ -71,4 +71,5 @@ const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy = {
.clk_list = exynosautov9_ufs_phy_clks,
.num_clks = ARRAY_SIZE(exynosautov9_ufs_phy_clks),
.cdr_lock_status_offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
.wait_for_cdr = samsung_ufs_phy_wait_for_lock_acq,
};

View file

@ -60,4 +60,5 @@ const struct samsung_ufs_phy_drvdata fsd_ufs_phy = {
.clk_list = fsd_ufs_phy_clks,
.num_clks = ARRAY_SIZE(fsd_ufs_phy_clks),
.cdr_lock_status_offset = FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
.wait_for_cdr = samsung_ufs_phy_wait_for_lock_acq,
};

View file

@ -0,0 +1,182 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* UFS PHY driver data for Google Tensor gs101 SoC
*
* Copyright (C) 2024 Linaro Ltd
* Author: Peter Griffin <peter.griffin@linaro.org>
*/
#include "phy-samsung-ufs.h"
#define TENSOR_GS101_PHY_CTRL 0x3ec8
#define TENSOR_GS101_PHY_CTRL_MASK 0x1
#define TENSOR_GS101_PHY_CTRL_EN BIT(0)
#define PHY_GS101_LANE_OFFSET 0x200
#define TRSV_REG338 0x338
#define LN0_MON_RX_CAL_DONE BIT(3)
#define TRSV_REG339 0x339
#define LN0_MON_RX_CDR_FLD_CK_MODE_DONE BIT(3)
#define TRSV_REG222 0x222
#define LN0_OVRD_RX_CDR_EN BIT(4)
#define LN0_RX_CDR_EN BIT(3)
#define PHY_PMA_TRSV_ADDR(reg, lane) (PHY_APB_ADDR((reg) + \
((lane) * PHY_GS101_LANE_OFFSET)))
#define PHY_TRSV_REG_CFG_GS101(o, v, d) \
PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_GS101_LANE_OFFSET)
/* Calibration for phy initialization */
static const struct samsung_ufs_phy_cfg tensor_gs101_pre_init_cfg[] = {
PHY_COMN_REG_CFG(0x43, 0x10, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x3C, 0x14, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x46, 0x48, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x200, 0x00, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x201, 0x06, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x202, 0x06, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x203, 0x0a, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x204, 0x00, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x205, 0x11, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x207, 0x0c, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2E1, 0xc0, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x22D, 0xb8, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x234, 0x60, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x238, 0x13, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x239, 0x48, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x23A, 0x01, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x23B, 0x25, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x23C, 0x2a, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x23D, 0x01, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x23E, 0x13, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x23F, 0x13, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x240, 0x4a, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x243, 0x40, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x244, 0x02, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x25D, 0x00, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x25E, 0x3f, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x25F, 0xff, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x273, 0x33, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x274, 0x50, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x284, 0x02, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x285, 0x02, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2A2, 0x04, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x25D, 0x01, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2FA, 0x01, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x286, 0x03, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x287, 0x03, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x288, 0x03, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x289, 0x03, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2B3, 0x04, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2B6, 0x0b, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2B7, 0x0b, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2B8, 0x0b, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2B9, 0x0b, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2BA, 0x0b, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2BB, 0x06, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2BC, 0x06, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2BD, 0x06, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x29E, 0x06, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2E4, 0x1a, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2ED, 0x25, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x269, 0x1a, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x2F4, 0x2f, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x34B, 0x01, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x34C, 0x23, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x34D, 0x23, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x34E, 0x45, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x34F, 0x00, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x350, 0x31, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x351, 0x00, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x352, 0x02, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x353, 0x00, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x354, 0x01, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x43, 0x18, PWR_MODE_ANY),
PHY_COMN_REG_CFG(0x43, 0x00, PWR_MODE_ANY),
END_UFS_PHY_CFG,
};
static const struct samsung_ufs_phy_cfg tensor_gs101_pre_pwr_hs_config[] = {
PHY_TRSV_REG_CFG_GS101(0x369, 0x11, PWR_MODE_ANY),
PHY_TRSV_REG_CFG_GS101(0x246, 0x03, PWR_MODE_ANY),
};
/* Calibration for HS mode series A/B */
static const struct samsung_ufs_phy_cfg tensor_gs101_post_pwr_hs_config[] = {
PHY_COMN_REG_CFG(0x8, 0x60, PWR_MODE_PWM_ANY),
PHY_TRSV_REG_CFG_GS101(0x222, 0x08, PWR_MODE_PWM_ANY),
PHY_TRSV_REG_CFG_GS101(0x246, 0x01, PWR_MODE_ANY),
END_UFS_PHY_CFG,
};
static const struct samsung_ufs_phy_cfg *tensor_gs101_ufs_phy_cfgs[CFG_TAG_MAX] = {
[CFG_PRE_INIT] = tensor_gs101_pre_init_cfg,
[CFG_PRE_PWR_HS] = tensor_gs101_pre_pwr_hs_config,
[CFG_POST_PWR_HS] = tensor_gs101_post_pwr_hs_config,
};
static const char * const tensor_gs101_ufs_phy_clks[] = {
"ref_clk",
};
static int gs101_phy_wait_for_calibration(struct phy *phy, u8 lane)
{
struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
const unsigned int timeout_us = 40000;
const unsigned int sleep_us = 40;
u32 val;
u32 off;
int err;
off = PHY_PMA_TRSV_ADDR(TRSV_REG338, lane);
err = readl_poll_timeout(ufs_phy->reg_pma + off,
val, (val & LN0_MON_RX_CAL_DONE),
sleep_us, timeout_us);
if (err) {
dev_err(ufs_phy->dev,
"failed to get phy cal done %d\n", err);
}
return err;
}
#define DELAY_IN_US 40
#define RETRY_CNT 100
static int gs101_phy_wait_for_cdr_lock(struct phy *phy, u8 lane)
{
struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
u32 val;
int i;
for (i = 0; i < RETRY_CNT; i++) {
udelay(DELAY_IN_US);
val = readl(ufs_phy->reg_pma +
PHY_PMA_TRSV_ADDR(TRSV_REG339, lane));
if (val & LN0_MON_RX_CDR_FLD_CK_MODE_DONE)
return 0;
udelay(DELAY_IN_US);
/* Override and enable clock data recovery */
writel(LN0_OVRD_RX_CDR_EN, ufs_phy->reg_pma +
PHY_PMA_TRSV_ADDR(TRSV_REG222, lane));
writel(LN0_OVRD_RX_CDR_EN | LN0_RX_CDR_EN,
ufs_phy->reg_pma + PHY_PMA_TRSV_ADDR(TRSV_REG222, lane));
}
dev_err(ufs_phy->dev, "failed to get cdr lock\n");
return -ETIMEDOUT;
}
const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy = {
.cfgs = tensor_gs101_ufs_phy_cfgs,
.isol = {
.offset = TENSOR_GS101_PHY_CTRL,
.mask = TENSOR_GS101_PHY_CTRL_MASK,
.en = TENSOR_GS101_PHY_CTRL_EN,
},
.clk_list = tensor_gs101_ufs_phy_clks,
.num_clks = ARRAY_SIZE(tensor_gs101_ufs_phy_clks),
.wait_for_cal = gs101_phy_wait_for_calibration,
.wait_for_cdr = gs101_phy_wait_for_cdr_lock,
};

View file

@ -13,11 +13,11 @@
#include <linux/of.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/soc/samsung/exynos-pmu.h>
#include "phy-samsung-ufs.h"
@ -45,7 +45,7 @@ static void samsung_ufs_phy_config(struct samsung_ufs_phy *phy,
}
}
static int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy)
int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy, u8 lane)
{
struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
const unsigned int timeout_us = 100000;
@ -97,8 +97,21 @@ static int samsung_ufs_phy_calibrate(struct phy *phy)
}
}
if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS)
err = samsung_ufs_phy_wait_for_lock_acq(phy);
for_each_phy_lane(ufs_phy, i) {
if (ufs_phy->ufs_phy_state == CFG_PRE_INIT &&
ufs_phy->drvdata->wait_for_cal) {
err = ufs_phy->drvdata->wait_for_cal(phy, i);
if (err)
goto out;
}
if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS &&
ufs_phy->drvdata->wait_for_cdr) {
err = ufs_phy->drvdata->wait_for_cdr(phy, i);
if (err)
goto out;
}
}
/**
* In Samsung ufshci, PHY need to be calibrated at different
@ -255,8 +268,8 @@ static int samsung_ufs_phy_probe(struct platform_device *pdev)
goto out;
}
phy->reg_pmu = syscon_regmap_lookup_by_phandle(
dev->of_node, "samsung,pmu-syscon");
phy->reg_pmu = exynos_get_pmu_regmap_by_phandle(dev->of_node,
"samsung,pmu-syscon");
if (IS_ERR(phy->reg_pmu)) {
err = PTR_ERR(phy->reg_pmu);
dev_err(dev, "failed syscon remap for pmu\n");
@ -302,6 +315,9 @@ static int samsung_ufs_phy_probe(struct platform_device *pdev)
static const struct of_device_id samsung_ufs_phy_match[] = {
{
.compatible = "google,gs101-ufs-phy",
.data = &tensor_gs101_ufs_phy,
}, {
.compatible = "samsung,exynos7-ufs-phy",
.data = &exynos7_ufs_phy,
}, {

View file

@ -112,6 +112,9 @@ struct samsung_ufs_phy_drvdata {
const char * const *clk_list;
int num_clks;
u32 cdr_lock_status_offset;
/* SoC's specific operations */
int (*wait_for_cal)(struct phy *phy, u8 lane);
int (*wait_for_cdr)(struct phy *phy, u8 lane);
};
struct samsung_ufs_phy {
@ -139,8 +142,11 @@ static inline void samsung_ufs_phy_ctrl_isol(
phy->isol.mask, isol ? 0 : phy->isol.en);
}
int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy, u8 lane);
extern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy;
extern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy;
extern const struct samsung_ufs_phy_drvdata fsd_ufs_phy;
extern const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy;
#endif /* _PHY_SAMSUNG_UFS_ */

View file

@ -995,15 +995,13 @@ static int xpsgtr_probe(struct platform_device *pdev)
return 0;
}
static int xpsgtr_remove(struct platform_device *pdev)
static void xpsgtr_remove(struct platform_device *pdev)
{
struct xpsgtr_dev *gtr_dev = platform_get_drvdata(pdev);
pm_runtime_disable(gtr_dev->dev);
pm_runtime_put_noidle(gtr_dev->dev);
pm_runtime_set_suspended(gtr_dev->dev);
return 0;
}
static const struct of_device_id xpsgtr_of_match[] = {
@ -1015,7 +1013,7 @@ MODULE_DEVICE_TABLE(of, xpsgtr_of_match);
static struct platform_driver xpsgtr_driver = {
.probe = xpsgtr_probe,
.remove = xpsgtr_remove,
.remove_new = xpsgtr_remove,
.driver = {
.name = "xilinx-psgtr",
.of_match_table = xpsgtr_of_match,

View file

@ -17,4 +17,8 @@
#define QMP_USB43DP_USB3_PHY 0
#define QMP_USB43DP_DP_PHY 1
/* QMP PCIE PHYs */
#define QMP_PCIE_PIPE_CLK 0
#define QMP_PCIE_PHY_AUX_CLK 1
#endif /* _DT_BINDINGS_PHY_QMP */