Commit 8053d2ff authored by Linus Torvalds's avatar Linus Torvalds

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.
  ...
parents d4e034b4 960b3f02
# 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>;
};
# 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>;
};
};
...
# 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>;
};
......@@ -21,6 +21,7 @@ properties:
- qcom,sc8180x-edp-phy
- qcom,sc8280xp-dp-phy
- qcom,sc8280xp-edp-phy
- qcom,x1e80100-dp-phy
reg:
items:
......
......@@ -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>
......
......@@ -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:
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -15,6 +15,7 @@ properties:
compatible:
enum:
- google,gs101-ufs-phy
- samsung,exynos7-ufs-phy
- samsung,exynosautov9-ufs-phy
- tesla,fsd-ufs-phy
......
......@@ -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>
......
......@@ -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
......
......@@ -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
// 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");
......@@ -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
......
......@@ -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
......
// 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");
......@@ -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);
......@@ -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,30 +510,150 @@ 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)
{
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_V6_COM_CMN_STATUS,
val, val & BIT(7), 5, 200);
}
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 vco_div;
u32 step1;
u32 step2;
switch (dp_opts->link_rate) {
case 1620:
vco_div = 0x1;
*pixel_freq = 1620000000UL / 2;
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:
vco_div = 0x1;
*pixel_freq = 2700000000UL / 2;
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:
vco_div = 0x2;
*pixel_freq = 5400000000UL / 4;
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:
vco_div = 0x0;
*pixel_freq = 8100000000UL / 6;
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:
......@@ -462,33 +661,69 @@ static int qcom_edp_set_vco_div(const struct qcom_edp *edp, unsigned long *pixel
return -EINVAL;
}
writel(vco_div, edp->edp + DP_PHY_VCO_DIV);
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 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 |
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);
timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS,
val, val & BIT(7), 5, 200);
if (timeout)
return timeout;
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);
......
......@@ -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);
......
......@@ -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",
......
......@@ -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,14 +3705,87 @@ 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);
if (ret)
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;
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &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;
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
* the child node is not actually a device.
......@@ -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;
......
......@@ -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
......@@ -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
......
......@@ -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,
......
......@@ -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,
......
......@@ -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
......@@ -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
......@@ -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);
......
......@@ -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);
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Rockchip USBDP Combo PHY with Samsung IP block driver
*
* Copyright (C) 2021-2024 Rockchip Electronics Co., Ltd
* Copyright (C) 2024 Collabora Ltd
*/
#include <dt-bindings/phy/phy.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/mfd/syscon.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/usb/ch9.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
/* USBDP PHY Register Definitions */
#define UDPHY_PCS 0x4000
#define UDPHY_PMA 0x8000
/* VO0 GRF Registers */
#define DP_SINK_HPD_CFG BIT(11)
#define DP_SINK_HPD_SEL BIT(10)
#define DP_AUX_DIN_SEL BIT(9)
#define DP_AUX_DOUT_SEL BIT(8)
#define DP_LANE_SEL_N(n) GENMASK(2 * (n) + 1, 2 * (n))
#define DP_LANE_SEL_ALL GENMASK(7, 0)
/* PMA CMN Registers */
#define CMN_LANE_MUX_AND_EN_OFFSET 0x0288 /* cmn_reg00A2 */
#define CMN_DP_LANE_MUX_N(n) BIT((n) + 4)
#define CMN_DP_LANE_EN_N(n) BIT(n)
#define CMN_DP_LANE_MUX_ALL GENMASK(7, 4)
#define CMN_DP_LANE_EN_ALL GENMASK(3, 0)
#define CMN_DP_LINK_OFFSET 0x28c /* cmn_reg00A3 */
#define CMN_DP_TX_LINK_BW GENMASK(6, 5)
#define CMN_DP_TX_LANE_SWAP_EN BIT(2)
#define CMN_SSC_EN_OFFSET 0x2d0 /* cmn_reg00B4 */
#define CMN_ROPLL_SSC_EN BIT(1)
#define CMN_LCPLL_SSC_EN BIT(0)
#define CMN_ANA_LCPLL_DONE_OFFSET 0x0350 /* cmn_reg00D4 */
#define CMN_ANA_LCPLL_LOCK_DONE BIT(7)
#define CMN_ANA_LCPLL_AFC_DONE BIT(6)
#define CMN_ANA_ROPLL_DONE_OFFSET 0x0354 /* cmn_reg00D5 */
#define CMN_ANA_ROPLL_LOCK_DONE BIT(1)
#define CMN_ANA_ROPLL_AFC_DONE BIT(0)
#define CMN_DP_RSTN_OFFSET 0x038c /* cmn_reg00E3 */
#define CMN_DP_INIT_RSTN BIT(3)
#define CMN_DP_CMN_RSTN BIT(2)
#define CMN_CDR_WTCHDG_EN BIT(1)
#define CMN_CDR_WTCHDG_MSK_CDR_EN BIT(0)
#define TRSV_ANA_TX_CLK_OFFSET_N(n) (0x854 + (n) * 0x800) /* trsv_reg0215 */
#define LN_ANA_TX_SER_TXCLK_INV BIT(1)
#define TRSV_LN0_MON_RX_CDR_DONE_OFFSET 0x0b84 /* trsv_reg02E1 */
#define TRSV_LN0_MON_RX_CDR_LOCK_DONE BIT(0)
#define TRSV_LN2_MON_RX_CDR_DONE_OFFSET 0x1b84 /* trsv_reg06E1 */
#define TRSV_LN2_MON_RX_CDR_LOCK_DONE BIT(0)
#define BIT_WRITEABLE_SHIFT 16
#define PHY_AUX_DP_DATA_POL_NORMAL 0
#define PHY_AUX_DP_DATA_POL_INVERT 1
#define PHY_LANE_MUX_USB 0
#define PHY_LANE_MUX_DP 1
enum {
DP_BW_RBR,
DP_BW_HBR,
DP_BW_HBR2,
DP_BW_HBR3,
};
enum {
UDPHY_MODE_NONE = 0,
UDPHY_MODE_USB = BIT(0),
UDPHY_MODE_DP = BIT(1),
UDPHY_MODE_DP_USB = BIT(1) | BIT(0),
};
struct rk_udphy_grf_reg {
unsigned int offset;
unsigned int disable;
unsigned int enable;
};
#define _RK_UDPHY_GEN_GRF_REG(offset, mask, disable, enable) \
{\
offset, \
FIELD_PREP_CONST(mask, disable) | (mask << BIT_WRITEABLE_SHIFT), \
FIELD_PREP_CONST(mask, enable) | (mask << BIT_WRITEABLE_SHIFT), \
}
#define RK_UDPHY_GEN_GRF_REG(offset, bitend, bitstart, disable, enable) \
_RK_UDPHY_GEN_GRF_REG(offset, GENMASK(bitend, bitstart), disable, enable)
struct rk_udphy_grf_cfg {
/* u2phy-grf */
struct rk_udphy_grf_reg bvalid_phy_con;
struct rk_udphy_grf_reg bvalid_grf_con;
/* usb-grf */
struct rk_udphy_grf_reg usb3otg0_cfg;
struct rk_udphy_grf_reg usb3otg1_cfg;
/* usbdpphy-grf */
struct rk_udphy_grf_reg low_pwrn;
struct rk_udphy_grf_reg rx_lfps;
};
struct rk_udphy_vogrf_cfg {
/* vo-grf */
struct rk_udphy_grf_reg hpd_trigger;
u32 dp_lane_reg;
};
struct rk_udphy_dp_tx_drv_ctrl {
u32 trsv_reg0204;
u32 trsv_reg0205;
u32 trsv_reg0206;
u32 trsv_reg0207;
};
struct rk_udphy_cfg {
unsigned int num_phys;
unsigned int phy_ids[2];
/* resets to be requested */
const char * const *rst_list;
int num_rsts;
struct rk_udphy_grf_cfg grfcfg;
struct rk_udphy_vogrf_cfg vogrfcfg[2];
const struct rk_udphy_dp_tx_drv_ctrl (*dp_tx_ctrl_cfg[4])[4];
const struct rk_udphy_dp_tx_drv_ctrl (*dp_tx_ctrl_cfg_typec[4])[4];
};
struct rk_udphy {
struct device *dev;
struct regmap *pma_regmap;
struct regmap *u2phygrf;
struct regmap *udphygrf;
struct regmap *usbgrf;
struct regmap *vogrf;
struct typec_switch_dev *sw;
struct typec_mux_dev *mux;
struct mutex mutex; /* mutex to protect access to individual PHYs */
/* clocks and rests */
int num_clks;
struct clk_bulk_data *clks;
struct clk *refclk;
int num_rsts;
struct reset_control_bulk_data *rsts;
/* PHY status management */
bool flip;
bool mode_change;
u8 mode;
u8 status;
/* utilized for USB */
bool hs; /* flag for high-speed */
/* utilized for DP */
struct gpio_desc *sbu1_dc_gpio;
struct gpio_desc *sbu2_dc_gpio;
u32 lane_mux_sel[4];
u32 dp_lane_sel[4];
u32 dp_aux_dout_sel;
u32 dp_aux_din_sel;
bool dp_sink_hpd_sel;
bool dp_sink_hpd_cfg;
u8 bw;
int id;
bool dp_in_use;
/* PHY const config */
const struct rk_udphy_cfg *cfgs;
/* PHY devices */
struct phy *phy_dp;
struct phy *phy_u3;
};
static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr[4][4] = {
/* voltage swing 0, pre-emphasis 0->3 */
{
{ 0x20, 0x10, 0x42, 0xe5 },
{ 0x26, 0x14, 0x42, 0xe5 },
{ 0x29, 0x18, 0x42, 0xe5 },
{ 0x2b, 0x1c, 0x43, 0xe7 },
},
/* voltage swing 1, pre-emphasis 0->2 */
{
{ 0x23, 0x10, 0x42, 0xe7 },
{ 0x2a, 0x17, 0x43, 0xe7 },
{ 0x2b, 0x1a, 0x43, 0xe7 },
},
/* voltage swing 2, pre-emphasis 0->1 */
{
{ 0x27, 0x10, 0x42, 0xe7 },
{ 0x2b, 0x17, 0x43, 0xe7 },
},
/* voltage swing 3, pre-emphasis 0 */
{
{ 0x29, 0x10, 0x43, 0xe7 },
},
};
static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_rbr_hbr_typec[4][4] = {
/* voltage swing 0, pre-emphasis 0->3 */
{
{ 0x20, 0x10, 0x42, 0xe5 },
{ 0x26, 0x14, 0x42, 0xe5 },
{ 0x29, 0x18, 0x42, 0xe5 },
{ 0x2b, 0x1c, 0x43, 0xe7 },
},
/* voltage swing 1, pre-emphasis 0->2 */
{
{ 0x23, 0x10, 0x42, 0xe7 },
{ 0x2a, 0x17, 0x43, 0xe7 },
{ 0x2b, 0x1a, 0x43, 0xe7 },
},
/* voltage swing 2, pre-emphasis 0->1 */
{
{ 0x27, 0x10, 0x43, 0x67 },
{ 0x2b, 0x17, 0x43, 0xe7 },
},
/* voltage swing 3, pre-emphasis 0 */
{
{ 0x29, 0x10, 0x43, 0xe7 },
},
};
static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_hbr2[4][4] = {
/* voltage swing 0, pre-emphasis 0->3 */
{
{ 0x21, 0x10, 0x42, 0xe5 },
{ 0x26, 0x14, 0x42, 0xe5 },
{ 0x26, 0x16, 0x43, 0xe5 },
{ 0x2a, 0x19, 0x43, 0xe7 },
},
/* voltage swing 1, pre-emphasis 0->2 */
{
{ 0x24, 0x10, 0x42, 0xe7 },
{ 0x2a, 0x17, 0x43, 0xe7 },
{ 0x2b, 0x1a, 0x43, 0xe7 },
},
/* voltage swing 2, pre-emphasis 0->1 */
{
{ 0x28, 0x10, 0x42, 0xe7 },
{ 0x2b, 0x17, 0x43, 0xe7 },
},
/* voltage swing 3, pre-emphasis 0 */
{
{ 0x28, 0x10, 0x43, 0xe7 },
},
};
static const struct rk_udphy_dp_tx_drv_ctrl rk3588_dp_tx_drv_ctrl_hbr3[4][4] = {
/* voltage swing 0, pre-emphasis 0->3 */
{
{ 0x21, 0x10, 0x42, 0xe5 },
{ 0x26, 0x14, 0x42, 0xe5 },
{ 0x26, 0x16, 0x43, 0xe5 },
{ 0x29, 0x18, 0x43, 0xe7 },
},
/* voltage swing 1, pre-emphasis 0->2 */
{
{ 0x24, 0x10, 0x42, 0xe7 },
{ 0x2a, 0x18, 0x43, 0xe7 },
{ 0x2b, 0x1b, 0x43, 0xe7 }
},
/* voltage swing 2, pre-emphasis 0->1 */
{
{ 0x27, 0x10, 0x42, 0xe7 },
{ 0x2b, 0x18, 0x43, 0xe7 }
},
/* voltage swing 3, pre-emphasis 0 */
{
{ 0x28, 0x10, 0x43, 0xe7 },
},
};
static const struct reg_sequence rk_udphy_24m_refclk_cfg[] = {
{0x0090, 0x68}, {0x0094, 0x68},
{0x0128, 0x24}, {0x012c, 0x44},
{0x0130, 0x3f}, {0x0134, 0x44},
{0x015c, 0xa9}, {0x0160, 0x71},
{0x0164, 0x71}, {0x0168, 0xa9},
{0x0174, 0xa9}, {0x0178, 0x71},
{0x017c, 0x71}, {0x0180, 0xa9},
{0x018c, 0x41}, {0x0190, 0x00},
{0x0194, 0x05}, {0x01ac, 0x2a},
{0x01b0, 0x17}, {0x01b4, 0x17},
{0x01b8, 0x2a}, {0x01c8, 0x04},
{0x01cc, 0x08}, {0x01d0, 0x08},
{0x01d4, 0x04}, {0x01d8, 0x20},
{0x01dc, 0x01}, {0x01e0, 0x09},
{0x01e4, 0x03}, {0x01f0, 0x29},
{0x01f4, 0x02}, {0x01f8, 0x02},
{0x01fc, 0x29}, {0x0208, 0x2a},
{0x020c, 0x17}, {0x0210, 0x17},
{0x0214, 0x2a}, {0x0224, 0x20},
{0x03f0, 0x0a}, {0x03f4, 0x07},
{0x03f8, 0x07}, {0x03fc, 0x0c},
{0x0404, 0x12}, {0x0408, 0x1a},
{0x040c, 0x1a}, {0x0410, 0x3f},
{0x0ce0, 0x68}, {0x0ce8, 0xd0},
{0x0cf0, 0x87}, {0x0cf8, 0x70},
{0x0d00, 0x70}, {0x0d08, 0xa9},
{0x1ce0, 0x68}, {0x1ce8, 0xd0},
{0x1cf0, 0x87}, {0x1cf8, 0x70},
{0x1d00, 0x70}, {0x1d08, 0xa9},
{0x0a3c, 0xd0}, {0x0a44, 0xd0},
{0x0a48, 0x01}, {0x0a4c, 0x0d},
{0x0a54, 0xe0}, {0x0a5c, 0xe0},
{0x0a64, 0xa8}, {0x1a3c, 0xd0},
{0x1a44, 0xd0}, {0x1a48, 0x01},
{0x1a4c, 0x0d}, {0x1a54, 0xe0},
{0x1a5c, 0xe0}, {0x1a64, 0xa8}
};
static const struct reg_sequence rk_udphy_26m_refclk_cfg[] = {
{0x0830, 0x07}, {0x085c, 0x80},
{0x1030, 0x07}, {0x105c, 0x80},
{0x1830, 0x07}, {0x185c, 0x80},
{0x2030, 0x07}, {0x205c, 0x80},
{0x0228, 0x38}, {0x0104, 0x44},
{0x0248, 0x44}, {0x038c, 0x02},
{0x0878, 0x04}, {0x1878, 0x04},
{0x0898, 0x77}, {0x1898, 0x77},
{0x0054, 0x01}, {0x00e0, 0x38},
{0x0060, 0x24}, {0x0064, 0x77},
{0x0070, 0x76}, {0x0234, 0xe8},
{0x0af4, 0x15}, {0x1af4, 0x15},
{0x081c, 0xe5}, {0x181c, 0xe5},
{0x099c, 0x48}, {0x199c, 0x48},
{0x09a4, 0x07}, {0x09a8, 0x22},
{0x19a4, 0x07}, {0x19a8, 0x22},
{0x09b8, 0x3e}, {0x19b8, 0x3e},
{0x09e4, 0x02}, {0x19e4, 0x02},
{0x0a34, 0x1e}, {0x1a34, 0x1e},
{0x0a98, 0x2f}, {0x1a98, 0x2f},
{0x0c30, 0x0e}, {0x0c48, 0x06},
{0x1c30, 0x0e}, {0x1c48, 0x06},
{0x028c, 0x18}, {0x0af0, 0x00},
{0x1af0, 0x00}
};
static const struct reg_sequence rk_udphy_init_sequence[] = {
{0x0104, 0x44}, {0x0234, 0xe8},
{0x0248, 0x44}, {0x028c, 0x18},
{0x081c, 0xe5}, {0x0878, 0x00},
{0x0994, 0x1c}, {0x0af0, 0x00},
{0x181c, 0xe5}, {0x1878, 0x00},
{0x1994, 0x1c}, {0x1af0, 0x00},
{0x0428, 0x60}, {0x0d58, 0x33},
{0x1d58, 0x33}, {0x0990, 0x74},
{0x0d64, 0x17}, {0x08c8, 0x13},
{0x1990, 0x74}, {0x1d64, 0x17},
{0x18c8, 0x13}, {0x0d90, 0x40},
{0x0da8, 0x40}, {0x0dc0, 0x40},
{0x0dd8, 0x40}, {0x1d90, 0x40},
{0x1da8, 0x40}, {0x1dc0, 0x40},
{0x1dd8, 0x40}, {0x03c0, 0x30},
{0x03c4, 0x06}, {0x0e10, 0x00},
{0x1e10, 0x00}, {0x043c, 0x0f},
{0x0d2c, 0xff}, {0x1d2c, 0xff},
{0x0d34, 0x0f}, {0x1d34, 0x0f},
{0x08fc, 0x2a}, {0x0914, 0x28},
{0x0a30, 0x03}, {0x0e38, 0x03},
{0x0ecc, 0x27}, {0x0ed0, 0x22},
{0x0ed4, 0x26}, {0x18fc, 0x2a},
{0x1914, 0x28}, {0x1a30, 0x03},
{0x1e38, 0x03}, {0x1ecc, 0x27},
{0x1ed0, 0x22}, {0x1ed4, 0x26},
{0x0048, 0x0f}, {0x0060, 0x3c},
{0x0064, 0xf7}, {0x006c, 0x20},
{0x0070, 0x7d}, {0x0074, 0x68},
{0x0af4, 0x1a}, {0x1af4, 0x1a},
{0x0440, 0x3f}, {0x10d4, 0x08},
{0x20d4, 0x08}, {0x00d4, 0x30},
{0x0024, 0x6e},
};
static inline int rk_udphy_grfreg_write(struct regmap *base,
const struct rk_udphy_grf_reg *reg, bool en)
{
return regmap_write(base, reg->offset, en ? reg->enable : reg->disable);
}
static int rk_udphy_clk_init(struct rk_udphy *udphy, struct device *dev)
{
int i;
udphy->num_clks = devm_clk_bulk_get_all(dev, &udphy->clks);
if (udphy->num_clks < 1)
return -ENODEV;
/* used for configure phy reference clock frequency */
for (i = 0; i < udphy->num_clks; i++) {
if (!strncmp(udphy->clks[i].id, "refclk", 6)) {
udphy->refclk = udphy->clks[i].clk;
break;
}
}
if (!udphy->refclk)
return dev_err_probe(udphy->dev, -EINVAL, "no refclk found\n");
return 0;
}
static int rk_udphy_reset_assert_all(struct rk_udphy *udphy)
{
return reset_control_bulk_assert(udphy->num_rsts, udphy->rsts);
}
static int rk_udphy_reset_deassert_all(struct rk_udphy *udphy)
{
return reset_control_bulk_deassert(udphy->num_rsts, udphy->rsts);
}
static int rk_udphy_reset_deassert(struct rk_udphy *udphy, char *name)
{
struct reset_control_bulk_data *list = udphy->rsts;
int idx;
for (idx = 0; idx < udphy->num_rsts; idx++) {
if (!strcmp(list[idx].id, name))
return reset_control_deassert(list[idx].rstc);
}
return -EINVAL;
}
static int rk_udphy_reset_init(struct rk_udphy *udphy, struct device *dev)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
int idx;
udphy->num_rsts = cfg->num_rsts;
udphy->rsts = devm_kcalloc(dev, udphy->num_rsts,
sizeof(*udphy->rsts), GFP_KERNEL);
if (!udphy->rsts)
return -ENOMEM;
for (idx = 0; idx < cfg->num_rsts; idx++)
udphy->rsts[idx].id = cfg->rst_list[idx];
return devm_reset_control_bulk_get_exclusive(dev, cfg->num_rsts,
udphy->rsts);
}
static void rk_udphy_u3_port_disable(struct rk_udphy *udphy, u8 disable)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
const struct rk_udphy_grf_reg *preg;
preg = udphy->id ? &cfg->grfcfg.usb3otg1_cfg : &cfg->grfcfg.usb3otg0_cfg;
rk_udphy_grfreg_write(udphy->usbgrf, preg, disable);
}
static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
rk_udphy_grfreg_write(udphy->u2phygrf, &cfg->grfcfg.bvalid_phy_con, enable);
rk_udphy_grfreg_write(udphy->u2phygrf, &cfg->grfcfg.bvalid_grf_con, enable);
}
/*
* In usb/dp combo phy driver, here are 2 ways to mapping lanes.
*
* 1 Type-C Mapping table (DP_Alt_Mode V1.0b remove ABF pin mapping)
* ---------------------------------------------------------------------------
* Type-C Pin B11-B10 A2-A3 A11-A10 B2-B3
* PHY Pad ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
* C/E(Normal) dpln3 dpln2 dpln0 dpln1
* C/E(Flip ) dpln0 dpln1 dpln3 dpln2
* D/F(Normal) usbrx usbtx dpln0 dpln1
* D/F(Flip ) dpln0 dpln1 usbrx usbtx
* A(Normal ) dpln3 dpln1 dpln2 dpln0
* A(Flip ) dpln2 dpln0 dpln3 dpln1
* B(Normal ) usbrx usbtx dpln1 dpln0
* B(Flip ) dpln1 dpln0 usbrx usbtx
* ---------------------------------------------------------------------------
*
* 2 Mapping the lanes in dtsi
* if all 4 lane assignment for dp function, define rockchip,dp-lane-mux = <x x x x>;
* sample as follow:
* ---------------------------------------------------------------------------
* B11-B10 A2-A3 A11-A10 B2-B3
* rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
* <0 1 2 3> dpln0 dpln1 dpln2 dpln3
* <2 3 0 1> dpln2 dpln3 dpln0 dpln1
* ---------------------------------------------------------------------------
* if 2 lane for dp function, 2 lane for usb function, define rockchip,dp-lane-mux = <x x>;
* sample as follow:
* ---------------------------------------------------------------------------
* B11-B10 A2-A3 A11-A10 B2-B3
* rockchip,dp-lane-mux ln0(tx/rx) ln1(tx) ln2(tx/rx) ln3(tx)
* <0 1> dpln0 dpln1 usbrx usbtx
* <2 3> usbrx usbtx dpln0 dpln1
* ---------------------------------------------------------------------------
*/
static void rk_udphy_dplane_select(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
u32 value = 0;
switch (udphy->mode) {
case UDPHY_MODE_DP:
value |= 2 << udphy->dp_lane_sel[2] * 2;
value |= 3 << udphy->dp_lane_sel[3] * 2;
fallthrough;
case UDPHY_MODE_DP_USB:
value |= 0 << udphy->dp_lane_sel[0] * 2;
value |= 1 << udphy->dp_lane_sel[1] * 2;
break;
case UDPHY_MODE_USB:
break;
default:
break;
}
regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg,
((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) |
FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) |
FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
}
static int rk_udphy_dplane_get(struct rk_udphy *udphy)
{
int dp_lanes;
switch (udphy->mode) {
case UDPHY_MODE_DP:
dp_lanes = 4;
break;
case UDPHY_MODE_DP_USB:
dp_lanes = 2;
break;
case UDPHY_MODE_USB:
default:
dp_lanes = 0;
break;
}
return dp_lanes;
}
static void rk_udphy_dplane_enable(struct rk_udphy *udphy, int dp_lanes)
{
u32 val = 0;
int i;
for (i = 0; i < dp_lanes; i++)
val |= BIT(udphy->dp_lane_sel[i]);
regmap_update_bits(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, CMN_DP_LANE_EN_ALL,
FIELD_PREP(CMN_DP_LANE_EN_ALL, val));
if (!dp_lanes)
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
}
static void rk_udphy_dp_hpd_event_trigger(struct rk_udphy *udphy, bool hpd)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
udphy->dp_sink_hpd_sel = true;
udphy->dp_sink_hpd_cfg = hpd;
if (!udphy->dp_in_use)
return;
rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, hpd);
}
static void rk_udphy_set_typec_default_mapping(struct rk_udphy *udphy)
{
if (udphy->flip) {
udphy->dp_lane_sel[0] = 0;
udphy->dp_lane_sel[1] = 1;
udphy->dp_lane_sel[2] = 3;
udphy->dp_lane_sel[3] = 2;
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_INVERT;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_INVERT;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 1);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
} else {
udphy->dp_lane_sel[0] = 2;
udphy->dp_lane_sel[1] = 3;
udphy->dp_lane_sel[2] = 1;
udphy->dp_lane_sel[3] = 0;
udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
udphy->dp_aux_dout_sel = PHY_AUX_DP_DATA_POL_NORMAL;
udphy->dp_aux_din_sel = PHY_AUX_DP_DATA_POL_NORMAL;
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 1);
}
udphy->mode = UDPHY_MODE_DP_USB;
}
static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
enum typec_orientation orien)
{
struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
mutex_lock(&udphy->mutex);
if (orien == TYPEC_ORIENTATION_NONE) {
gpiod_set_value_cansleep(udphy->sbu1_dc_gpio, 0);
gpiod_set_value_cansleep(udphy->sbu2_dc_gpio, 0);
/* unattached */
rk_udphy_usb_bvalid_enable(udphy, false);
goto unlock_ret;
}
udphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false;
rk_udphy_set_typec_default_mapping(udphy);
rk_udphy_usb_bvalid_enable(udphy, true);
unlock_ret:
mutex_unlock(&udphy->mutex);
return 0;
}
static void rk_udphy_orien_switch_unregister(void *data)
{
struct rk_udphy *udphy = data;
typec_switch_unregister(udphy->sw);
}
static int rk_udphy_setup_orien_switch(struct rk_udphy *udphy)
{
struct typec_switch_desc sw_desc = { };
sw_desc.drvdata = udphy;
sw_desc.fwnode = dev_fwnode(udphy->dev);
sw_desc.set = rk_udphy_orien_sw_set;
udphy->sw = typec_switch_register(udphy->dev, &sw_desc);
if (IS_ERR(udphy->sw)) {
dev_err(udphy->dev, "Error register typec orientation switch: %ld\n",
PTR_ERR(udphy->sw));
return PTR_ERR(udphy->sw);
}
return devm_add_action_or_reset(udphy->dev,
rk_udphy_orien_switch_unregister, udphy);
}
static int rk_udphy_refclk_set(struct rk_udphy *udphy)
{
unsigned long rate;
int ret;
/* configure phy reference clock */
rate = clk_get_rate(udphy->refclk);
dev_dbg(udphy->dev, "refclk freq %ld\n", rate);
switch (rate) {
case 24000000:
ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_24m_refclk_cfg,
ARRAY_SIZE(rk_udphy_24m_refclk_cfg));
if (ret)
return ret;
break;
case 26000000:
/* register default is 26MHz */
ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_26m_refclk_cfg,
ARRAY_SIZE(rk_udphy_26m_refclk_cfg));
if (ret)
return ret;
break;
default:
dev_err(udphy->dev, "unsupported refclk freq %ld\n", rate);
return -EINVAL;
}
return 0;
}
static int rk_udphy_status_check(struct rk_udphy *udphy)
{
unsigned int val;
int ret;
/* LCPLL check */
if (udphy->mode & UDPHY_MODE_USB) {
ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_LCPLL_DONE_OFFSET,
val, (val & CMN_ANA_LCPLL_AFC_DONE) &&
(val & CMN_ANA_LCPLL_LOCK_DONE), 200, 100000);
if (ret) {
dev_err(udphy->dev, "cmn ana lcpll lock timeout\n");
/*
* If earlier software (U-Boot) enabled USB once already
* the PLL may have problems locking on the first try.
* It will be successful on the second try, so for the
* time being a -EPROBE_DEFER will solve the issue.
*
* This requires further investigation to understand the
* root cause, especially considering that the driver is
* asserting all reset lines at probe time.
*/
return -EPROBE_DEFER;
}
if (!udphy->flip) {
ret = regmap_read_poll_timeout(udphy->pma_regmap,
TRSV_LN0_MON_RX_CDR_DONE_OFFSET, val,
val & TRSV_LN0_MON_RX_CDR_LOCK_DONE,
200, 100000);
if (ret)
dev_err(udphy->dev, "trsv ln0 mon rx cdr lock timeout\n");
} else {
ret = regmap_read_poll_timeout(udphy->pma_regmap,
TRSV_LN2_MON_RX_CDR_DONE_OFFSET, val,
val & TRSV_LN2_MON_RX_CDR_LOCK_DONE,
200, 100000);
if (ret)
dev_err(udphy->dev, "trsv ln2 mon rx cdr lock timeout\n");
}
}
return 0;
}
static int rk_udphy_init(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
int ret;
rk_udphy_reset_assert_all(udphy);
usleep_range(10000, 11000);
/* enable rx lfps for usb */
if (udphy->mode & UDPHY_MODE_USB)
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.rx_lfps, true);
/* Step 1: power on pma and deassert apb rstn */
rk_udphy_grfreg_write(udphy->udphygrf, &cfg->grfcfg.low_pwrn, true);
rk_udphy_reset_deassert(udphy, "pma_apb");
rk_udphy_reset_deassert(udphy, "pcs_apb");
/* Step 2: set init sequence and phy refclk */
ret = regmap_multi_reg_write(udphy->pma_regmap, rk_udphy_init_sequence,
ARRAY_SIZE(rk_udphy_init_sequence));
if (ret) {
dev_err(udphy->dev, "init sequence set error %d\n", ret);
goto assert_resets;
}
ret = rk_udphy_refclk_set(udphy);
if (ret) {
dev_err(udphy->dev, "refclk set error %d\n", ret);
goto assert_resets;
}
/* Step 3: configure lane mux */
regmap_update_bits(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET,
CMN_DP_LANE_MUX_ALL | CMN_DP_LANE_EN_ALL,
FIELD_PREP(CMN_DP_LANE_MUX_N(3), udphy->lane_mux_sel[3]) |
FIELD_PREP(CMN_DP_LANE_MUX_N(2), udphy->lane_mux_sel[2]) |
FIELD_PREP(CMN_DP_LANE_MUX_N(1), udphy->lane_mux_sel[1]) |
FIELD_PREP(CMN_DP_LANE_MUX_N(0), udphy->lane_mux_sel[0]) |
FIELD_PREP(CMN_DP_LANE_EN_ALL, 0));
/* Step 4: deassert init rstn and wait for 200ns from datasheet */
if (udphy->mode & UDPHY_MODE_USB)
rk_udphy_reset_deassert(udphy, "init");
if (udphy->mode & UDPHY_MODE_DP) {
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
CMN_DP_INIT_RSTN,
FIELD_PREP(CMN_DP_INIT_RSTN, 0x1));
}
udelay(1);
/* Step 5: deassert cmn/lane rstn */
if (udphy->mode & UDPHY_MODE_USB) {
rk_udphy_reset_deassert(udphy, "cmn");
rk_udphy_reset_deassert(udphy, "lane");
}
/* Step 6: wait for lock done of pll */
ret = rk_udphy_status_check(udphy);
if (ret)
goto assert_resets;
return 0;
assert_resets:
rk_udphy_reset_assert_all(udphy);
return ret;
}
static int rk_udphy_setup(struct rk_udphy *udphy)
{
int ret;
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
if (ret) {
dev_err(udphy->dev, "failed to enable clk\n");
return ret;
}
ret = rk_udphy_init(udphy);
if (ret) {
dev_err(udphy->dev, "failed to init combophy\n");
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
return ret;
}
return 0;
}
static void rk_udphy_disable(struct rk_udphy *udphy)
{
clk_bulk_disable_unprepare(udphy->num_clks, udphy->clks);
rk_udphy_reset_assert_all(udphy);
}
static int rk_udphy_parse_lane_mux_data(struct rk_udphy *udphy)
{
int ret, i, num_lanes;
num_lanes = device_property_count_u32(udphy->dev, "rockchip,dp-lane-mux");
if (num_lanes < 0) {
dev_dbg(udphy->dev, "no dp-lane-mux, following dp alt mode\n");
udphy->mode = UDPHY_MODE_USB;
return 0;
}
if (num_lanes != 2 && num_lanes != 4)
return dev_err_probe(udphy->dev, -EINVAL,
"invalid number of lane mux\n");
ret = device_property_read_u32_array(udphy->dev, "rockchip,dp-lane-mux",
udphy->dp_lane_sel, num_lanes);
if (ret)
return dev_err_probe(udphy->dev, ret, "get dp lane mux failed\n");
for (i = 0; i < num_lanes; i++) {
int j;
if (udphy->dp_lane_sel[i] > 3)
return dev_err_probe(udphy->dev, -EINVAL,
"lane mux between 0 and 3, exceeding the range\n");
udphy->lane_mux_sel[udphy->dp_lane_sel[i]] = PHY_LANE_MUX_DP;
for (j = i + 1; j < num_lanes; j++) {
if (udphy->dp_lane_sel[i] == udphy->dp_lane_sel[j])
return dev_err_probe(udphy->dev, -EINVAL,
"set repeat lane mux value\n");
}
}
udphy->mode = UDPHY_MODE_DP;
if (num_lanes == 2) {
udphy->mode |= UDPHY_MODE_USB;
udphy->flip = (udphy->lane_mux_sel[0] == PHY_LANE_MUX_DP);
}
return 0;
}
static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
{
int ret;
u32 value;
ret = clk_bulk_prepare_enable(udphy->num_clks, udphy->clks);
if (ret) {
dev_err(udphy->dev, "failed to enable clk\n");
return ret;
}
rk_udphy_reset_deassert_all(udphy);
regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value))
udphy->status = UDPHY_MODE_DP;
else
rk_udphy_disable(udphy);
return 0;
}
static int rk_udphy_parse_dt(struct rk_udphy *udphy)
{
struct device *dev = udphy->dev;
struct device_node *np = dev_of_node(dev);
enum usb_device_speed maximum_speed;
int ret;
udphy->u2phygrf = syscon_regmap_lookup_by_phandle(np, "rockchip,u2phy-grf");
if (IS_ERR(udphy->u2phygrf))
return dev_err_probe(dev, PTR_ERR(udphy->u2phygrf), "failed to get u2phy-grf\n");
udphy->udphygrf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbdpphy-grf");
if (IS_ERR(udphy->udphygrf))
return dev_err_probe(dev, PTR_ERR(udphy->udphygrf), "failed to get usbdpphy-grf\n");
udphy->usbgrf = syscon_regmap_lookup_by_phandle(np, "rockchip,usb-grf");
if (IS_ERR(udphy->usbgrf))
return dev_err_probe(dev, PTR_ERR(udphy->usbgrf), "failed to get usb-grf\n");
udphy->vogrf = syscon_regmap_lookup_by_phandle(np, "rockchip,vo-grf");
if (IS_ERR(udphy->vogrf))
return dev_err_probe(dev, PTR_ERR(udphy->vogrf), "failed to get vo-grf\n");
ret = rk_udphy_parse_lane_mux_data(udphy);
if (ret)
return ret;
udphy->sbu1_dc_gpio = devm_gpiod_get_optional(dev, "sbu1-dc", GPIOD_OUT_LOW);
if (IS_ERR(udphy->sbu1_dc_gpio))
return PTR_ERR(udphy->sbu1_dc_gpio);
udphy->sbu2_dc_gpio = devm_gpiod_get_optional(dev, "sbu2-dc", GPIOD_OUT_LOW);
if (IS_ERR(udphy->sbu2_dc_gpio))
return PTR_ERR(udphy->sbu2_dc_gpio);
if (device_property_present(dev, "maximum-speed")) {
maximum_speed = usb_get_maximum_speed(dev);
udphy->hs = maximum_speed <= USB_SPEED_HIGH ? true : false;
}
ret = rk_udphy_clk_init(udphy, dev);
if (ret)
return ret;
return rk_udphy_reset_init(udphy, dev);
}
static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
{
int ret;
if (!(udphy->mode & mode)) {
dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
return 0;
}
if (udphy->status == UDPHY_MODE_NONE) {
udphy->mode_change = false;
ret = rk_udphy_setup(udphy);
if (ret)
return ret;
if (udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
} else if (udphy->mode_change) {
udphy->mode_change = false;
udphy->status = UDPHY_MODE_NONE;
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
rk_udphy_disable(udphy);
ret = rk_udphy_setup(udphy);
if (ret)
return ret;
}
udphy->status |= mode;
return 0;
}
static void rk_udphy_power_off(struct rk_udphy *udphy, u8 mode)
{
if (!(udphy->mode & mode)) {
dev_info(udphy->dev, "mode 0x%02x is not support\n", mode);
return;
}
if (!udphy->status)
return;
udphy->status &= ~mode;
if (udphy->status == UDPHY_MODE_NONE)
rk_udphy_disable(udphy);
}
static int rk_udphy_dp_phy_init(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
udphy->dp_in_use = true;
rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
mutex_unlock(&udphy->mutex);
return 0;
}
static int rk_udphy_dp_phy_exit(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
udphy->dp_in_use = false;
mutex_unlock(&udphy->mutex);
return 0;
}
static int rk_udphy_dp_phy_power_on(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
int ret, dp_lanes;
mutex_lock(&udphy->mutex);
dp_lanes = rk_udphy_dplane_get(udphy);
phy_set_bus_width(phy, dp_lanes);
ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
if (ret)
goto unlock;
rk_udphy_dplane_enable(udphy, dp_lanes);
rk_udphy_dplane_select(udphy);
unlock:
mutex_unlock(&udphy->mutex);
/*
* If data send by aux channel too fast after phy power on,
* the aux may be not ready which will cause aux error. Adding
* delay to avoid this issue.
*/
usleep_range(10000, 11000);
return ret;
}
static int rk_udphy_dp_phy_power_off(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
rk_udphy_dplane_enable(udphy, 0);
rk_udphy_power_off(udphy, UDPHY_MODE_DP);
mutex_unlock(&udphy->mutex);
return 0;
}
static int rk_udphy_dp_phy_verify_link_rate(unsigned int link_rate)
{
switch (link_rate) {
case 1620:
case 2700:
case 5400:
case 8100:
break;
default:
return -EINVAL;
}
return 0;
}
static int rk_udphy_dp_phy_verify_config(struct rk_udphy *udphy,
struct phy_configure_opts_dp *dp)
{
int i, ret;
/* If changing link rate was required, verify it's supported. */
ret = rk_udphy_dp_phy_verify_link_rate(dp->link_rate);
if (ret)
return ret;
/* Verify lane count. */
switch (dp->lanes) {
case 1:
case 2:
case 4:
/* valid lane count. */
break;
default:
return -EINVAL;
}
/*
* If changing voltages is required, check swing and pre-emphasis
* levels, per-lane.
*/
if (dp->set_voltages) {
/* Lane count verified previously. */
for (i = 0; i < dp->lanes; i++) {
if (dp->voltage[i] > 3 || dp->pre[i] > 3)
return -EINVAL;
/*
* Sum of voltage swing and pre-emphasis levels cannot
* exceed 3.
*/
if (dp->voltage[i] + dp->pre[i] > 3)
return -EINVAL;
}
}
return 0;
}
static void rk_udphy_dp_set_voltage(struct rk_udphy *udphy, u8 bw,
u32 voltage, u32 pre, u32 lane)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
const struct rk_udphy_dp_tx_drv_ctrl (*dp_ctrl)[4];
u32 offset = 0x800 * lane;
u32 val;
if (udphy->mux)
dp_ctrl = cfg->dp_tx_ctrl_cfg_typec[bw];
else
dp_ctrl = cfg->dp_tx_ctrl_cfg[bw];
val = dp_ctrl[voltage][pre].trsv_reg0204;
regmap_write(udphy->pma_regmap, 0x0810 + offset, val);
val = dp_ctrl[voltage][pre].trsv_reg0205;
regmap_write(udphy->pma_regmap, 0x0814 + offset, val);
val = dp_ctrl[voltage][pre].trsv_reg0206;
regmap_write(udphy->pma_regmap, 0x0818 + offset, val);
val = dp_ctrl[voltage][pre].trsv_reg0207;
regmap_write(udphy->pma_regmap, 0x081c + offset, val);
}
static int rk_udphy_dp_phy_configure(struct phy *phy,
union phy_configure_opts *opts)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
struct phy_configure_opts_dp *dp = &opts->dp;
u32 i, val, lane;
int ret;
ret = rk_udphy_dp_phy_verify_config(udphy, dp);
if (ret)
return ret;
if (dp->set_rate) {
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
CMN_DP_CMN_RSTN, FIELD_PREP(CMN_DP_CMN_RSTN, 0x0));
switch (dp->link_rate) {
case 1620:
udphy->bw = DP_BW_RBR;
break;
case 2700:
udphy->bw = DP_BW_HBR;
break;
case 5400:
udphy->bw = DP_BW_HBR2;
break;
case 8100:
udphy->bw = DP_BW_HBR3;
break;
default:
return -EINVAL;
}
regmap_update_bits(udphy->pma_regmap, CMN_DP_LINK_OFFSET, CMN_DP_TX_LINK_BW,
FIELD_PREP(CMN_DP_TX_LINK_BW, udphy->bw));
regmap_update_bits(udphy->pma_regmap, CMN_SSC_EN_OFFSET, CMN_ROPLL_SSC_EN,
FIELD_PREP(CMN_ROPLL_SSC_EN, dp->ssc));
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET, CMN_DP_CMN_RSTN,
FIELD_PREP(CMN_DP_CMN_RSTN, 0x1));
ret = regmap_read_poll_timeout(udphy->pma_regmap, CMN_ANA_ROPLL_DONE_OFFSET, val,
FIELD_GET(CMN_ANA_ROPLL_LOCK_DONE, val) &&
FIELD_GET(CMN_ANA_ROPLL_AFC_DONE, val),
0, 1000);
if (ret) {
dev_err(udphy->dev, "ROPLL is not lock, set_rate failed\n");
return ret;
}
}
if (dp->set_voltages) {
for (i = 0; i < dp->lanes; i++) {
lane = udphy->dp_lane_sel[i];
switch (dp->link_rate) {
case 1620:
case 2700:
regmap_update_bits(udphy->pma_regmap,
TRSV_ANA_TX_CLK_OFFSET_N(lane),
LN_ANA_TX_SER_TXCLK_INV,
FIELD_PREP(LN_ANA_TX_SER_TXCLK_INV,
udphy->lane_mux_sel[lane]));
break;
case 5400:
case 8100:
regmap_update_bits(udphy->pma_regmap,
TRSV_ANA_TX_CLK_OFFSET_N(lane),
LN_ANA_TX_SER_TXCLK_INV,
FIELD_PREP(LN_ANA_TX_SER_TXCLK_INV, 0x0));
break;
}
rk_udphy_dp_set_voltage(udphy, udphy->bw, dp->voltage[i],
dp->pre[i], lane);
}
}
return 0;
}
static const struct phy_ops rk_udphy_dp_phy_ops = {
.init = rk_udphy_dp_phy_init,
.exit = rk_udphy_dp_phy_exit,
.power_on = rk_udphy_dp_phy_power_on,
.power_off = rk_udphy_dp_phy_power_off,
.configure = rk_udphy_dp_phy_configure,
.owner = THIS_MODULE,
};
static int rk_udphy_usb3_phy_init(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
int ret = 0;
mutex_lock(&udphy->mutex);
/* DP only or high-speed, disable U3 port */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
rk_udphy_u3_port_disable(udphy, true);
goto unlock;
}
ret = rk_udphy_power_on(udphy, UDPHY_MODE_USB);
unlock:
mutex_unlock(&udphy->mutex);
return ret;
}
static int rk_udphy_usb3_phy_exit(struct phy *phy)
{
struct rk_udphy *udphy = phy_get_drvdata(phy);
mutex_lock(&udphy->mutex);
/* DP only or high-speed */
if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
goto unlock;
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
unlock:
mutex_unlock(&udphy->mutex);
return 0;
}
static const struct phy_ops rk_udphy_usb3_phy_ops = {
.init = rk_udphy_usb3_phy_init,
.exit = rk_udphy_usb3_phy_exit,
.owner = THIS_MODULE,
};
static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
u8 mode;
mutex_lock(&udphy->mutex);
switch (state->mode) {
case TYPEC_DP_STATE_C:
case TYPEC_DP_STATE_E:
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
mode = UDPHY_MODE_DP;
break;
case TYPEC_DP_STATE_D:
default:
if (udphy->flip) {
udphy->lane_mux_sel[0] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_USB;
} else {
udphy->lane_mux_sel[0] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[1] = PHY_LANE_MUX_USB;
udphy->lane_mux_sel[2] = PHY_LANE_MUX_DP;
udphy->lane_mux_sel[3] = PHY_LANE_MUX_DP;
}
mode = UDPHY_MODE_DP_USB;
break;
}
if (state->alt && state->alt->svid == USB_TYPEC_DP_SID) {
struct typec_displayport_data *data = state->data;
if (!data) {
rk_udphy_dp_hpd_event_trigger(udphy, false);
} else if (data->status & DP_STATUS_IRQ_HPD) {
rk_udphy_dp_hpd_event_trigger(udphy, false);
usleep_range(750, 800);
rk_udphy_dp_hpd_event_trigger(udphy, true);
} else if (data->status & DP_STATUS_HPD_STATE) {
if (udphy->mode != mode) {
udphy->mode = mode;
udphy->mode_change = true;
}
rk_udphy_dp_hpd_event_trigger(udphy, true);
} else {
rk_udphy_dp_hpd_event_trigger(udphy, false);
}
}
mutex_unlock(&udphy->mutex);
return 0;
}
static void rk_udphy_typec_mux_unregister(void *data)
{
struct rk_udphy *udphy = data;
typec_mux_unregister(udphy->mux);
}
static int rk_udphy_setup_typec_mux(struct rk_udphy *udphy)
{
struct typec_mux_desc mux_desc = {};
mux_desc.drvdata = udphy;
mux_desc.fwnode = dev_fwnode(udphy->dev);
mux_desc.set = rk_udphy_typec_mux_set;
udphy->mux = typec_mux_register(udphy->dev, &mux_desc);
if (IS_ERR(udphy->mux)) {
dev_err(udphy->dev, "Error register typec mux: %ld\n",
PTR_ERR(udphy->mux));
return PTR_ERR(udphy->mux);
}
return devm_add_action_or_reset(udphy->dev, rk_udphy_typec_mux_unregister,
udphy);
}
static const struct regmap_config rk_udphy_pma_regmap_cfg = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.fast_io = true,
.max_register = 0x20dc,
};
static struct phy *rk_udphy_phy_xlate(struct device *dev, const struct of_phandle_args *args)
{
struct rk_udphy *udphy = dev_get_drvdata(dev);
if (args->args_count == 0)
return ERR_PTR(-EINVAL);
switch (args->args[0]) {
case PHY_TYPE_USB3:
return udphy->phy_u3;
case PHY_TYPE_DP:
return udphy->phy_dp;
}
return ERR_PTR(-EINVAL);
}
static int rk_udphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
struct resource *res;
struct rk_udphy *udphy;
void __iomem *base;
int id, ret;
udphy = devm_kzalloc(dev, sizeof(*udphy), GFP_KERNEL);
if (!udphy)
return -ENOMEM;
udphy->cfgs = device_get_match_data(dev);
if (!udphy->cfgs)
return dev_err_probe(dev, -EINVAL, "missing match data\n");
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
/* find the phy-id from the io address */
udphy->id = -ENODEV;
for (id = 0; id < udphy->cfgs->num_phys; id++) {
if (res->start == udphy->cfgs->phy_ids[id]) {
udphy->id = id;
break;
}
}
if (udphy->id < 0)
return dev_err_probe(dev, -ENODEV, "no matching device found\n");
udphy->pma_regmap = devm_regmap_init_mmio(dev, base + UDPHY_PMA,
&rk_udphy_pma_regmap_cfg);
if (IS_ERR(udphy->pma_regmap))
return PTR_ERR(udphy->pma_regmap);
udphy->dev = dev;
ret = rk_udphy_parse_dt(udphy);
if (ret)
return ret;
ret = rk_udphy_get_initial_status(udphy);
if (ret)
return ret;
mutex_init(&udphy->mutex);
platform_set_drvdata(pdev, udphy);
if (device_property_present(dev, "orientation-switch")) {
ret = rk_udphy_setup_orien_switch(udphy);
if (ret)
return ret;
}
if (device_property_present(dev, "mode-switch")) {
ret = rk_udphy_setup_typec_mux(udphy);
if (ret)
return ret;
}
udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops);
if (IS_ERR(udphy->phy_u3)) {
ret = PTR_ERR(udphy->phy_u3);
return dev_err_probe(dev, ret, "failed to create USB3 phy\n");
}
phy_set_drvdata(udphy->phy_u3, udphy);
udphy->phy_dp = devm_phy_create(dev, dev->of_node, &rk_udphy_dp_phy_ops);
if (IS_ERR(udphy->phy_dp)) {
ret = PTR_ERR(udphy->phy_dp);
return dev_err_probe(dev, ret, "failed to create DP phy\n");
}
phy_set_bus_width(udphy->phy_dp, rk_udphy_dplane_get(udphy));
udphy->phy_dp->attrs.max_link_rate = 8100;
phy_set_drvdata(udphy->phy_dp, udphy);
phy_provider = devm_of_phy_provider_register(dev, rk_udphy_phy_xlate);
if (IS_ERR(phy_provider)) {
ret = PTR_ERR(phy_provider);
return dev_err_probe(dev, ret, "failed to register phy provider\n");
}
return 0;
}
static int __maybe_unused rk_udphy_resume(struct device *dev)
{
struct rk_udphy *udphy = dev_get_drvdata(dev);
if (udphy->dp_sink_hpd_sel)
rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
return 0;
}
static const struct dev_pm_ops rk_udphy_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, rk_udphy_resume)
};
static const char * const rk_udphy_rst_list[] = {
"init", "cmn", "lane", "pcs_apb", "pma_apb"
};
static const struct rk_udphy_cfg rk3588_udphy_cfgs = {
.num_phys = 2,
.phy_ids = {
0xfed80000,
0xfed90000,
},
.num_rsts = ARRAY_SIZE(rk_udphy_rst_list),
.rst_list = rk_udphy_rst_list,
.grfcfg = {
/* u2phy-grf */
.bvalid_phy_con = RK_UDPHY_GEN_GRF_REG(0x0008, 1, 0, 0x2, 0x3),
.bvalid_grf_con = RK_UDPHY_GEN_GRF_REG(0x0010, 3, 2, 0x2, 0x3),
/* usb-grf */
.usb3otg0_cfg = RK_UDPHY_GEN_GRF_REG(0x001c, 15, 0, 0x1100, 0x0188),
.usb3otg1_cfg = RK_UDPHY_GEN_GRF_REG(0x0034, 15, 0, 0x1100, 0x0188),
/* usbdpphy-grf */
.low_pwrn = RK_UDPHY_GEN_GRF_REG(0x0004, 13, 13, 0, 1),
.rx_lfps = RK_UDPHY_GEN_GRF_REG(0x0004, 14, 14, 0, 1),
},
.vogrfcfg = {
{
.hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
.dp_lane_reg = 0x0000,
},
{
.hpd_trigger = RK_UDPHY_GEN_GRF_REG(0x0008, 11, 10, 1, 3),
.dp_lane_reg = 0x0008,
},
},
.dp_tx_ctrl_cfg = {
rk3588_dp_tx_drv_ctrl_rbr_hbr,
rk3588_dp_tx_drv_ctrl_rbr_hbr,
rk3588_dp_tx_drv_ctrl_hbr2,
rk3588_dp_tx_drv_ctrl_hbr3,
},
.dp_tx_ctrl_cfg_typec = {
rk3588_dp_tx_drv_ctrl_rbr_hbr_typec,
rk3588_dp_tx_drv_ctrl_rbr_hbr_typec,
rk3588_dp_tx_drv_ctrl_hbr2,
rk3588_dp_tx_drv_ctrl_hbr3,
},
};
static const struct of_device_id rk_udphy_dt_match[] = {
{
.compatible = "rockchip,rk3588-usbdp-phy",
.data = &rk3588_udphy_cfgs
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rk_udphy_dt_match);
static struct platform_driver rk_udphy_driver = {
.probe = rk_udphy_probe,
.driver = {
.name = "rockchip-usbdp-phy",
.of_match_table = rk_udphy_dt_match,
.pm = &rk_udphy_pm_ops,
},
};
module_platform_driver(rk_udphy_driver);
MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
MODULE_AUTHOR("Zhang Yubing <yubing.zhang@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip USBDP Combo PHY driver");
MODULE_LICENSE("GPL");
......@@ -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
......
......@@ -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,
};
......@@ -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,
};
......@@ -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,
};
// 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,
};
......@@ -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,
}, {
......
......@@ -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_ */
......@@ -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,
......
......@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment