summaryrefslogtreecommitdiff
path: root/drivers/phy
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-07-06 07:38:13 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2023-07-06 07:38:13 +0300
commit7afb9d76bc513cb8a2409092dbd3610524a198fe (patch)
tree04afd841f933a2311296e828544ec181fa18e8f4 /drivers/phy
parent6843306689aff3aea608e4d2630b2a5a0137f827 (diff)
parenta454850a815e62fa5d7c1eded0e8d56742613b94 (diff)
downloadlinux-7afb9d76bc513cb8a2409092dbd3610524a198fe.tar.xz
Merge tag 'phy-for-6.5_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy
Pull phy updates from Vinod Koul: "New Support: - Debugfs support for phy core and mediatek driver - Hisilicon inno-usb2-phy driver supporting Hi3798MV100 - Qualcomm SGMII SerDes PHY driver, SM6115 & QCM2290 QMP-USB support, SA8775P USB PHY & USB3 UNI support, QUSB2 support for IPQ9574, IPQ9574 USB3 PHY UpdatesL - Sparx5 serdes phy power optimzation - cadence salvo usb properties and updates and torrent DP with PCIe & USB support - Yaml conversion for Broadcom kona USB bindings and MXS USB binding" * tag 'phy-for-6.5_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy: (67 commits) dt-bindings: phy: brcm,brcmstb-usb-phy: Fix error in "compatible" conditional schema dt-bindings: phy: mixel,mipi-dsi-phy: Remove assigned-clock* properties dt-bindings: phy: intel,combo-phy: restrict node name suffixes dt-bindings: phy: qcom,usb-hs-phy: Add compatible phy: tegra: xusb: check return value of devm_kzalloc() phy: qcom: qmp-combo: fix Display Port PHY configuration for SM8550 phy: qcom: add the SGMII SerDes PHY driver dt-bindings: phy: describe the Qualcomm SGMII PHY phy: qualcomm: fix indentation in Makefile phy: usb: suppress OC condition for 7439b2 phy: usb: Turn off phy when port is in suspend phy: tegra: xusb: Clear the driver reference in usb-phy dev dt-bindings: phy: mxs-usb-phy: add imx8ulp and imx8qm compatible dt-bindings: phy: mxs-usb-phy: convert to DT schema format dt-bindings: phy: qcom,qmp-usb: fix bindings error dt-bindings: phy: qcom,qmp-ufs: fix the sc8180x regs dt-bindings: phy: qcom,qmp-pcie: fix the sc8180x regs phy: mediatek: tphy: add debugfs files phy: core: add debugfs files phy: fsl-imx8mp-usb: add support for phy tuning ...
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/amlogic/phy-meson-g12a-usb2.c15
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c15
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init.c34
-rw-r--r--drivers/phy/broadcom/phy-brcm-usb-init.h8
-rw-r--r--drivers/phy/cadence/phy-cadence-salvo.c95
-rw-r--r--drivers/phy/cadence/phy-cadence-torrent.c720
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8m-pcie.c4
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c218
-rw-r--r--drivers/phy/hisilicon/Kconfig2
-rw-r--r--drivers/phy/hisilicon/phy-hisi-inno-usb2.c62
-rw-r--r--drivers/phy/mediatek/phy-mtk-tphy.c356
-rw-r--r--drivers/phy/microchip/sparx5_serdes.c426
-rw-r--r--drivers/phy/microchip/sparx5_serdes.h1
-rw-r--r--drivers/phy/microchip/sparx5_serdes_regs.h106
-rw-r--r--drivers/phy/phy-core.c14
-rw-r--r--drivers/phy/qualcomm/Kconfig12
-rw-r--r--drivers/phy/qualcomm/Makefile3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-combo.c263
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-usb.c182
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qusb2.c3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-sgmii-eth.c451
-rw-r--r--drivers/phy/samsung/Kconfig2
-rw-r--r--drivers/phy/tegra/xusb.c4
-rw-r--r--drivers/phy/ti/phy-gmii-sel.c34
24 files changed, 2584 insertions, 446 deletions
diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb2.c b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
index 9d1efa0d9394..ec2555bb83d5 100644
--- a/drivers/phy/amlogic/phy-meson-g12a-usb2.c
+++ b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
@@ -172,10 +172,16 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
int ret;
unsigned int value;
- ret = reset_control_reset(priv->reset);
+ ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
+ ret = reset_control_reset(priv->reset);
+ if (ret) {
+ clk_disable_unprepare(priv->clk);
+ return ret;
+ }
+
udelay(RESET_COMPLETE_TIME);
/* usb2_otg_aca_en == 0 */
@@ -277,8 +283,13 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
static int phy_meson_g12a_usb2_exit(struct phy *phy)
{
struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = reset_control_reset(priv->reset);
+ if (!ret)
+ clk_disable_unprepare(priv->clk);
- return reset_control_reset(priv->reset);
+ return ret;
}
/* set_mode is not needed, mode setting is handled via the UTMI bus */
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
index 76cf4280d7ed..4c10cafded4e 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
+++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
@@ -59,6 +59,8 @@
#define USB_CTLR_TP_DIAG1_wake_MASK BIT(1)
#define USB_CTRL_CTLR_CSHCR 0x50
#define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK BIT(18)
+#define USB_CTRL_P0_U2PHY_CFG1 0x68
+#define USB_CTRL_P0_U2PHY_CFG1_COMMONONN_MASK BIT(10)
/* Register definitions for the USB_PHY block in 7211b0 */
#define USB_PHY_PLL_CTL 0x00
@@ -90,6 +92,8 @@
#define BDC_EC_AXIRDA_RTS_MASK GENMASK(31, 28)
#define BDC_EC_AXIRDA_RTS_SHIFT 28
+#define USB_XHCI_GBL_GUSB2PHYCFG 0x100
+#define USB_XHCI_GBL_GUSB2PHYCFG_U2_FREECLK_EXISTS_MASK BIT(30)
static void usb_mdio_write_7211b0(struct brcm_usb_init_params *params,
uint8_t addr, uint16_t data)
@@ -140,13 +144,17 @@ static void xhci_soft_reset(struct brcm_usb_init_params *params,
int on_off)
{
void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+ void __iomem *xhci_gbl = params->regs[BRCM_REGS_XHCI_GBL];
/* Assert reset */
- if (on_off)
+ if (on_off) {
USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB);
/* De-assert reset */
- else
+ } else {
USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB);
+ /* Required for COMMONONN to be set */
+ USB_XHCI_GBL_UNSET(xhci_gbl, GUSB2PHYCFG, U2_FREECLK_EXISTS);
+ }
}
static void usb_init_ipp(struct brcm_usb_init_params *params)
@@ -320,6 +328,9 @@ static void usb_init_common_7216(struct brcm_usb_init_params *params)
/* 1 millisecond - for USB clocks to settle down */
usleep_range(1000, 2000);
+ /* Disable PHY when port is suspended */
+ USB_CTRL_SET(ctrl, P0_U2PHY_CFG1, COMMONONN);
+
usb_wake_enable_7216(params, false);
usb_init_common(params);
}
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c
index a1ca83308f98..39536b6d96a9 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init.c
+++ b/drivers/phy/broadcom/phy-brcm-usb-init.c
@@ -35,6 +35,11 @@
#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK BIT(25) /* option */
#define USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK BIT(26) /* option */
#define USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK BIT(27) /* opt */
+#define USB_CTRL_SETUP_OC_DISABLE_PORT0_MASK BIT(28)
+#define USB_CTRL_SETUP_OC_DISABLE_PORT1_MASK BIT(29)
+#define USB_CTRL_SETUP_OC_DISABLE_MASK GENMASK(29, 28) /* option */
+#define USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK BIT(30)
+#define USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK BIT(31)
#define USB_CTRL_SETUP_OC3_DISABLE_MASK GENMASK(31, 30) /* option */
#define USB_CTRL_PLL_CTL 0x04
#define USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK BIT(27)
@@ -114,6 +119,8 @@ enum {
USB_CTRL_SETUP_SCB2_EN_SELECTOR,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR,
USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR,
USB_CTRL_SETUP_OC3_DISABLE_SELECTOR,
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR,
USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR,
@@ -190,6 +197,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
@@ -232,6 +241,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
@@ -253,6 +264,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
0, /* USB_CTRL_SETUP_SCB2_EN_MASK */
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
@@ -274,6 +287,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
@@ -295,6 +310,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
@@ -316,6 +333,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
+ 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK */
+ 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK */
0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
@@ -337,6 +356,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
@@ -358,6 +379,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
USB_CTRL_SETUP_SCB2_EN_MASK,
USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK,
0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK,
0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */
@@ -379,6 +402,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
0, /* USB_CTRL_SETUP_SCB2_EN_MASK */
USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK,
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
@@ -400,6 +425,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
0, /* USB_CTRL_SETUP_SCB2_EN_MASK */
0, /*USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */
USB_CTRL_SETUP_STRAP_IPP_SEL_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK,
+ USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK,
USB_CTRL_SETUP_OC3_DISABLE_MASK,
0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */
USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK,
@@ -872,6 +899,13 @@ static void usb_init_common(struct brcm_usb_init_params *params)
brcmusb_memc_fix(params);
+ /* Workaround for false positive OC for 7439b2 in DRD/Device mode */
+ if ((params->family_id == 0x74390012) &&
+ (params->supported_port_modes != USB_CTLR_MODE_HOST)) {
+ USB_CTRL_SET(ctrl, SETUP, OC_DISABLE_PORT1);
+ USB_CTRL_SET_FAMILY(params, SETUP, OC3_DISABLE_PORT1);
+ }
+
if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h
index f9fbf8fb80e5..c1a88f5cd4cd 100644
--- a/drivers/phy/broadcom/phy-brcm-usb-init.h
+++ b/drivers/phy/broadcom/phy-brcm-usb-init.h
@@ -34,6 +34,14 @@ enum brcmusb_reg_sel {
brcm_usb_ctrl_unset(USB_CTRL_REG(base, reg), \
USB_CTRL_##reg##_##field##_MASK)
+#define USB_XHCI_GBL_REG(base, reg) ((void __iomem *)base + USB_XHCI_GBL_##reg)
+#define USB_XHCI_GBL_SET(base, reg, field) \
+ brcm_usb_ctrl_set(USB_XHCI_GBL_REG(base, reg), \
+ USB_XHCI_GBL_##reg##_##field##_MASK)
+#define USB_XHCI_GBL_UNSET(base, reg, field) \
+ brcm_usb_ctrl_unset(USB_XHCI_GBL_REG(base, reg), \
+ USB_XHCI_GBL_##reg##_##field##_MASK)
+
struct brcm_usb_init_params;
struct brcm_usb_init_ops {
diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c
index e569f5f67578..f461585c84c6 100644
--- a/drivers/phy/cadence/phy-cadence-salvo.c
+++ b/drivers/phy/cadence/phy-cadence-salvo.c
@@ -6,6 +6,7 @@
* Copyright (c) 2019-2020 NXP
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -15,7 +16,9 @@
#include <linux/of.h>
#include <linux/of_platform.h>
-/* PHY register definition */
+#define USB3_PHY_OFFSET 0x0
+#define USB2_PHY_OFFSET 0x38000
+/* USB3 PHY register definition */
#define PHY_PMA_CMN_CTRL1 0xC800
#define TB_ADDR_CMN_DIAG_HSCLK_SEL 0x01e0
#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR 0x0084
@@ -87,8 +90,35 @@
#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2
#define TB_ADDR_TX_RCVDETSC_CTRL 0x4124
+/* USB2 PHY register definition */
+#define UTMI_REG15 0xaf
+#define UTMI_AFE_RX_REG0 0x0d
+#define UTMI_AFE_RX_REG5 0x12
+#define UTMI_AFE_BC_REG4 0x29
+
+/* Align UTMI_AFE_RX_REG0 bit[7:6] define */
+enum usb2_disconn_threshold {
+ USB2_DISCONN_THRESHOLD_575 = 0x0,
+ USB2_DISCONN_THRESHOLD_610 = 0x1,
+ USB2_DISCONN_THRESHOLD_645 = 0x3,
+};
+
+#define RX_USB2_DISCONN_MASK GENMASK(7, 6)
+
/* TB_ADDR_TX_RCVDETSC_CTRL */
#define RXDET_IN_P3_32KHZ BIT(0)
+/*
+ * UTMI_REG15
+ *
+ * Gate how many us for the txvalid signal until analog
+ * HS/FS transmitters have powered up
+ */
+#define TXVALID_GATE_THRESHOLD_HS_MASK (BIT(4) | BIT(5))
+/* 0us, txvalid is ready just after HS/FS transmitters have powered up */
+#define TXVALID_GATE_THRESHOLD_HS_0US (BIT(4) | BIT(5))
+
+#define SET_B_SESSION_VALID (BIT(6) | BIT(5))
+#define CLR_B_SESSION_VALID (BIT(6))
struct cdns_reg_pairs {
u16 val;
@@ -106,19 +136,27 @@ struct cdns_salvo_phy {
struct clk *clk;
void __iomem *base;
struct cdns_salvo_data *data;
+ enum usb2_disconn_threshold usb2_disconn;
};
static const struct of_device_id cdns_salvo_phy_of_match[];
-static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 reg)
+static const struct cdns_salvo_data cdns_nxp_salvo_data;
+
+static bool cdns_is_nxp_phy(struct cdns_salvo_phy *salvo_phy)
+{
+ return salvo_phy->data == &cdns_nxp_salvo_data;
+}
+
+static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 offset, u32 reg)
{
- return (u16)readl(salvo_phy->base +
+ return (u16)readl(salvo_phy->base + offset +
reg * (1 << salvo_phy->data->reg_offset_shift));
}
-static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy,
+static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, u32 offset,
u32 reg, u16 val)
{
- writel(val, salvo_phy->base +
+ writel(val, salvo_phy->base + offset +
reg * (1 << salvo_phy->data->reg_offset_shift));
}
@@ -219,15 +257,27 @@ static int cdns_salvo_phy_init(struct phy *phy)
for (i = 0; i < data->init_sequence_length; i++) {
const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;
- cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val);
+ cdns_salvo_write(salvo_phy, USB3_PHY_OFFSET, reg_pair->off, reg_pair->val);
}
/* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */
- value = cdns_salvo_read(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL);
+ value = cdns_salvo_read(salvo_phy, USB3_PHY_OFFSET, TB_ADDR_TX_RCVDETSC_CTRL);
value |= RXDET_IN_P3_32KHZ;
- cdns_salvo_write(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL,
+ cdns_salvo_write(salvo_phy, USB3_PHY_OFFSET, TB_ADDR_TX_RCVDETSC_CTRL,
RXDET_IN_P3_32KHZ);
+ value = cdns_salvo_read(salvo_phy, USB2_PHY_OFFSET, UTMI_REG15);
+ value &= ~TXVALID_GATE_THRESHOLD_HS_MASK;
+ cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_REG15,
+ value | TXVALID_GATE_THRESHOLD_HS_0US);
+
+ cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG5, 0x5);
+
+ value = cdns_salvo_read(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG0);
+ value &= ~RX_USB2_DISCONN_MASK;
+ value = FIELD_PREP(RX_USB2_DISCONN_MASK, salvo_phy->usb2_disconn);
+ cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG0, value);
+
udelay(10);
clk_disable_unprepare(salvo_phy->clk);
@@ -251,11 +301,29 @@ static int cdns_salvo_phy_power_off(struct phy *phy)
return 0;
}
+static int cdns_salvo_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
+
+ if (!cdns_is_nxp_phy(salvo_phy))
+ return 0;
+
+ if (mode == PHY_MODE_USB_DEVICE)
+ cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4,
+ SET_B_SESSION_VALID);
+ else
+ cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4,
+ CLR_B_SESSION_VALID);
+
+ return 0;
+}
+
static const struct phy_ops cdns_salvo_phy_ops = {
.init = cdns_salvo_phy_init,
.power_on = cdns_salvo_phy_power_on,
.power_off = cdns_salvo_phy_power_off,
.owner = THIS_MODULE,
+ .set_mode = cdns_salvo_set_mode,
};
static int cdns_salvo_phy_probe(struct platform_device *pdev)
@@ -264,6 +332,7 @@ static int cdns_salvo_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct cdns_salvo_phy *salvo_phy;
struct cdns_salvo_data *data;
+ u32 val;
data = (struct cdns_salvo_data *)of_device_get_match_data(dev);
salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL);
@@ -275,6 +344,16 @@ static int cdns_salvo_phy_probe(struct platform_device *pdev)
if (IS_ERR(salvo_phy->clk))
return PTR_ERR(salvo_phy->clk);
+ if (of_property_read_u32(dev->of_node, "cdns,usb2-disconnect-threshold-microvolt", &val))
+ val = 575;
+
+ if (val < 610)
+ salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_575;
+ else if (val < 645)
+ salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_610;
+ else
+ salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_645;
+
salvo_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(salvo_phy->base))
return PTR_ERR(salvo_phy->base);
diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c
index 62e59d1bb9c3..37b6b5c05be8 100644
--- a/drivers/phy/cadence/phy-cadence-torrent.c
+++ b/drivers/phy/cadence/phy-cadence-torrent.c
@@ -38,6 +38,9 @@
#define POLL_TIMEOUT_US 5000
#define PLL_LOCK_TIMEOUT 100000
+#define DP_PLL0 BIT(0)
+#define DP_PLL1 BIT(1)
+
#define TORRENT_COMMON_CDB_OFFSET 0x0
#define TORRENT_TX_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \
@@ -66,16 +69,11 @@
*/
#define PHY_AUX_CTRL 0x04
#define PHY_RESET 0x20
-#define PMA_TX_ELEC_IDLE_MASK 0xF0U
#define PMA_TX_ELEC_IDLE_SHIFT 4
-#define PHY_L00_RESET_N_MASK 0x01U
#define PHY_PMA_XCVR_PLLCLK_EN 0x24
#define PHY_PMA_XCVR_PLLCLK_EN_ACK 0x28
#define PHY_PMA_XCVR_POWER_STATE_REQ 0x2c
-#define PHY_POWER_STATE_LN_0 0x0000
-#define PHY_POWER_STATE_LN_1 0x0008
-#define PHY_POWER_STATE_LN_2 0x0010
-#define PHY_POWER_STATE_LN_3 0x0018
+#define PHY_POWER_STATE_LN(ln) ((ln) * 8)
#define PMA_XCVR_POWER_STATE_REQ_LN_MASK 0x3FU
#define PHY_PMA_XCVR_POWER_STATE_ACK 0x30
#define PHY_PMA_CMN_READY 0x34
@@ -323,6 +321,7 @@ struct cdns_torrent_phy {
void __iomem *base; /* DPTX registers base */
void __iomem *sd_base; /* SD0801 registers base */
u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
+ u32 dp_pll;
struct reset_control *phy_rst;
struct reset_control *apb_rst;
struct device *dev;
@@ -905,88 +904,129 @@ void cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(struct cdns_torrent_phy *cdns_phy,
/* Setting VCO for 10.8GHz */
case 2700:
case 5400:
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0028);
- cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_FBH_OVRD_M0, 0x0022);
- cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBH_OVRD_M0, 0x0022);
- cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBL_OVRD_M0, 0x000C);
+ if (cdns_phy->dp_pll & DP_PLL0)
+ cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_FBH_OVRD_M0, 0x0022);
+
+ if (cdns_phy->dp_pll & DP_PLL1) {
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0028);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBH_OVRD_M0, 0x0022);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBL_OVRD_M0, 0x000C);
+ }
break;
/* Setting VCO for 9.72GHz */
case 1620:
case 2430:
case 3240:
- cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
- cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
- cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0061);
- cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0061);
- cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x3333);
- cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x3333);
- cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0042);
- cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0042);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ if (cdns_phy->dp_pll & DP_PLL0) {
+ cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0061);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x3333);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0042);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ }
+ if (cdns_phy->dp_pll & DP_PLL1) {
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0061);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x3333);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0042);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ }
break;
/* Setting VCO for 8.64GHz */
case 2160:
case 4320:
- cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
- cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
- cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0056);
- cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0056);
- cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x6666);
- cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x6666);
- cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x003A);
- cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x003A);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ if (cdns_phy->dp_pll & DP_PLL0) {
+ cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0056);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x6666);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x003A);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ }
+ if (cdns_phy->dp_pll & DP_PLL1) {
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0056);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x6666);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x003A);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ }
break;
/* Setting VCO for 8.1GHz */
case 8100:
- cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
- cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
- cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0051);
- cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0051);
- cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0036);
- cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0036);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
- cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ if (cdns_phy->dp_pll & DP_PLL0) {
+ cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0051);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0036);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+ }
+ if (cdns_phy->dp_pll & DP_PLL1) {
+ cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0051);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+ cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0036);
+ cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+ }
break;
}
}
+/* Set PLL used for DP configuration */
+static int cdns_torrent_dp_get_pll(struct cdns_torrent_phy *cdns_phy,
+ enum cdns_torrent_phy_type phy_t2)
+{
+ switch (phy_t2) {
+ case TYPE_PCIE:
+ case TYPE_USB:
+ cdns_phy->dp_pll = DP_PLL1;
+ break;
+ case TYPE_SGMII:
+ case TYPE_QSGMII:
+ cdns_phy->dp_pll = DP_PLL0;
+ break;
+ case TYPE_NONE:
+ cdns_phy->dp_pll = DP_PLL0 | DP_PLL1;
+ break;
+ default:
+ dev_err(cdns_phy->dev, "Unsupported PHY configuration\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/*
* Enable or disable PLL for selected lanes.
*/
static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
struct phy_configure_opts_dp *dp,
bool enable)
{
- u32 rd_val;
- u32 ret;
struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+ u32 rd_val, pll_ack_val;
+ int ret;
/*
* Used to determine, which bits to check for or enable in
@@ -996,28 +1036,18 @@ static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy,
/* Used to enable or disable lanes. */
u32 pll_val;
- /* Select values of registers and mask, depending on enabled lane
- * count.
- */
- switch (dp->lanes) {
- /* lane 0 */
- case (1):
- pll_bits = 0x00000001;
- break;
- /* lanes 0-1 */
- case (2):
- pll_bits = 0x00000003;
- break;
- /* lanes 0-3, all */
- default:
- pll_bits = 0x0000000F;
- break;
- }
+ /* Select values of registers and mask, depending on enabled lane count. */
+ pll_val = cdns_torrent_dp_read(regmap, PHY_PMA_XCVR_PLLCLK_EN);
- if (enable)
- pll_val = pll_bits;
- else
- pll_val = 0x00000000;
+ if (enable) {
+ pll_bits = ((1 << dp->lanes) - 1);
+ pll_val |= pll_bits;
+ pll_ack_val = pll_bits;
+ } else {
+ pll_bits = ((1 << inst->num_lanes) - 1);
+ pll_val &= (~pll_bits);
+ pll_ack_val = 0;
+ }
cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, pll_val);
@@ -1025,22 +1055,23 @@ static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy,
ret = regmap_read_poll_timeout(regmap,
PHY_PMA_XCVR_PLLCLK_EN_ACK,
rd_val,
- (rd_val & pll_bits) == pll_val,
+ (rd_val & pll_bits) == pll_ack_val,
0, POLL_TIMEOUT_US);
ndelay(100);
return ret;
}
static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
u32 num_lanes,
enum phy_powerstate powerstate)
{
/* Register value for power state for a single byte. */
- u32 value_part;
- u32 value;
- u32 mask;
+ u32 value_part, i;
+ u32 value = 0;
+ u32 mask = 0;
u32 read_val;
- u32 ret;
+ int ret;
struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
switch (powerstate) {
@@ -1056,29 +1087,11 @@ static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
break;
}
- /* Select values of registers and mask, depending on enabled
- * lane count.
- */
- switch (num_lanes) {
- /* lane 0 */
- case (1):
- value = value_part;
- mask = 0x0000003FU;
- break;
- /* lanes 0-1 */
- case (2):
- value = (value_part
- | (value_part << 8));
- mask = 0x00003F3FU;
- break;
- /* lanes 0-3, all */
- default:
- value = (value_part
- | (value_part << 8)
- | (value_part << 16)
- | (value_part << 24));
- mask = 0x3F3F3F3FU;
- break;
+ /* Select values of registers and mask, depending on enabled lane count. */
+
+ for (i = 0; i < num_lanes; i++) {
+ value |= (value_part << PHY_POWER_STATE_LN(i));
+ mask |= (PMA_XCVR_POWER_STATE_REQ_LN_MASK << PHY_POWER_STATE_LN(i));
}
/* Set power state A<n>. */
@@ -1093,7 +1106,8 @@ static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
return ret;
}
-static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes)
+static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst, u32 num_lanes)
{
unsigned int read_val;
int ret;
@@ -1114,12 +1128,12 @@ static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes)
ndelay(100);
- ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes,
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, num_lanes,
POWERSTATE_A2);
if (ret)
return ret;
- ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes,
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, num_lanes,
POWERSTATE_A0);
return ret;
@@ -1143,6 +1157,7 @@ static int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy)
}
static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
u32 rate, u32 num_lanes)
{
unsigned int clk_sel_val = 0;
@@ -1175,14 +1190,17 @@ static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy,
break;
}
- cdns_torrent_phy_write(cdns_phy->regmap_common_cdb,
- CMN_PDIAG_PLL0_CLK_SEL_M0, clk_sel_val);
- cdns_torrent_phy_write(cdns_phy->regmap_common_cdb,
- CMN_PDIAG_PLL1_CLK_SEL_M0, clk_sel_val);
+ if (cdns_phy->dp_pll & DP_PLL0)
+ cdns_torrent_phy_write(cdns_phy->regmap_common_cdb,
+ CMN_PDIAG_PLL0_CLK_SEL_M0, clk_sel_val);
+
+ if (cdns_phy->dp_pll & DP_PLL1)
+ cdns_torrent_phy_write(cdns_phy->regmap_common_cdb,
+ CMN_PDIAG_PLL1_CLK_SEL_M0, clk_sel_val);
/* PMA lane configuration to deal with multi-link operation */
for (i = 0; i < num_lanes; i++)
- cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[i],
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + i],
XCVR_DIAG_HSCLK_DIV, hsclk_div_val);
}
@@ -1191,23 +1209,44 @@ static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy,
* set and PLL disable request was processed.
*/
static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
struct phy_configure_opts_dp *dp)
{
- u32 read_val, ret;
+ u32 read_val, field_val;
+ int ret;
- /* Disable the cmn_pll0_en before re-programming the new data rate. */
- regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x0);
+ /*
+ * Disable the associated PLL (cmn_pll0_en or cmn_pll1_en) before
+ * re-programming the new data rate.
+ */
+ ret = regmap_field_read(cdns_phy->phy_pma_pll_raw_ctrl, &field_val);
+ if (ret)
+ return ret;
+ field_val &= ~(cdns_phy->dp_pll);
+ regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, field_val);
/*
* Wait for PLL ready de-assertion.
* For PLL0 - PHY_PMA_CMN_CTRL2[2] == 1
+ * For PLL1 - PHY_PMA_CMN_CTRL2[3] == 1
*/
- ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
- read_val,
- ((read_val >> 2) & 0x01) != 0,
- 0, POLL_TIMEOUT_US);
- if (ret)
- return ret;
+ if (cdns_phy->dp_pll & DP_PLL0) {
+ ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
+ read_val,
+ ((read_val >> 2) & 0x01) != 0,
+ 0, POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+ }
+
+ if ((cdns_phy->dp_pll & DP_PLL1) && cdns_phy->nsubnodes != 1) {
+ ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
+ read_val,
+ ((read_val >> 3) & 0x01) != 0,
+ 0, POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+ }
ndelay(200);
/* DP Rate Change - VCO Output settings. */
@@ -1221,19 +1260,35 @@ static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy,
/* PMA common configuration 100MHz */
cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(cdns_phy, dp->link_rate, dp->ssc);
- cdns_torrent_dp_pma_cmn_rate(cdns_phy, dp->link_rate, dp->lanes);
+ cdns_torrent_dp_pma_cmn_rate(cdns_phy, inst, dp->link_rate, dp->lanes);
- /* Enable the cmn_pll0_en. */
- regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x3);
+ /* Enable the associated PLL (cmn_pll0_en or cmn_pll1_en) */
+ ret = regmap_field_read(cdns_phy->phy_pma_pll_raw_ctrl, &field_val);
+ if (ret)
+ return ret;
+ field_val |= cdns_phy->dp_pll;
+ regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, field_val);
/*
* Wait for PLL ready assertion.
* For PLL0 - PHY_PMA_CMN_CTRL2[0] == 1
+ * For PLL1 - PHY_PMA_CMN_CTRL2[1] == 1
*/
- ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
- read_val,
- (read_val & 0x01) != 0,
- 0, POLL_TIMEOUT_US);
+ if (cdns_phy->dp_pll & DP_PLL0) {
+ ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
+ read_val,
+ (read_val & 0x01) != 0,
+ 0, POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+ }
+
+ if ((cdns_phy->dp_pll & DP_PLL1) && cdns_phy->nsubnodes != 1)
+ ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
+ read_val,
+ ((read_val >> 1) & 0x01) != 0,
+ 0, POLL_TIMEOUT_US);
+
return ret;
}
@@ -1301,6 +1356,7 @@ static int cdns_torrent_dp_verify_config(struct cdns_torrent_inst *inst,
/* Set power state A0 and PLL clock enable to 0 on enabled lanes. */
static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
u32 num_lanes)
{
struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
@@ -1308,27 +1364,13 @@ static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy,
PHY_PMA_XCVR_POWER_STATE_REQ);
u32 pll_clk_en = cdns_torrent_dp_read(regmap,
PHY_PMA_XCVR_PLLCLK_EN);
+ u32 i;
- /* Lane 0 is always enabled. */
- pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
- PHY_POWER_STATE_LN_0);
- pll_clk_en &= ~0x01U;
-
- if (num_lanes > 1) {
- /* lane 1 */
- pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
- PHY_POWER_STATE_LN_1);
- pll_clk_en &= ~(0x01U << 1);
- }
+ for (i = 0; i < num_lanes; i++) {
+ pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK
+ << PHY_POWER_STATE_LN(inst->mlane + i));
- if (num_lanes > 2) {
- /* lanes 2 and 3 */
- pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
- PHY_POWER_STATE_LN_2);
- pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
- PHY_POWER_STATE_LN_3);
- pll_clk_en &= ~(0x01U << 2);
- pll_clk_en &= ~(0x01U << 3);
+ pll_clk_en &= ~(0x01U << (inst->mlane + i));
}
cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, pwr_state);
@@ -1337,36 +1379,57 @@ static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy,
/* Configure lane count as required. */
static int cdns_torrent_dp_set_lanes(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
struct phy_configure_opts_dp *dp)
{
- u32 value;
- u32 ret;
+ u32 value, i;
+ int ret;
struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
u8 lane_mask = (1 << dp->lanes) - 1;
+ u8 pma_tx_elec_idle_mask = 0;
+ u32 clane = inst->mlane;
+
+ lane_mask <<= clane;
value = cdns_torrent_dp_read(regmap, PHY_RESET);
/* clear pma_tx_elec_idle_ln_* bits. */
- value &= ~PMA_TX_ELEC_IDLE_MASK;
+ pma_tx_elec_idle_mask = ((1 << inst->num_lanes) - 1) << clane;
+
+ pma_tx_elec_idle_mask <<= PMA_TX_ELEC_IDLE_SHIFT;
+
+ value &= ~pma_tx_elec_idle_mask;
+
/* Assert pma_tx_elec_idle_ln_* for disabled lanes. */
value |= ((~lane_mask) << PMA_TX_ELEC_IDLE_SHIFT) &
- PMA_TX_ELEC_IDLE_MASK;
+ pma_tx_elec_idle_mask;
+
cdns_torrent_dp_write(regmap, PHY_RESET, value);
- /* reset the link by asserting phy_l00_reset_n low */
+ /* reset the link by asserting master lane phy_l0*_reset_n low */
cdns_torrent_dp_write(regmap, PHY_RESET,
- value & (~PHY_L00_RESET_N_MASK));
+ value & (~(1 << clane)));
/*
- * Assert lane reset on unused lanes and lane 0 so they remain in reset
+ * Assert lane reset on unused lanes and master lane so they remain in reset
* and powered down when re-enabling the link
*/
- value = (value & 0x0000FFF0) | (0x0000000E & lane_mask);
+ for (i = 0; i < inst->num_lanes; i++)
+ value &= (~(1 << (clane + i)));
+
+ for (i = 1; i < inst->num_lanes; i++)
+ value |= ((1 << (clane + i)) & lane_mask);
+
cdns_torrent_dp_write(regmap, PHY_RESET, value);
- cdns_torrent_dp_set_a0_pll(cdns_phy, dp->lanes);
+ cdns_torrent_dp_set_a0_pll(cdns_phy, inst, dp->lanes);
/* release phy_l0*_reset_n based on used laneCount */
- value = (value & 0x0000FFF0) | (0x0000000F & lane_mask);
+ for (i = 0; i < inst->num_lanes; i++)
+ value &= (~(1 << (clane + i)));
+
+ for (i = 0; i < inst->num_lanes; i++)
+ value |= ((1 << (clane + i)) & lane_mask);
+
cdns_torrent_dp_write(regmap, PHY_RESET, value);
/* Wait, until PHY gets ready after releasing PHY reset signal. */
@@ -1377,41 +1440,44 @@ static int cdns_torrent_dp_set_lanes(struct cdns_torrent_phy *cdns_phy,
ndelay(100);
/* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
- cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001);
+ value = cdns_torrent_dp_read(regmap, PHY_PMA_XCVR_PLLCLK_EN);
+ value |= (1 << clane);
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, value);
- ret = cdns_torrent_dp_run(cdns_phy, dp->lanes);
+ ret = cdns_torrent_dp_run(cdns_phy, inst, dp->lanes);
return ret;
}
/* Configure link rate as required. */
static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
struct phy_configure_opts_dp *dp)
{
- u32 ret;
+ int ret;
- ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, dp->lanes,
POWERSTATE_A3);
if (ret)
return ret;
- ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, false);
+ ret = cdns_torrent_dp_set_pll_en(cdns_phy, inst, dp, false);
if (ret)
return ret;
ndelay(200);
- ret = cdns_torrent_dp_configure_rate(cdns_phy, dp);
+ ret = cdns_torrent_dp_configure_rate(cdns_phy, inst, dp);
if (ret)
return ret;
ndelay(200);
- ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, true);
+ ret = cdns_torrent_dp_set_pll_en(cdns_phy, inst, dp, true);
if (ret)
return ret;
- ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, dp->lanes,
POWERSTATE_A2);
if (ret)
return ret;
- ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+ ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, dp->lanes,
POWERSTATE_A0);
if (ret)
return ret;
@@ -1422,44 +1488,45 @@ static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy,
/* Configure voltage swing and pre-emphasis for all enabled lanes. */
static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
struct phy_configure_opts_dp *dp)
{
u8 lane;
u16 val;
for (lane = 0; lane < dp->lanes; lane++) {
- val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane],
+ val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane],
TX_DIAG_ACYA);
/*
* Write 1 to register bit TX_DIAG_ACYA[0] to freeze the
* current state of the analog TX driver.
*/
val |= TX_DIAG_ACYA_HBDC_MASK;
- cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane],
TX_DIAG_ACYA, val);
- cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane],
TX_TXCC_CTRL, 0x08A4);
val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].diag_tx_drv;
- cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane],
DRV_DIAG_TX_DRV, val);
val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].mgnfs_mult;
- cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane],
TX_TXCC_MGNFS_MULT_000,
val);
val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].cpost_mult;
- cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane],
TX_TXCC_CPOST_MULT_00,
val);
- val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane],
+ val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane],
TX_DIAG_ACYA);
/*
* Write 0 to register bit TX_DIAG_ACYA[0] to allow the state of
* analog TX driver to reflect the new programmed one.
*/
val &= ~TX_DIAG_ACYA_HBDC_MASK;
- cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+ cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane],
TX_DIAG_ACYA, val);
}
};
@@ -1478,7 +1545,7 @@ static int cdns_torrent_dp_configure(struct phy *phy,
}
if (opts->dp.set_lanes) {
- ret = cdns_torrent_dp_set_lanes(cdns_phy, &opts->dp);
+ ret = cdns_torrent_dp_set_lanes(cdns_phy, inst, &opts->dp);
if (ret) {
dev_err(&phy->dev, "cdns_torrent_dp_set_lanes failed\n");
return ret;
@@ -1486,7 +1553,7 @@ static int cdns_torrent_dp_configure(struct phy *phy,
}
if (opts->dp.set_rate) {
- ret = cdns_torrent_dp_set_rate(cdns_phy, &opts->dp);
+ ret = cdns_torrent_dp_set_rate(cdns_phy, inst, &opts->dp);
if (ret) {
dev_err(&phy->dev, "cdns_torrent_dp_set_rate failed\n");
return ret;
@@ -1494,7 +1561,7 @@ static int cdns_torrent_dp_configure(struct phy *phy,
}
if (opts->dp.set_voltages)
- cdns_torrent_dp_set_voltages(cdns_phy, &opts->dp);
+ cdns_torrent_dp_set_voltages(cdns_phy, inst, &opts->dp);
return ret;
}
@@ -1562,6 +1629,7 @@ static void cdns_torrent_dp_common_init(struct cdns_torrent_phy *cdns_phy,
{
struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
unsigned char lane_bits;
+ u32 val;
cdns_torrent_dp_write(regmap, PHY_AUX_CTRL, 0x0003); /* enable AUX */
@@ -1569,18 +1637,23 @@ static void cdns_torrent_dp_common_init(struct cdns_torrent_phy *cdns_phy,
* Set lines power state to A0
* Set lines pll clk enable to 0
*/
- cdns_torrent_dp_set_a0_pll(cdns_phy, inst->num_lanes);
+ cdns_torrent_dp_set_a0_pll(cdns_phy, inst, inst->num_lanes);
/*
* release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
* used lanes
*/
lane_bits = (1 << inst->num_lanes) - 1;
- cdns_torrent_dp_write(regmap, PHY_RESET,
- ((0xF & ~lane_bits) << 4) | (0xF & lane_bits));
+
+ val = cdns_torrent_dp_read(regmap, PHY_RESET);
+ val |= (0xF & lane_bits);
+ val &= ~(lane_bits << 4);
+ cdns_torrent_dp_write(regmap, PHY_RESET, val);
/* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
- cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001);
+ val = cdns_torrent_dp_read(regmap, PHY_PMA_XCVR_PLLCLK_EN);
+ val |= 1;
+ cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, val);
/*
* PHY PMA registers configuration functions
@@ -1599,7 +1672,7 @@ static void cdns_torrent_dp_common_init(struct cdns_torrent_phy *cdns_phy,
cdns_phy->max_bit_rate,
false);
- cdns_torrent_dp_pma_cmn_rate(cdns_phy, cdns_phy->max_bit_rate,
+ cdns_torrent_dp_pma_cmn_rate(cdns_phy, inst, cdns_phy->max_bit_rate,
inst->num_lanes);
/* take out of reset */
@@ -1612,13 +1685,15 @@ static int cdns_torrent_dp_start(struct cdns_torrent_phy *cdns_phy,
{
int ret;
- cdns_torrent_phy_on(phy);
+ ret = cdns_torrent_phy_on(phy);
+ if (ret)
+ return ret;
ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy);
if (ret)
return ret;
- ret = cdns_torrent_dp_run(cdns_phy, inst->num_lanes);
+ ret = cdns_torrent_dp_run(cdns_phy, inst, inst->num_lanes);
return ret;
}
@@ -1627,6 +1702,7 @@ static int cdns_torrent_dp_init(struct phy *phy)
{
struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+ int ret;
switch (cdns_phy->ref_clk_rate) {
case CLK_19_2_MHZ:
@@ -1639,6 +1715,24 @@ static int cdns_torrent_dp_init(struct phy *phy)
return -EINVAL;
}
+ ret = cdns_torrent_dp_get_pll(cdns_phy, TYPE_NONE);
+ if (ret)
+ return ret;
+
+ cdns_torrent_dp_common_init(cdns_phy, inst);
+
+ return cdns_torrent_dp_start(cdns_phy, inst, phy);
+}
+
+static int cdns_torrent_dp_multilink_init(struct cdns_torrent_phy *cdns_phy,
+ struct cdns_torrent_inst *inst,
+ struct phy *phy)
+{
+ if (cdns_phy->ref_clk_rate != CLK_100_MHZ) {
+ dev_err(cdns_phy->dev, "Unsupported Ref Clock Rate\n");
+ return -EINVAL;
+ }
+
cdns_torrent_dp_common_init(cdns_phy, inst);
return cdns_torrent_dp_start(cdns_phy, inst, phy);
@@ -2156,8 +2250,11 @@ static int cdns_torrent_phy_init(struct phy *phy)
u32 num_regs;
int i, j;
- if (cdns_phy->nsubnodes > 1)
+ if (cdns_phy->nsubnodes > 1) {
+ if (phy_type == TYPE_DP)
+ return cdns_torrent_dp_multilink_init(cdns_phy, inst, phy);
return 0;
+ }
/**
* Spread spectrum generation is not required or supported
@@ -2399,6 +2496,12 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy)
}
}
+ if (phy_t1 == TYPE_DP) {
+ ret = cdns_torrent_dp_get_pll(cdns_phy, phy_t2);
+ if (ret)
+ return ret;
+ }
+
reset_control_deassert(cdns_phy->phys[node].lnk_rst);
}
@@ -2794,6 +2897,109 @@ static void cdns_torrent_phy_remove(struct platform_device *pdev)
cdns_torrent_clk_cleanup(cdns_phy);
}
+/* USB and DP link configuration */
+static struct cdns_reg_pairs usb_dp_link_cmn_regs[] = {
+ {0x0002, PHY_PLL_CFG},
+ {0x8600, CMN_PDIAG_PLL0_CLK_SEL_M0}
+};
+
+static struct cdns_reg_pairs usb_dp_xcvr_diag_ln_regs[] = {
+ {0x0000, XCVR_DIAG_HSCLK_SEL},
+ {0x0001, XCVR_DIAG_HSCLK_DIV},
+ {0x0041, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_reg_pairs dp_usb_xcvr_diag_ln_regs[] = {
+ {0x0001, XCVR_DIAG_HSCLK_SEL},
+ {0x0009, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_torrent_vals usb_dp_link_cmn_vals = {
+ .reg_pairs = usb_dp_link_cmn_regs,
+ .num_regs = ARRAY_SIZE(usb_dp_link_cmn_regs),
+};
+
+static struct cdns_torrent_vals usb_dp_xcvr_diag_ln_vals = {
+ .reg_pairs = usb_dp_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(usb_dp_xcvr_diag_ln_regs),
+};
+
+static struct cdns_torrent_vals dp_usb_xcvr_diag_ln_vals = {
+ .reg_pairs = dp_usb_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(dp_usb_xcvr_diag_ln_regs),
+};
+
+/* PCIe and DP link configuration */
+static struct cdns_reg_pairs pcie_dp_link_cmn_regs[] = {
+ {0x0003, PHY_PLL_CFG},
+ {0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0},
+ {0x0400, CMN_PDIAG_PLL0_CLK_SEL_M1}
+};
+
+static struct cdns_reg_pairs pcie_dp_xcvr_diag_ln_regs[] = {
+ {0x0000, XCVR_DIAG_HSCLK_SEL},
+ {0x0001, XCVR_DIAG_HSCLK_DIV},
+ {0x0012, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_reg_pairs dp_pcie_xcvr_diag_ln_regs[] = {
+ {0x0001, XCVR_DIAG_HSCLK_SEL},
+ {0x0009, XCVR_DIAG_PLLDRC_CTRL}
+};
+
+static struct cdns_torrent_vals pcie_dp_link_cmn_vals = {
+ .reg_pairs = pcie_dp_link_cmn_regs,
+ .num_regs = ARRAY_SIZE(pcie_dp_link_cmn_regs),
+};
+
+static struct cdns_torrent_vals pcie_dp_xcvr_diag_ln_vals = {
+ .reg_pairs = pcie_dp_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(pcie_dp_xcvr_diag_ln_regs),
+};
+
+static struct cdns_torrent_vals dp_pcie_xcvr_diag_ln_vals = {
+ .reg_pairs = dp_pcie_xcvr_diag_ln_regs,
+ .num_regs = ARRAY_SIZE(dp_pcie_xcvr_diag_ln_regs),
+};
+
+/* DP Multilink, 100 MHz Ref clk, no SSC */
+static struct cdns_reg_pairs dp_100_no_ssc_cmn_regs[] = {
+ {0x007F, CMN_TXPUCAL_TUNE},
+ {0x007F, CMN_TXPDCAL_TUNE}
+};
+
+static struct cdns_reg_pairs dp_100_no_ssc_tx_ln_regs[] = {
+ {0x00FB, TX_PSC_A0},
+ {0x04AA, TX_PSC_A2},
+ {0x04AA, TX_PSC_A3},
+ {0x000F, XCVR_DIAG_BIDI_CTRL}
+};
+
+static struct cdns_reg_pairs dp_100_no_ssc_rx_ln_regs[] = {
+ {0x0000, RX_PSC_A0},
+ {0x0000, RX_PSC_A2},
+ {0x0000, RX_PSC_A3},
+ {0x0000, RX_PSC_CAL},
+ {0x0000, RX_REE_GCSM1_CTRL},
+ {0x0000, RX_REE_GCSM2_CTRL},
+ {0x0000, RX_REE_PERGCSM_CTRL}
+};
+
+static struct cdns_torrent_vals dp_100_no_ssc_cmn_vals = {
+ .reg_pairs = dp_100_no_ssc_cmn_regs,
+ .num_regs = ARRAY_SIZE(dp_100_no_ssc_cmn_regs),
+};
+
+static struct cdns_torrent_vals dp_100_no_ssc_tx_ln_vals = {
+ .reg_pairs = dp_100_no_ssc_tx_ln_regs,
+ .num_regs = ARRAY_SIZE(dp_100_no_ssc_tx_ln_regs),
+};
+
+static struct cdns_torrent_vals dp_100_no_ssc_rx_ln_vals = {
+ .reg_pairs = dp_100_no_ssc_rx_ln_regs,
+ .num_regs = ARRAY_SIZE(dp_100_no_ssc_rx_ln_regs),
+};
+
/* Single DisplayPort(DP) link configuration */
static struct cdns_reg_pairs sl_dp_link_cmn_regs[] = {
{0x0000, PHY_PLL_CFG},
@@ -3736,6 +3942,12 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_link_cmn_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &pcie_dp_link_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &usb_dp_link_cmn_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -3758,6 +3970,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
[INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &pcie_dp_link_cmn_vals,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -3810,6 +4025,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_dp_link_cmn_vals,
+ },
},
},
.xcvr_diag_vals = {
@@ -3817,6 +4035,12 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_xcvr_diag_ln_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &dp_pcie_xcvr_diag_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &dp_usb_xcvr_diag_ln_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -3839,6 +4063,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &pcie_dp_xcvr_diag_ln_vals,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -3891,6 +4118,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_dp_xcvr_diag_ln_vals,
+ },
},
},
.pcs_cmn_vals = {
@@ -3915,6 +4145,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ },
},
},
.cmn_vals = {
@@ -3937,6 +4170,12 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_100_no_ssc_cmn_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &dp_100_no_ssc_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -3959,6 +4198,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = NULL,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -4011,6 +4253,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_100_no_ssc_cmn_vals,
+ },
},
},
},
@@ -4034,6 +4279,12 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &dp_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &dp_100_no_ssc_tx_ln_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -4056,6 +4307,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
+ [TYPE_DP] = {
+ [NO_SSC] = NULL,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -4108,6 +4362,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
},
},
},
@@ -4131,6 +4388,12 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &dp_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &dp_100_no_ssc_rx_ln_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -4153,6 +4416,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -4205,6 +4471,9 @@ static const struct cdns_torrent_data cdns_map_torrent = {
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
},
},
},
@@ -4218,6 +4487,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_link_cmn_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &pcie_dp_link_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &usb_dp_link_cmn_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -4240,6 +4515,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &pcie_usb_link_cmn_vals,
[INTERNAL_SSC] = &pcie_usb_link_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &pcie_dp_link_cmn_vals,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -4292,6 +4570,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
[INTERNAL_SSC] = &usb_sgmii_link_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_dp_link_cmn_vals,
+ },
},
},
.xcvr_diag_vals = {
@@ -4299,6 +4580,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_xcvr_diag_ln_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &dp_pcie_xcvr_diag_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &dp_usb_xcvr_diag_ln_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -4321,6 +4608,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &pcie_dp_xcvr_diag_ln_vals,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -4373,6 +4663,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
[INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_dp_xcvr_diag_ln_vals,
+ },
},
},
.pcs_cmn_vals = {
@@ -4397,6 +4690,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
[INTERNAL_SSC] = &usb_phy_pcs_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_phy_pcs_cmn_vals,
+ },
},
},
.cmn_vals = {
@@ -4419,6 +4715,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_100_no_ssc_cmn_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &dp_100_no_ssc_cmn_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -4441,6 +4743,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = NULL,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -4493,6 +4798,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals,
[INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_100_no_ssc_cmn_vals,
+ },
},
},
},
@@ -4516,6 +4824,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &dp_100_no_ssc_tx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &dp_100_no_ssc_tx_ln_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -4538,6 +4852,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = NULL,
[INTERNAL_SSC] = NULL,
},
+ [TYPE_DP] = {
+ [NO_SSC] = NULL,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -4590,6 +4907,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_100_no_ssc_tx_ln_vals,
+ },
},
},
},
@@ -4613,6 +4933,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[TYPE_NONE] = {
[NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals,
},
+ [TYPE_PCIE] = {
+ [NO_SSC] = &dp_100_no_ssc_rx_ln_vals,
+ },
+ [TYPE_USB] = {
+ [NO_SSC] = &dp_100_no_ssc_rx_ln_vals,
+ },
},
[TYPE_PCIE] = {
[TYPE_NONE] = {
@@ -4635,6 +4961,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals,
+ },
},
[TYPE_SGMII] = {
[TYPE_NONE] = {
@@ -4687,6 +5016,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = {
[EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
[INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals,
},
+ [TYPE_DP] = {
+ [NO_SSC] = &usb_100_no_ssc_rx_ln_vals,
+ },
},
},
},
diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
index afc63552ecaf..d4c92498ad1e 100644
--- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
+++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
@@ -206,7 +206,6 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct imx8_pcie_phy *imx8_phy;
- struct resource *res;
imx8_phy = devm_kzalloc(dev, sizeof(*imx8_phy), GFP_KERNEL);
if (!imx8_phy)
@@ -259,8 +258,7 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev)
"Failed to get PCIE PHY PERST control\n");
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- imx8_phy->base = devm_ioremap_resource(dev, res);
+ imx8_phy->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(imx8_phy->base))
return PTR_ERR(imx8_phy->base);
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index a29b4a6f7c24..88826ceb72f8 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -27,17 +27,231 @@
#define PHY_CTRL2_TXENABLEN0 BIT(8)
#define PHY_CTRL2_OTG_DISABLE BIT(9)
+#define PHY_CTRL3 0xc
+#define PHY_CTRL3_COMPDISTUNE_MASK GENMASK(2, 0)
+#define PHY_CTRL3_TXPREEMP_TUNE_MASK GENMASK(16, 15)
+#define PHY_CTRL3_TXRISE_TUNE_MASK GENMASK(21, 20)
+#define PHY_CTRL3_TXVREF_TUNE_MASK GENMASK(25, 22)
+#define PHY_CTRL3_TX_VBOOST_LEVEL_MASK GENMASK(31, 29)
+
+#define PHY_CTRL4 0x10
+#define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(20, 15)
+
+#define PHY_CTRL5 0x14
+#define PHY_CTRL5_DMPWD_OVERRIDE_SEL BIT(23)
+#define PHY_CTRL5_DMPWD_OVERRIDE BIT(22)
+#define PHY_CTRL5_DPPWD_OVERRIDE_SEL BIT(21)
+#define PHY_CTRL5_DPPWD_OVERRIDE BIT(20)
+#define PHY_CTRL5_PCS_TX_SWING_FULL_MASK GENMASK(6, 0)
+
#define PHY_CTRL6 0x18
#define PHY_CTRL6_ALT_CLK_EN BIT(1)
#define PHY_CTRL6_ALT_CLK_SEL BIT(0)
+#define PHY_TUNE_DEFAULT 0xffffffff
+
struct imx8mq_usb_phy {
struct phy *phy;
struct clk *clk;
void __iomem *base;
struct regulator *vbus;
+ u32 pcs_tx_swing_full;
+ u32 pcs_tx_deemph_3p5db;
+ u32 tx_vref_tune;
+ u32 tx_rise_tune;
+ u32 tx_preemp_amp_tune;
+ u32 tx_vboost_level;
+ u32 comp_dis_tune;
};
+static u32 phy_tx_vref_tune_from_property(u32 percent)
+{
+ percent = clamp(percent, 94U, 124U);
+
+ return DIV_ROUND_CLOSEST(percent - 94U, 2);
+}
+
+static u32 phy_tx_rise_tune_from_property(u32 percent)
+{
+ switch (percent) {
+ case 0 ... 98:
+ return 3;
+ case 99:
+ return 2;
+ case 100 ... 101:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static u32 phy_tx_preemp_amp_tune_from_property(u32 microamp)
+{
+ microamp = min(microamp, 1800U);
+
+ return microamp / 600;
+}
+
+static u32 phy_tx_vboost_level_from_property(u32 microvolt)
+{
+ switch (microvolt) {
+ case 0 ... 960:
+ return 0;
+ case 961 ... 1160:
+ return 2;
+ default:
+ return 3;
+ }
+}
+
+static u32 phy_pcs_tx_deemph_3p5db_from_property(u32 decibel)
+{
+ return min(decibel, 36U);
+}
+
+static u32 phy_comp_dis_tune_from_property(u32 percent)
+{
+ switch (percent) {
+ case 0 ... 92:
+ return 0;
+ case 93 ... 95:
+ return 1;
+ case 96 ... 97:
+ return 2;
+ case 98 ... 102:
+ return 3;
+ case 103 ... 105:
+ return 4;
+ case 106 ... 109:
+ return 5;
+ case 110 ... 113:
+ return 6;
+ default:
+ return 7;
+ }
+}
+static u32 phy_pcs_tx_swing_full_from_property(u32 percent)
+{
+ percent = min(percent, 100U);
+
+ return (percent * 127) / 100;
+}
+
+static void imx8m_get_phy_tuning_data(struct imx8mq_usb_phy *imx_phy)
+{
+ struct device *dev = imx_phy->phy->dev.parent;
+
+ if (device_property_read_u32(dev, "fsl,phy-tx-vref-tune-percent",
+ &imx_phy->tx_vref_tune))
+ imx_phy->tx_vref_tune = PHY_TUNE_DEFAULT;
+ else
+ imx_phy->tx_vref_tune =
+ phy_tx_vref_tune_from_property(imx_phy->tx_vref_tune);
+
+ if (device_property_read_u32(dev, "fsl,phy-tx-rise-tune-percent",
+ &imx_phy->tx_rise_tune))
+ imx_phy->tx_rise_tune = PHY_TUNE_DEFAULT;
+ else
+ imx_phy->tx_rise_tune =
+ phy_tx_rise_tune_from_property(imx_phy->tx_rise_tune);
+
+ if (device_property_read_u32(dev, "fsl,phy-tx-preemp-amp-tune-microamp",
+ &imx_phy->tx_preemp_amp_tune))
+ imx_phy->tx_preemp_amp_tune = PHY_TUNE_DEFAULT;
+ else
+ imx_phy->tx_preemp_amp_tune =
+ phy_tx_preemp_amp_tune_from_property(imx_phy->tx_preemp_amp_tune);
+
+ if (device_property_read_u32(dev, "fsl,phy-tx-vboost-level-microvolt",
+ &imx_phy->tx_vboost_level))
+ imx_phy->tx_vboost_level = PHY_TUNE_DEFAULT;
+ else
+ imx_phy->tx_vboost_level =
+ phy_tx_vboost_level_from_property(imx_phy->tx_vboost_level);
+
+ if (device_property_read_u32(dev, "fsl,phy-comp-dis-tune-percent",
+ &imx_phy->comp_dis_tune))
+ imx_phy->comp_dis_tune = PHY_TUNE_DEFAULT;
+ else
+ imx_phy->comp_dis_tune =
+ phy_comp_dis_tune_from_property(imx_phy->comp_dis_tune);
+
+ if (device_property_read_u32(dev, "fsl,pcs-tx-deemph-3p5db-attenuation-db",
+ &imx_phy->pcs_tx_deemph_3p5db))
+ imx_phy->pcs_tx_deemph_3p5db = PHY_TUNE_DEFAULT;
+ else
+ imx_phy->pcs_tx_deemph_3p5db =
+ phy_pcs_tx_deemph_3p5db_from_property(imx_phy->pcs_tx_deemph_3p5db);
+
+ if (device_property_read_u32(dev, "fsl,phy-pcs-tx-swing-full-percent",
+ &imx_phy->pcs_tx_swing_full))
+ imx_phy->pcs_tx_swing_full = PHY_TUNE_DEFAULT;
+ else
+ imx_phy->pcs_tx_swing_full =
+ phy_pcs_tx_swing_full_from_property(imx_phy->pcs_tx_swing_full);
+}
+
+static void imx8m_phy_tune(struct imx8mq_usb_phy *imx_phy)
+{
+ u32 value;
+
+ /* PHY tuning */
+ if (imx_phy->pcs_tx_deemph_3p5db != PHY_TUNE_DEFAULT) {
+ value = readl(imx_phy->base + PHY_CTRL4);
+ value &= ~PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK;
+ value |= FIELD_PREP(PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK,
+ imx_phy->pcs_tx_deemph_3p5db);
+ writel(value, imx_phy->base + PHY_CTRL4);
+ }
+
+ if (imx_phy->pcs_tx_swing_full != PHY_TUNE_DEFAULT) {
+ value = readl(imx_phy->base + PHY_CTRL5);
+ value |= FIELD_PREP(PHY_CTRL5_PCS_TX_SWING_FULL_MASK,
+ imx_phy->pcs_tx_swing_full);
+ writel(value, imx_phy->base + PHY_CTRL5);
+ }
+
+ if ((imx_phy->tx_vref_tune & imx_phy->tx_rise_tune &
+ imx_phy->tx_preemp_amp_tune & imx_phy->comp_dis_tune &
+ imx_phy->tx_vboost_level) == PHY_TUNE_DEFAULT)
+ /* If all are the default values, no need update. */
+ return;
+
+ value = readl(imx_phy->base + PHY_CTRL3);
+
+ if (imx_phy->tx_vref_tune != PHY_TUNE_DEFAULT) {
+ value &= ~PHY_CTRL3_TXVREF_TUNE_MASK;
+ value |= FIELD_PREP(PHY_CTRL3_TXVREF_TUNE_MASK,
+ imx_phy->tx_vref_tune);
+ }
+
+ if (imx_phy->tx_rise_tune != PHY_TUNE_DEFAULT) {
+ value &= ~PHY_CTRL3_TXRISE_TUNE_MASK;
+ value |= FIELD_PREP(PHY_CTRL3_TXRISE_TUNE_MASK,
+ imx_phy->tx_rise_tune);
+ }
+
+ if (imx_phy->tx_preemp_amp_tune != PHY_TUNE_DEFAULT) {
+ value &= ~PHY_CTRL3_TXPREEMP_TUNE_MASK;
+ value |= FIELD_PREP(PHY_CTRL3_TXPREEMP_TUNE_MASK,
+ imx_phy->tx_preemp_amp_tune);
+ }
+
+ if (imx_phy->comp_dis_tune != PHY_TUNE_DEFAULT) {
+ value &= ~PHY_CTRL3_COMPDISTUNE_MASK;
+ value |= FIELD_PREP(PHY_CTRL3_COMPDISTUNE_MASK,
+ imx_phy->comp_dis_tune);
+ }
+
+ if (imx_phy->tx_vboost_level != PHY_TUNE_DEFAULT) {
+ value &= ~PHY_CTRL3_TX_VBOOST_LEVEL_MASK;
+ value |= FIELD_PREP(PHY_CTRL3_TX_VBOOST_LEVEL_MASK,
+ imx_phy->tx_vboost_level);
+ }
+
+ writel(value, imx_phy->base + PHY_CTRL3);
+}
+
static int imx8mq_usb_phy_init(struct phy *phy)
{
struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy);
@@ -99,6 +313,8 @@ static int imx8mp_usb_phy_init(struct phy *phy)
value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET);
writel(value, imx_phy->base + PHY_CTRL1);
+ imx8m_phy_tune(imx_phy);
+
return 0;
}
@@ -182,6 +398,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
phy_set_drvdata(imx_phy->phy, imx_phy);
+ imx8m_get_phy_tuning_data(imx_phy);
+
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig
index d3b92c288554..6c89136fc8c2 100644
--- a/drivers/phy/hisilicon/Kconfig
+++ b/drivers/phy/hisilicon/Kconfig
@@ -54,7 +54,7 @@ config PHY_HISTB_COMBPHY
config PHY_HISI_INNO_USB2
tristate "HiSilicon INNO USB2 PHY support"
- depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+ depends on ARCH_HISI || COMPILE_TEST
select GENERIC_PHY
select MFD_SYSCON
help
diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
index b133ae06757a..15dafe359552 100644
--- a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
+++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c
@@ -9,7 +9,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
+#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/reset.h>
@@ -20,12 +20,25 @@
#define PHY_CLK_STABLE_TIME 2 /* unit:ms */
#define UTMI_RST_COMPLETE_TIME 2 /* unit:ms */
#define POR_RST_COMPLETE_TIME 300 /* unit:us */
+
+#define PHY_TYPE_0 0
+#define PHY_TYPE_1 1
+
#define PHY_TEST_DATA GENMASK(7, 0)
-#define PHY_TEST_ADDR GENMASK(15, 8)
-#define PHY_TEST_PORT GENMASK(18, 16)
-#define PHY_TEST_WREN BIT(21)
-#define PHY_TEST_CLK BIT(22) /* rising edge active */
-#define PHY_TEST_RST BIT(23) /* low active */
+#define PHY_TEST_ADDR_OFFSET 8
+#define PHY0_TEST_ADDR GENMASK(15, 8)
+#define PHY0_TEST_PORT_OFFSET 16
+#define PHY0_TEST_PORT GENMASK(18, 16)
+#define PHY0_TEST_WREN BIT(21)
+#define PHY0_TEST_CLK BIT(22) /* rising edge active */
+#define PHY0_TEST_RST BIT(23) /* low active */
+#define PHY1_TEST_ADDR GENMASK(11, 8)
+#define PHY1_TEST_PORT_OFFSET 12
+#define PHY1_TEST_PORT BIT(12)
+#define PHY1_TEST_WREN BIT(13)
+#define PHY1_TEST_CLK BIT(14) /* rising edge active */
+#define PHY1_TEST_RST BIT(15) /* low active */
+
#define PHY_CLK_ENABLE BIT(2)
struct hisi_inno_phy_port {
@@ -37,6 +50,7 @@ struct hisi_inno_phy_priv {
void __iomem *mmio;
struct clk *ref_clk;
struct reset_control *por_rst;
+ unsigned int type;
struct hisi_inno_phy_port ports[INNO_PHY_PORT_NUM];
};
@@ -45,17 +59,27 @@ static void hisi_inno_phy_write_reg(struct hisi_inno_phy_priv *priv,
{
void __iomem *reg = priv->mmio;
u32 val;
-
- val = (data & PHY_TEST_DATA) |
- ((addr << 8) & PHY_TEST_ADDR) |
- ((port << 16) & PHY_TEST_PORT) |
- PHY_TEST_WREN | PHY_TEST_RST;
+ u32 value;
+
+ if (priv->type == PHY_TYPE_0)
+ val = (data & PHY_TEST_DATA) |
+ ((addr << PHY_TEST_ADDR_OFFSET) & PHY0_TEST_ADDR) |
+ ((port << PHY0_TEST_PORT_OFFSET) & PHY0_TEST_PORT) |
+ PHY0_TEST_WREN | PHY0_TEST_RST;
+ else
+ val = (data & PHY_TEST_DATA) |
+ ((addr << PHY_TEST_ADDR_OFFSET) & PHY1_TEST_ADDR) |
+ ((port << PHY1_TEST_PORT_OFFSET) & PHY1_TEST_PORT) |
+ PHY1_TEST_WREN | PHY1_TEST_RST;
writel(val, reg);
- val |= PHY_TEST_CLK;
- writel(val, reg);
+ value = val;
+ if (priv->type == PHY_TYPE_0)
+ value |= PHY0_TEST_CLK;
+ else
+ value |= PHY1_TEST_CLK;
+ writel(value, reg);
- val &= ~PHY_TEST_CLK;
writel(val, reg);
}
@@ -135,6 +159,8 @@ static int hisi_inno_phy_probe(struct platform_device *pdev)
if (IS_ERR(priv->por_rst))
return PTR_ERR(priv->por_rst);
+ priv->type = (uintptr_t) of_device_get_match_data(dev);
+
for_each_child_of_node(np, child) {
struct reset_control *rst;
struct phy *phy;
@@ -170,8 +196,12 @@ static int hisi_inno_phy_probe(struct platform_device *pdev)
}
static const struct of_device_id hisi_inno_phy_of_match[] = {
- { .compatible = "hisilicon,inno-usb2-phy", },
- { .compatible = "hisilicon,hi3798cv200-usb2-phy", },
+ { .compatible = "hisilicon,inno-usb2-phy",
+ .data = (void *) PHY_TYPE_0 },
+ { .compatible = "hisilicon,hi3798cv200-usb2-phy",
+ .data = (void *) PHY_TYPE_0 },
+ { .compatible = "hisilicon,hi3798mv100-usb2-phy",
+ .data = (void *) PHY_TYPE_1 },
{ },
};
MODULE_DEVICE_TABLE(of, hisi_inno_phy_of_match);
diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c
index e906a82791bd..0d110e50bbfd 100644
--- a/drivers/phy/mediatek/phy-mtk-tphy.c
+++ b/drivers/phy/mediatek/phy-mtk-tphy.c
@@ -7,6 +7,7 @@
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
@@ -264,6 +265,8 @@
#define TPHY_CLKS_CNT 2
+#define USER_BUF_LEN(count) min_t(size_t, 8, (count))
+
enum mtk_phy_version {
MTK_PHY_V1 = 1,
MTK_PHY_V2,
@@ -336,6 +339,358 @@ struct mtk_tphy {
int src_coef; /* coefficient for slew rate calibrate */
};
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+
+enum u2_phy_params {
+ U2P_EYE_VRT = 0,
+ U2P_EYE_TERM,
+ U2P_EFUSE_EN,
+ U2P_EFUSE_INTR,
+ U2P_DISCTH,
+ U2P_PRE_EMPHASIS,
+};
+
+enum u3_phy_params {
+ U3P_EFUSE_EN = 0,
+ U3P_EFUSE_INTR,
+ U3P_EFUSE_TX_IMP,
+ U3P_EFUSE_RX_IMP,
+};
+
+static const char *const u2_phy_files[] = {
+ [U2P_EYE_VRT] = "vrt",
+ [U2P_EYE_TERM] = "term",
+ [U2P_EFUSE_EN] = "efuse",
+ [U2P_EFUSE_INTR] = "intr",
+ [U2P_DISCTH] = "discth",
+ [U2P_PRE_EMPHASIS] = "preemph",
+};
+
+static const char *const u3_phy_files[] = {
+ [U3P_EFUSE_EN] = "efuse",
+ [U3P_EFUSE_INTR] = "intr",
+ [U3P_EFUSE_TX_IMP] = "tx-imp",
+ [U3P_EFUSE_RX_IMP] = "rx-imp",
+};
+
+static int u2_phy_params_show(struct seq_file *sf, void *unused)
+{
+ struct mtk_phy_instance *inst = sf->private;
+ const char *fname = file_dentry(sf->file)->d_iname;
+ struct u2phy_banks *u2_banks = &inst->u2_banks;
+ void __iomem *com = u2_banks->com;
+ u32 max = 0;
+ u32 tmp = 0;
+ u32 val = 0;
+ int ret;
+
+ ret = match_string(u2_phy_files, ARRAY_SIZE(u2_phy_files), fname);
+ if (ret < 0)
+ return ret;
+
+ switch (ret) {
+ case U2P_EYE_VRT:
+ tmp = readl(com + U3P_USBPHYACR1);
+ val = FIELD_GET(PA1_RG_VRT_SEL, tmp);
+ max = FIELD_MAX(PA1_RG_VRT_SEL);
+ break;
+
+ case U2P_EYE_TERM:
+ tmp = readl(com + U3P_USBPHYACR1);
+ val = FIELD_GET(PA1_RG_TERM_SEL, tmp);
+ max = FIELD_MAX(PA1_RG_TERM_SEL);
+ break;
+
+ case U2P_EFUSE_EN:
+ if (u2_banks->misc) {
+ tmp = readl(u2_banks->misc + U3P_MISC_REG1);
+ max = 1;
+ }
+
+ val = !!(tmp & MR1_EFUSE_AUTO_LOAD_DIS);
+ break;
+
+ case U2P_EFUSE_INTR:
+ tmp = readl(com + U3P_USBPHYACR1);
+ val = FIELD_GET(PA1_RG_INTR_CAL, tmp);
+ max = FIELD_MAX(PA1_RG_INTR_CAL);
+ break;
+
+ case U2P_DISCTH:
+ tmp = readl(com + U3P_USBPHYACR6);
+ val = FIELD_GET(PA6_RG_U2_DISCTH, tmp);
+ max = FIELD_MAX(PA6_RG_U2_DISCTH);
+ break;
+
+ case U2P_PRE_EMPHASIS:
+ tmp = readl(com + U3P_USBPHYACR6);
+ val = FIELD_GET(PA6_RG_U2_PRE_EMP, tmp);
+ max = FIELD_MAX(PA6_RG_U2_PRE_EMP);
+ break;
+
+ default:
+ seq_printf(sf, "invalid, %d\n", ret);
+ break;
+ }
+
+ seq_printf(sf, "%s : %d [0, %d]\n", fname, val, max);
+
+ return 0;
+}
+
+static int u2_phy_params_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, u2_phy_params_show, inode->i_private);
+}
+
+static ssize_t u2_phy_params_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ const char *fname = file_dentry(file)->d_iname;
+ struct seq_file *sf = file->private_data;
+ struct mtk_phy_instance *inst = sf->private;
+ struct u2phy_banks *u2_banks = &inst->u2_banks;
+ void __iomem *com = u2_banks->com;
+ ssize_t rc;
+ u32 val;
+ int ret;
+
+ rc = kstrtouint_from_user(ubuf, USER_BUF_LEN(count), 0, &val);
+ if (rc)
+ return rc;
+
+ ret = match_string(u2_phy_files, ARRAY_SIZE(u2_phy_files), fname);
+ if (ret < 0)
+ return (ssize_t)ret;
+
+ switch (ret) {
+ case U2P_EYE_VRT:
+ mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_VRT_SEL, val);
+ break;
+
+ case U2P_EYE_TERM:
+ mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_TERM_SEL, val);
+ break;
+
+ case U2P_EFUSE_EN:
+ if (u2_banks->misc)
+ mtk_phy_update_field(u2_banks->misc + U3P_MISC_REG1,
+ MR1_EFUSE_AUTO_LOAD_DIS, !!val);
+ break;
+
+ case U2P_EFUSE_INTR:
+ mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_INTR_CAL, val);
+ break;
+
+ case U2P_DISCTH:
+ mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_DISCTH, val);
+ break;
+
+ case U2P_PRE_EMPHASIS:
+ mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_PRE_EMP, val);
+ break;
+
+ default:
+ break;
+ }
+
+ return count;
+}
+
+static const struct file_operations u2_phy_fops = {
+ .open = u2_phy_params_open,
+ .write = u2_phy_params_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void u2_phy_dbgfs_files_create(struct mtk_phy_instance *inst)
+{
+ u32 count = ARRAY_SIZE(u2_phy_files);
+ int i;
+
+ for (i = 0; i < count; i++)
+ debugfs_create_file(u2_phy_files[i], 0644, inst->phy->debugfs,
+ inst, &u2_phy_fops);
+}
+
+static int u3_phy_params_show(struct seq_file *sf, void *unused)
+{
+ struct mtk_phy_instance *inst = sf->private;
+ const char *fname = file_dentry(sf->file)->d_iname;
+ struct u3phy_banks *u3_banks = &inst->u3_banks;
+ u32 val = 0;
+ u32 max = 0;
+ u32 tmp;
+ int ret;
+
+ ret = match_string(u3_phy_files, ARRAY_SIZE(u3_phy_files), fname);
+ if (ret < 0)
+ return ret;
+
+ switch (ret) {
+ case U3P_EFUSE_EN:
+ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV);
+ val = !!(tmp & P3D_RG_EFUSE_AUTO_LOAD_DIS);
+ max = 1;
+ break;
+
+ case U3P_EFUSE_INTR:
+ tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0);
+ val = FIELD_GET(P3A_RG_IEXT_INTR, tmp);
+ max = FIELD_MAX(P3A_RG_IEXT_INTR);
+ break;
+
+ case U3P_EFUSE_TX_IMP:
+ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0);
+ val = FIELD_GET(P3D_RG_TX_IMPEL, tmp);
+ max = FIELD_MAX(P3D_RG_TX_IMPEL);
+ break;
+
+ case U3P_EFUSE_RX_IMP:
+ tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1);
+ val = FIELD_GET(P3D_RG_RX_IMPEL, tmp);
+ max = FIELD_MAX(P3D_RG_RX_IMPEL);
+ break;
+
+ default:
+ seq_printf(sf, "invalid, %d\n", ret);
+ break;
+ }
+
+ seq_printf(sf, "%s : %d [0, %d]\n", fname, val, max);
+
+ return 0;
+}
+
+static int u3_phy_params_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, u3_phy_params_show, inode->i_private);
+}
+
+static ssize_t u3_phy_params_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ const char *fname = file_dentry(file)->d_iname;
+ struct seq_file *sf = file->private_data;
+ struct mtk_phy_instance *inst = sf->private;
+ struct u3phy_banks *u3_banks = &inst->u3_banks;
+ void __iomem *phyd = u3_banks->phyd;
+ ssize_t rc;
+ u32 val;
+ int ret;
+
+ rc = kstrtouint_from_user(ubuf, USER_BUF_LEN(count), 0, &val);
+ if (rc)
+ return rc;
+
+ ret = match_string(u3_phy_files, ARRAY_SIZE(u3_phy_files), fname);
+ if (ret < 0)
+ return (ssize_t)ret;
+
+ switch (ret) {
+ case U3P_EFUSE_EN:
+ mtk_phy_update_field(phyd + U3P_U3_PHYD_RSV,
+ P3D_RG_EFUSE_AUTO_LOAD_DIS, !!val);
+ break;
+
+ case U3P_EFUSE_INTR:
+ mtk_phy_update_field(u3_banks->phya + U3P_U3_PHYA_REG0,
+ P3A_RG_IEXT_INTR, val);
+ break;
+
+ case U3P_EFUSE_TX_IMP:
+ mtk_phy_update_field(phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_TX_IMPEL, val);
+ mtk_phy_set_bits(phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_FORCE_TX_IMPEL);
+ break;
+
+ case U3P_EFUSE_RX_IMP:
+ mtk_phy_update_field(phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_RX_IMPEL, val);
+ mtk_phy_set_bits(phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_FORCE_RX_IMPEL);
+ break;
+
+ default:
+ break;
+ }
+
+ return count;
+}
+
+static const struct file_operations u3_phy_fops = {
+ .open = u3_phy_params_open,
+ .write = u3_phy_params_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void u3_phy_dbgfs_files_create(struct mtk_phy_instance *inst)
+{
+ u32 count = ARRAY_SIZE(u3_phy_files);
+ int i;
+
+ for (i = 0; i < count; i++)
+ debugfs_create_file(u3_phy_files[i], 0644, inst->phy->debugfs,
+ inst, &u3_phy_fops);
+}
+
+static int phy_type_show(struct seq_file *sf, void *unused)
+{
+ struct mtk_phy_instance *inst = sf->private;
+ const char *type;
+
+ switch (inst->type) {
+ case PHY_TYPE_USB2:
+ type = "USB2";
+ break;
+ case PHY_TYPE_USB3:
+ type = "USB3";
+ break;
+ case PHY_TYPE_PCIE:
+ type = "PCIe";
+ break;
+ case PHY_TYPE_SGMII:
+ type = "SGMII";
+ break;
+ case PHY_TYPE_SATA:
+ type = "SATA";
+ break;
+ default:
+ type = "";
+ }
+
+ seq_printf(sf, "%s\n", type);
+
+ return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(phy_type);
+
+/* these files will be removed when phy is released by phy core */
+static void phy_debugfs_init(struct mtk_phy_instance *inst)
+{
+ debugfs_create_file("type", 0444, inst->phy->debugfs, inst, &phy_type_fops);
+
+ switch (inst->type) {
+ case PHY_TYPE_USB2:
+ u2_phy_dbgfs_files_create(inst);
+ break;
+ case PHY_TYPE_USB3:
+ case PHY_TYPE_PCIE:
+ u3_phy_dbgfs_files_create(inst);
+ break;
+ default:
+ break;
+ }
+}
+
+#else
+
+static void phy_debugfs_init(struct mtk_phy_instance *inst)
+{}
+
+#endif
+
static void hs_slew_rate_calibrate(struct mtk_tphy *tphy,
struct mtk_phy_instance *instance)
{
@@ -1140,6 +1495,7 @@ static struct phy *mtk_phy_xlate(struct device *dev,
phy_parse_property(tphy, instance);
phy_type_set(instance);
+ phy_debugfs_init(instance);
return instance->phy;
}
diff --git a/drivers/phy/microchip/sparx5_serdes.c b/drivers/phy/microchip/sparx5_serdes.c
index ab1b0986aa67..01bd5ea620c5 100644
--- a/drivers/phy/microchip/sparx5_serdes.c
+++ b/drivers/phy/microchip/sparx5_serdes.c
@@ -25,12 +25,17 @@
#define SPX5_SERDES_10G_START 13
#define SPX5_SERDES_25G_START 25
+#define SPX5_SERDES_6G10G_CNT SPX5_SERDES_25G_START
+
+/* Optimal power settings from GUC */
+#define SPX5_SERDES_QUIET_MODE_VAL 0x01ef4e0c
enum sparx5_10g28cmu_mode {
SPX5_SD10G28_CMU_MAIN = 0,
SPX5_SD10G28_CMU_AUX1 = 1,
SPX5_SD10G28_CMU_AUX2 = 3,
SPX5_SD10G28_CMU_NONE = 4,
+ SPX5_SD10G28_CMU_MAX,
};
enum sparx5_sd25g28_mode_preset_type {
@@ -922,6 +927,222 @@ static void sparx5_sd10g28_get_params(struct sparx5_serdes_macro *macro,
*params = init;
}
+static int sparx5_cmu_apply_cfg(struct sparx5_serdes_private *priv,
+ u32 cmu_idx,
+ void __iomem *cmu_tgt,
+ void __iomem *cmu_cfg_tgt,
+ u32 spd10g)
+{
+ void __iomem **regs = priv->regs;
+ struct device *dev = priv->dev;
+ int value;
+
+ cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx);
+ cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx);
+
+ if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 ||
+ cmu_idx == 10 || cmu_idx == 13) {
+ spd10g = 0;
+ }
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(1),
+ SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST,
+ cmu_cfg_tgt,
+ SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(0),
+ SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST,
+ cmu_cfg_tgt,
+ SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(1),
+ SD_CMU_CFG_SD_CMU_CFG_CMU_RST,
+ cmu_cfg_tgt,
+ SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_SET(0x1) |
+ SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_SET(0x1) |
+ SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_SET(0x1) |
+ SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_SET(0x1) |
+ SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(0x0),
+ SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT |
+ SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT |
+ SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT |
+ SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT |
+ SD_CMU_CMU_45_R_EN_RATECHG_CTRL,
+ cmu_tgt,
+ SD_CMU_CMU_45(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(0),
+ SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0,
+ cmu_tgt,
+ SD_CMU_CMU_47(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(0),
+ SD_CMU_CMU_1B_CFG_RESERVE_7_0,
+ cmu_tgt,
+ SD_CMU_CMU_1B(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_JC_BYP_SET(0x1),
+ SD_CMU_CMU_0D_CFG_JC_BYP,
+ cmu_tgt,
+ SD_CMU_CMU_0D(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_1F_CFG_VTUNE_SEL_SET(1),
+ SD_CMU_CMU_1F_CFG_VTUNE_SEL,
+ cmu_tgt,
+ SD_CMU_CMU_1F(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_SET(3),
+ SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0,
+ cmu_tgt,
+ SD_CMU_CMU_00(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_SET(3),
+ SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0,
+ cmu_tgt,
+ SD_CMU_CMU_05(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(1),
+ SD_CMU_CMU_30_R_PLL_DLOL_EN,
+ cmu_tgt,
+ SD_CMU_CMU_30(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_09_CFG_SW_10G_SET(spd10g),
+ SD_CMU_CMU_09_CFG_SW_10G,
+ cmu_tgt,
+ SD_CMU_CMU_09(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(0),
+ SD_CMU_CFG_SD_CMU_CFG_CMU_RST,
+ cmu_cfg_tgt,
+ SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
+
+ msleep(20);
+
+ sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(0),
+ SD_CMU_CMU_44_R_PLL_RSTN,
+ cmu_tgt,
+ SD_CMU_CMU_44(cmu_idx));
+
+ sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(1),
+ SD_CMU_CMU_44_R_PLL_RSTN,
+ cmu_tgt,
+ SD_CMU_CMU_44(cmu_idx));
+
+ msleep(20);
+
+ value = readl(sdx5_addr(regs, SD_CMU_CMU_E0(cmu_idx)));
+ value = SD_CMU_CMU_E0_PLL_LOL_UDL_GET(value);
+
+ if (value) {
+ dev_err(dev, "CMU PLL Loss of Lock: 0x%x\n", value);
+ return -EINVAL;
+ }
+ sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_SET(0),
+ SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD,
+ cmu_tgt,
+ SD_CMU_CMU_0D(cmu_idx));
+ return 0;
+}
+
+static int sparx5_cmu_cfg(struct sparx5_serdes_private *priv, u32 cmu_idx)
+{
+ void __iomem *cmu_tgt, *cmu_cfg_tgt;
+ u32 spd10g = 1;
+
+ if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 ||
+ cmu_idx == 10 || cmu_idx == 13) {
+ spd10g = 0;
+ }
+
+ cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx);
+ cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx);
+
+ return sparx5_cmu_apply_cfg(priv, cmu_idx, cmu_tgt, cmu_cfg_tgt, spd10g);
+}
+
+/* Map of 6G/10G serdes mode and index to CMU index. */
+static const int
+sparx5_serdes_cmu_map[SPX5_SD10G28_CMU_MAX][SPX5_SERDES_6G10G_CNT] = {
+ [SPX5_SD10G28_CMU_MAIN] = { 2, 2, 2, 2, 2,
+ 2, 2, 2, 5, 5,
+ 5, 5, 5, 5, 5,
+ 5, 8, 11, 11, 11,
+ 11, 11, 11, 11, 11 },
+ [SPX5_SD10G28_CMU_AUX1] = { 0, 0, 3, 3, 3,
+ 3, 3, 3, 3, 3,
+ 6, 6, 6, 6, 6,
+ 6, 6, 9, 9, 12,
+ 12, 12, 12, 12, 12 },
+ [SPX5_SD10G28_CMU_AUX2] = { 1, 1, 1, 1, 4,
+ 4, 4, 4, 4, 4,
+ 4, 4, 7, 7, 7,
+ 7, 7, 10, 10, 10,
+ 10, 13, 13, 13, 13 },
+ [SPX5_SD10G28_CMU_NONE] = { 1, 1, 1, 1, 4,
+ 4, 4, 4, 4, 4,
+ 4, 4, 7, 7, 7,
+ 7, 7, 10, 10, 10,
+ 10, 13, 13, 13, 13 },
+};
+
+/* Get the index of the CMU which provides the clock for the specified serdes
+ * mode and index.
+ */
+static int sparx5_serdes_cmu_get(enum sparx5_10g28cmu_mode mode, int sd_index)
+{
+ return sparx5_serdes_cmu_map[mode][sd_index];
+}
+
+static void sparx5_serdes_cmu_power_off(struct sparx5_serdes_private *priv)
+{
+ void __iomem *cmu_inst, *cmu_cfg_inst;
+ int i;
+
+ /* Power down each CMU */
+ for (i = 0; i < SPX5_CMU_MAX; i++) {
+ cmu_inst = sdx5_inst_get(priv, TARGET_SD_CMU, i);
+ cmu_cfg_inst = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, i);
+
+ sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(0),
+ SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, cmu_cfg_inst,
+ SD_CMU_CFG_SD_CMU_CFG(0));
+
+ sdx5_inst_rmw(SD_CMU_CMU_05_CFG_REFCK_TERM_EN_SET(0),
+ SD_CMU_CMU_05_CFG_REFCK_TERM_EN, cmu_inst,
+ SD_CMU_CMU_05(0));
+
+ sdx5_inst_rmw(SD_CMU_CMU_09_CFG_EN_TX_CK_DN_SET(0),
+ SD_CMU_CMU_09_CFG_EN_TX_CK_DN, cmu_inst,
+ SD_CMU_CMU_09(0));
+
+ sdx5_inst_rmw(SD_CMU_CMU_06_CFG_VCO_PD_SET(1),
+ SD_CMU_CMU_06_CFG_VCO_PD, cmu_inst,
+ SD_CMU_CMU_06(0));
+
+ sdx5_inst_rmw(SD_CMU_CMU_09_CFG_EN_TX_CK_UP_SET(0),
+ SD_CMU_CMU_09_CFG_EN_TX_CK_UP, cmu_inst,
+ SD_CMU_CMU_09(0));
+
+ sdx5_inst_rmw(SD_CMU_CMU_08_CFG_CK_TREE_PD_SET(1),
+ SD_CMU_CMU_08_CFG_CK_TREE_PD, cmu_inst,
+ SD_CMU_CMU_08(0));
+
+ sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_REFCK_PD_SET(1) |
+ SD_CMU_CMU_0D_CFG_PD_DIV64_SET(1) |
+ SD_CMU_CMU_0D_CFG_PD_DIV66_SET(1),
+ SD_CMU_CMU_0D_CFG_REFCK_PD |
+ SD_CMU_CMU_0D_CFG_PD_DIV64 |
+ SD_CMU_CMU_0D_CFG_PD_DIV66, cmu_inst,
+ SD_CMU_CMU_0D(0));
+
+ sdx5_inst_rmw(SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD_SET(1),
+ SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD, cmu_inst,
+ SD_CMU_CMU_06(0));
+ }
+}
+
static void sparx5_sd25g28_reset(void __iomem *regs[],
struct sparx5_sd25g28_params *params,
u32 sd_index)
@@ -1422,7 +1643,17 @@ static int sparx5_sd10g28_apply_params(struct sparx5_serdes_macro *macro,
u32 lane_index = macro->sidx;
u32 sd_index = macro->stpidx;
void __iomem *sd_inst;
- u32 value;
+ u32 value, cmu_idx;
+ int err;
+
+ /* Do not configure serdes if CMU is not to be configured too */
+ if (params->skip_cmu_cfg)
+ return 0;
+
+ cmu_idx = sparx5_serdes_cmu_get(params->cmu_sel, lane_index);
+ err = sparx5_cmu_cfg(priv, cmu_idx);
+ if (err)
+ return err;
if (params->is_6g)
sd_inst = sdx5_inst_get(priv, TARGET_SD6G_LANE, sd_index);
@@ -1884,6 +2115,7 @@ static int sparx5_sd10g28_config(struct sparx5_serdes_macro *macro, bool reset)
.rxinvert = 1,
.txswing = 240,
.reg_rst = reset,
+ .skip_cmu_cfg = reset,
};
int err;
@@ -1899,7 +2131,7 @@ static int sparx5_sd10g28_config(struct sparx5_serdes_macro *macro, bool reset)
static int sparx5_serdes_power_save(struct sparx5_serdes_macro *macro, u32 pwdn)
{
struct sparx5_serdes_private *priv = macro->priv;
- void __iomem *sd_inst;
+ void __iomem *sd_inst, *sd_lane_inst;
if (macro->serdestype == SPX5_SDT_6G)
sd_inst = sdx5_inst_get(priv, TARGET_SD6G_LANE, macro->stpidx);
@@ -1909,12 +2141,36 @@ static int sparx5_serdes_power_save(struct sparx5_serdes_macro *macro, u32 pwdn)
sd_inst = sdx5_inst_get(priv, TARGET_SD25G_LANE, macro->stpidx);
if (macro->serdestype == SPX5_SDT_25G) {
+ sd_lane_inst = sdx5_inst_get(priv, TARGET_SD_LANE_25G,
+ macro->stpidx);
+ /* Take serdes out of reset */
+ sdx5_inst_rmw(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_SET(0),
+ SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST, sd_lane_inst,
+ SD_LANE_25G_SD_LANE_CFG(0));
+
+ /* Configure optimal settings for quiet mode */
+ sdx5_inst_rmw(SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE_SET(SPX5_SERDES_QUIET_MODE_VAL),
+ SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE,
+ sd_lane_inst, SD_LANE_25G_QUIET_MODE_6G(0));
+
sdx5_inst_rmw(SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER_SET(pwdn),
SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER,
sd_inst,
SD25G_LANE_LANE_04(0));
} else {
/* 6G and 10G */
+ sd_lane_inst = sdx5_inst_get(priv, TARGET_SD_LANE, macro->sidx);
+
+ /* Take serdes out of reset */
+ sdx5_inst_rmw(SD_LANE_SD_LANE_CFG_EXT_CFG_RST_SET(0),
+ SD_LANE_SD_LANE_CFG_EXT_CFG_RST, sd_lane_inst,
+ SD_LANE_SD_LANE_CFG(0));
+
+ /* Configure optimal settings for quiet mode */
+ sdx5_inst_rmw(SD_LANE_QUIET_MODE_6G_QUIET_MODE_SET(SPX5_SERDES_QUIET_MODE_VAL),
+ SD_LANE_QUIET_MODE_6G_QUIET_MODE, sd_lane_inst,
+ SD_LANE_QUIET_MODE_6G(0));
+
sdx5_inst_rmw(SD10G_LANE_LANE_06_CFG_PD_DRIVER_SET(pwdn),
SD10G_LANE_LANE_06_CFG_PD_DRIVER,
sd_inst,
@@ -1939,159 +2195,6 @@ static int sparx5_serdes_clock_config(struct sparx5_serdes_macro *macro)
return 0;
}
-static int sparx5_cmu_apply_cfg(struct sparx5_serdes_private *priv,
- u32 cmu_idx,
- void __iomem *cmu_tgt,
- void __iomem *cmu_cfg_tgt,
- u32 spd10g)
-{
- void __iomem **regs = priv->regs;
- struct device *dev = priv->dev;
- int value;
-
- cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx);
- cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx);
-
- if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 ||
- cmu_idx == 10 || cmu_idx == 13) {
- spd10g = 0;
- }
-
- sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(1),
- SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST,
- cmu_cfg_tgt,
- SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(0),
- SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST,
- cmu_cfg_tgt,
- SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(1),
- SD_CMU_CFG_SD_CMU_CFG_CMU_RST,
- cmu_cfg_tgt,
- SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_SET(0x1) |
- SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_SET(0x1) |
- SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_SET(0x1) |
- SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_SET(0x1) |
- SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(0x0),
- SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT |
- SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT |
- SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT |
- SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT |
- SD_CMU_CMU_45_R_EN_RATECHG_CTRL,
- cmu_tgt,
- SD_CMU_CMU_45(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(0),
- SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0,
- cmu_tgt,
- SD_CMU_CMU_47(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(0),
- SD_CMU_CMU_1B_CFG_RESERVE_7_0,
- cmu_tgt,
- SD_CMU_CMU_1B(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_JC_BYP_SET(0x1),
- SD_CMU_CMU_0D_CFG_JC_BYP,
- cmu_tgt,
- SD_CMU_CMU_0D(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_1F_CFG_VTUNE_SEL_SET(1),
- SD_CMU_CMU_1F_CFG_VTUNE_SEL,
- cmu_tgt,
- SD_CMU_CMU_1F(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_SET(3),
- SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0,
- cmu_tgt,
- SD_CMU_CMU_00(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_SET(3),
- SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0,
- cmu_tgt,
- SD_CMU_CMU_05(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(1),
- SD_CMU_CMU_30_R_PLL_DLOL_EN,
- cmu_tgt,
- SD_CMU_CMU_30(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_09_CFG_SW_10G_SET(spd10g),
- SD_CMU_CMU_09_CFG_SW_10G,
- cmu_tgt,
- SD_CMU_CMU_09(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(0),
- SD_CMU_CFG_SD_CMU_CFG_CMU_RST,
- cmu_cfg_tgt,
- SD_CMU_CFG_SD_CMU_CFG(cmu_idx));
-
- msleep(20);
-
- sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(0),
- SD_CMU_CMU_44_R_PLL_RSTN,
- cmu_tgt,
- SD_CMU_CMU_44(cmu_idx));
-
- sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(1),
- SD_CMU_CMU_44_R_PLL_RSTN,
- cmu_tgt,
- SD_CMU_CMU_44(cmu_idx));
-
- msleep(20);
-
- value = readl(sdx5_addr(regs, SD_CMU_CMU_E0(cmu_idx)));
- value = SD_CMU_CMU_E0_PLL_LOL_UDL_GET(value);
-
- if (value) {
- dev_err(dev, "CMU PLL Loss of Lock: 0x%x\n", value);
- return -EINVAL;
- }
- sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_SET(0),
- SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD,
- cmu_tgt,
- SD_CMU_CMU_0D(cmu_idx));
- return 0;
-}
-
-static int sparx5_cmu_cfg(struct sparx5_serdes_private *priv, u32 cmu_idx)
-{
- void __iomem *cmu_tgt, *cmu_cfg_tgt;
- u32 spd10g = 1;
-
- if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 ||
- cmu_idx == 10 || cmu_idx == 13) {
- spd10g = 0;
- }
-
- cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx);
- cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx);
-
- return sparx5_cmu_apply_cfg(priv, cmu_idx, cmu_tgt, cmu_cfg_tgt, spd10g);
-}
-
-static int sparx5_serdes_cmu_enable(struct sparx5_serdes_private *priv)
-{
- int idx, err = 0;
-
- if (!priv->cmu_enabled) {
- for (idx = 0; idx < SPX5_CMU_MAX; idx++) {
- err = sparx5_cmu_cfg(priv, idx);
- if (err) {
- dev_err(priv->dev, "CMU %u, error: %d\n", idx, err);
- goto leave;
- }
- }
- priv->cmu_enabled = true;
- }
-leave:
- return err;
-}
-
static int sparx5_serdes_get_serdesmode(phy_interface_t portmode, int speed)
{
switch (portmode) {
@@ -2120,10 +2223,6 @@ static int sparx5_serdes_config(struct sparx5_serdes_macro *macro)
int serdesmode;
int err;
- err = sparx5_serdes_cmu_enable(macro->priv);
- if (err)
- return err;
-
serdesmode = sparx5_serdes_get_serdesmode(macro->portmode, macro->speed);
if (serdesmode < 0) {
dev_err(dev, "SerDes %u, interface not supported: %s\n",
@@ -2215,9 +2314,6 @@ static int sparx5_serdes_reset(struct phy *phy)
struct sparx5_serdes_macro *macro = phy_get_drvdata(phy);
int err;
- err = sparx5_serdes_cmu_enable(macro->priv);
- if (err)
- return err;
if (macro->serdestype == SPX5_SDT_25G)
err = sparx5_sd25g28_config(macro, true);
else
@@ -2308,6 +2404,9 @@ static int sparx5_phy_create(struct sparx5_serdes_private *priv,
phy_set_drvdata(*phy, macro);
+ /* Power off serdes by default */
+ sparx5_serdes_power_off(*phy);
+
return 0;
}
@@ -2491,6 +2590,9 @@ static int sparx5_serdes_probe(struct platform_device *pdev)
return err;
}
+ /* Power down all CMUs by default */
+ sparx5_serdes_cmu_power_off(priv);
+
provider = devm_of_phy_provider_register(priv->dev, sparx5_serdes_xlate);
return PTR_ERR_OR_ZERO(provider);
diff --git a/drivers/phy/microchip/sparx5_serdes.h b/drivers/phy/microchip/sparx5_serdes.h
index 0a3e496e6210..13f94a29225a 100644
--- a/drivers/phy/microchip/sparx5_serdes.h
+++ b/drivers/phy/microchip/sparx5_serdes.h
@@ -30,7 +30,6 @@ struct sparx5_serdes_private {
struct device *dev;
void __iomem *regs[NUM_TARGETS];
struct phy *phys[SPX5_SERDES_MAX];
- bool cmu_enabled;
unsigned long coreclock;
};
diff --git a/drivers/phy/microchip/sparx5_serdes_regs.h b/drivers/phy/microchip/sparx5_serdes_regs.h
index b96386a4df5a..d0543fd3dc94 100644
--- a/drivers/phy/microchip/sparx5_serdes_regs.h
+++ b/drivers/phy/microchip/sparx5_serdes_regs.h
@@ -2149,6 +2149,92 @@ enum sparx5_serdes_target {
#define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_GET(x)\
FIELD_GET(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, x)
+/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_06 */
+#define SD_CMU_CMU_06(t) \
+ __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 4, 0, 1, 4)
+
+#define SD_CMU_CMU_06_CFG_DISLOS BIT(0)
+#define SD_CMU_CMU_06_CFG_DISLOS_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_06_CFG_DISLOS, x)
+#define SD_CMU_CMU_06_CFG_DISLOS_GET(x)\
+ FIELD_GET(SD_CMU_CMU_06_CFG_DISLOS, x)
+
+#define SD_CMU_CMU_06_CFG_DISLOL BIT(1)
+#define SD_CMU_CMU_06_CFG_DISLOL_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_06_CFG_DISLOL, x)
+#define SD_CMU_CMU_06_CFG_DISLOL_GET(x)\
+ FIELD_GET(SD_CMU_CMU_06_CFG_DISLOL, x)
+
+#define SD_CMU_CMU_06_CFG_DCLOL BIT(2)
+#define SD_CMU_CMU_06_CFG_DCLOL_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_06_CFG_DCLOL, x)
+#define SD_CMU_CMU_06_CFG_DCLOL_GET(x)\
+ FIELD_GET(SD_CMU_CMU_06_CFG_DCLOL, x)
+
+#define SD_CMU_CMU_06_CFG_FORCE_RX_FILT BIT(3)
+#define SD_CMU_CMU_06_CFG_FORCE_RX_FILT_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_06_CFG_FORCE_RX_FILT, x)
+#define SD_CMU_CMU_06_CFG_FORCE_RX_FILT_GET(x)\
+ FIELD_GET(SD_CMU_CMU_06_CFG_FORCE_RX_FILT, x)
+
+#define SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD BIT(4)
+#define SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD, x)
+#define SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD_GET(x)\
+ FIELD_GET(SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD, x)
+
+#define SD_CMU_CMU_06_CFG_VCO_PD BIT(5)
+#define SD_CMU_CMU_06_CFG_VCO_PD_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_06_CFG_VCO_PD, x)
+#define SD_CMU_CMU_06_CFG_VCO_PD_GET(x)\
+ FIELD_GET(SD_CMU_CMU_06_CFG_VCO_PD, x)
+
+#define SD_CMU_CMU_06_CFG_VCO_CAL_RESETN BIT(6)
+#define SD_CMU_CMU_06_CFG_VCO_CAL_RESETN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_06_CFG_VCO_CAL_RESETN, x)
+#define SD_CMU_CMU_06_CFG_VCO_CAL_RESETN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_06_CFG_VCO_CAL_RESETN, x)
+
+#define SD_CMU_CMU_06_CFG_VCO_CAL_BYP BIT(7)
+#define SD_CMU_CMU_06_CFG_VCO_CAL_BYP_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_06_CFG_VCO_CAL_BYP, x)
+#define SD_CMU_CMU_06_CFG_VCO_CAL_BYP_GET(x)\
+ FIELD_GET(SD_CMU_CMU_06_CFG_VCO_CAL_BYP, x)
+
+/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_08 */
+#define SD_CMU_CMU_08(t) \
+ __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 12, 0, 1, 4)
+
+#define SD_CMU_CMU_08_CFG_VFILT2PAD BIT(0)
+#define SD_CMU_CMU_08_CFG_VFILT2PAD_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_08_CFG_VFILT2PAD, x)
+#define SD_CMU_CMU_08_CFG_VFILT2PAD_GET(x)\
+ FIELD_GET(SD_CMU_CMU_08_CFG_VFILT2PAD, x)
+
+#define SD_CMU_CMU_08_CFG_EN_DUMMY BIT(1)
+#define SD_CMU_CMU_08_CFG_EN_DUMMY_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_08_CFG_EN_DUMMY, x)
+#define SD_CMU_CMU_08_CFG_EN_DUMMY_GET(x)\
+ FIELD_GET(SD_CMU_CMU_08_CFG_EN_DUMMY, x)
+
+#define SD_CMU_CMU_08_CFG_CK_TREE_PD BIT(2)
+#define SD_CMU_CMU_08_CFG_CK_TREE_PD_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_08_CFG_CK_TREE_PD, x)
+#define SD_CMU_CMU_08_CFG_CK_TREE_PD_GET(x)\
+ FIELD_GET(SD_CMU_CMU_08_CFG_CK_TREE_PD, x)
+
+#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN BIT(3)
+#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN, x)
+#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN, x)
+
+#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN BIT(4)
+#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN_SET(x)\
+ FIELD_PREP(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN, x)
+#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN_GET(x)\
+ FIELD_GET(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN, x)
+
/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_09 */
#define SD_CMU_CMU_09(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 16, 0, 1, 4)
@@ -2443,6 +2529,16 @@ enum sparx5_serdes_target {
#define SD_LANE_SD_LANE_STAT_DBG_OBS_GET(x)\
FIELD_GET(SD_LANE_SD_LANE_STAT_DBG_OBS, x)
+/* SD_LANE_TARGET:SD_PWR_CFG:QUIET_MODE_6G */
+#define SD_LANE_QUIET_MODE_6G(t) \
+ __REG(TARGET_SD_LANE, t, 25, 24, 0, 1, 8, 4, 0, 1, 4)
+
+#define SD_LANE_QUIET_MODE_6G_QUIET_MODE GENMASK(24, 0)
+#define SD_LANE_QUIET_MODE_6G_QUIET_MODE_SET(x)\
+ FIELD_PREP(SD_LANE_QUIET_MODE_6G_QUIET_MODE, x)
+#define SD_LANE_QUIET_MODE_6G_QUIET_MODE_GET(x)\
+ FIELD_GET(SD_LANE_QUIET_MODE_6G_QUIET_MODE, x)
+
/* SD_LANE_TARGET:CFG_STAT_FX100:MISC */
#define SD_LANE_MISC(t) __REG(TARGET_SD_LANE, t, 25, 56, 0, 1, 56, 0, 0, 1, 4)
@@ -2692,4 +2788,14 @@ enum sparx5_serdes_target {
#define SD_LANE_25G_SD_LANE_STAT_DBG_OBS_GET(x)\
FIELD_GET(SD_LANE_25G_SD_LANE_STAT_DBG_OBS, x)
+/* SD25G_CFG_TARGET:SD_PWR_CFG:QUIET_MODE_6G */
+#define SD_LANE_25G_QUIET_MODE_6G(t) \
+ __REG(TARGET_SD_LANE_25G, t, 8, 28, 0, 1, 8, 4, 0, 1, 4)
+
+#define SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE GENMASK(24, 0)
+#define SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE_SET(x)\
+ FIELD_PREP(SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE, x)
+#define SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE_GET(x)\
+ FIELD_GET(SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE, x)
+
#endif /* _SPARX5_SERDES_REGS_H_ */
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 6464dcb56d56..96a0b1e111f3 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -11,6 +11,7 @@
#include <linux/export.h>
#include <linux/module.h>
#include <linux/err.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/of.h>
@@ -20,6 +21,7 @@
#include <linux/regulator/consumer.h>
static struct class *phy_class;
+static struct dentry *phy_debugfs_root;
static DEFINE_MUTEX(phy_provider_mutex);
static LIST_HEAD(phy_provider_list);
static LIST_HEAD(phys);
@@ -996,6 +998,8 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
pm_runtime_no_callbacks(&phy->dev);
}
+ phy->debugfs = debugfs_create_dir(dev_name(&phy->dev), phy_debugfs_root);
+
return phy;
put_dev:
@@ -1226,6 +1230,7 @@ static void phy_release(struct device *dev)
phy = to_phy(dev);
dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
+ debugfs_remove_recursive(phy->debugfs);
regulator_put(phy->pwr);
ida_simple_remove(&phy_ida, phy->id);
kfree(phy);
@@ -1242,6 +1247,15 @@ static int __init phy_core_init(void)
phy_class->dev_release = phy_release;
+ phy_debugfs_root = debugfs_create_dir("phy", NULL);
+
return 0;
}
device_initcall(phy_core_init);
+
+static void __exit phy_core_exit(void)
+{
+ debugfs_remove_recursive(phy_debugfs_root);
+ class_destroy(phy_class);
+}
+module_exit(phy_core_exit);
diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index 4850d48f31fa..97ca5952e34e 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -59,8 +59,11 @@ if PHY_QCOM_QMP
config PHY_QCOM_QMP_COMBO
tristate "Qualcomm QMP Combo PHY Driver"
default PHY_QCOM_QMP
+ depends on TYPEC || TYPEC=n
+ depends on DRM || DRM=n
select GENERIC_PHY
select MFD_SYSCON
+ select DRM_PANEL_BRIDGE if DRM
help
Enable this to support the QMP Combo PHY transceiver that is used
with USB3 and DisplayPort controllers on Qualcomm chips.
@@ -185,3 +188,12 @@ config PHY_QCOM_IPQ806X_USB
This option enables support for the Synopsis PHYs present inside the
Qualcomm USB3.0 DWC3 controller on ipq806x SoC. This driver supports
both HS and SS PHY controllers.
+
+config PHY_QCOM_SGMII_ETH
+ tristate "Qualcomm DWMAC SGMII SerDes/PHY driver"
+ depends on OF && (ARCH_QCOM || COMPILE_TEST)
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ help
+ Enable this to support the internal SerDes/SGMII PHY on various
+ Qualcomm chipsets.
diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile
index de3dc9ccf067..b030858e0f8d 100644
--- a/drivers/phy/qualcomm/Makefile
+++ b/drivers/phy/qualcomm/Makefile
@@ -20,4 +20,5 @@ obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o
obj-$(CONFIG_PHY_QCOM_USB_SS) += phy-qcom-usb-ss.o
obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2)+= phy-qcom-snps-femto-v2.o
-obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o
+obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o
+obj-$(CONFIG_PHY_QCOM_SGMII_ETH) += phy-qcom-sgmii-eth.o
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
index 87b17e5877ab..bebce8c591a3 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
@@ -19,6 +19,10 @@
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_mux.h>
+
+#include <drm/drm_bridge.h>
#include <dt-bindings/phy/phy-qcom-qmp.h>
@@ -63,6 +67,10 @@
/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */
#define CLAMP_EN BIT(0) /* enables i/o clamp_n */
+/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */
+#define SW_PORTSELECT_VAL BIT(0)
+#define SW_PORTSELECT_MUX BIT(1)
+
#define PHY_INIT_COMPLETE_TIMEOUT 10000
struct qmp_phy_init_tbl {
@@ -1315,14 +1323,21 @@ struct qmp_combo {
struct phy *usb_phy;
enum phy_mode mode;
+ unsigned int usb_init_count;
struct phy *dp_phy;
unsigned int dp_aux_cfg;
struct phy_configure_opts_dp dp_opts;
+ unsigned int dp_init_count;
struct clk_fixed_rate pipe_clk_fixed;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;
+
+ struct drm_bridge bridge;
+
+ struct typec_switch_dev *sw;
+ enum typec_orientation orientation;
};
static void qmp_v3_dp_aux_init(struct qmp_combo *qmp);
@@ -1954,30 +1969,24 @@ static void qmp_v3_configure_dp_tx(struct qmp_combo *qmp)
static bool qmp_combo_configure_dp_mode(struct qmp_combo *qmp)
{
+ bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
+ const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 val;
- bool reverse = false;
val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN;
- /*
- * TODO: Assume orientation is CC1 for now and two lanes, need to
- * use type-c connector to understand orientation and lanes.
- *
- * Otherwise val changes to be like below if this code understood
- * the orientation of the type-c cable.
- *
- * if (lane_cnt == 4 || orientation == ORIENTATION_CC2)
- * val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN;
- * if (lane_cnt == 4 || orientation == ORIENTATION_CC1)
- * val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
- * if (orientation == ORIENTATION_CC2)
- * writel(0x4c, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_MODE);
- */
- val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
+ if (dp_opts->lanes == 4 || reverse)
+ val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN;
+ if (dp_opts->lanes == 4 || !reverse)
+ val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN;
+
writel(val, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
- writel(0x5c, qmp->dp_dp_phy + QSERDES_DP_PHY_MODE);
+ if (reverse)
+ writel(0x4c, qmp->pcs + QSERDES_DP_PHY_MODE);
+ else
+ writel(0x5c, qmp->pcs + QSERDES_DP_PHY_MODE);
return reverse;
}
@@ -2142,6 +2151,7 @@ static void qmp_v4_configure_dp_tx(struct qmp_combo *qmp)
static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp,
unsigned int com_resetm_ctrl_reg,
unsigned int com_c_ready_status_reg,
+ unsigned int com_cmn_status_reg,
unsigned int dp_phy_status_reg)
{
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
@@ -2198,14 +2208,14 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp,
10000))
return -ETIMEDOUT;
- if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V4_COM_CMN_STATUS,
+ if (readl_poll_timeout(qmp->dp_serdes + com_cmn_status_reg,
status,
((status & BIT(0)) > 0),
500,
10000))
return -ETIMEDOUT;
- if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V4_COM_CMN_STATUS,
+ if (readl_poll_timeout(qmp->dp_serdes + com_cmn_status_reg,
status,
((status & BIT(1)) > 0),
500,
@@ -2233,14 +2243,15 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp,
static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp)
{
+ bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
u32 status;
int ret;
ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V4_COM_RESETSM_CNTRL,
QSERDES_V4_COM_C_READY_STATUS,
+ QSERDES_V4_COM_CMN_STATUS,
QSERDES_V4_DP_PHY_STATUS);
if (ret < 0)
return ret;
@@ -2297,14 +2308,15 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp)
static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp)
{
+ bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
u32 status;
int ret;
ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V4_COM_RESETSM_CNTRL,
QSERDES_V4_COM_C_READY_STATUS,
+ QSERDES_V4_COM_CMN_STATUS,
QSERDES_V4_DP_PHY_STATUS);
if (ret < 0)
return ret;
@@ -2356,14 +2368,15 @@ static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp)
static int qmp_v6_configure_dp_phy(struct qmp_combo *qmp)
{
+ bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
- bool reverse = false;
u32 status;
int ret;
ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V6_COM_RESETSM_CNTRL,
QSERDES_V6_COM_C_READY_STATUS,
+ QSERDES_V6_COM_CMN_STATUS,
QSERDES_V6_DP_PHY_STATUS);
if (ret < 0)
return ret;
@@ -2437,12 +2450,16 @@ static int qmp_combo_dp_configure(struct phy *phy, union phy_configure_opts *opt
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ mutex_lock(&qmp->phy_mutex);
+
memcpy(&qmp->dp_opts, dp_opts, sizeof(*dp_opts));
if (qmp->dp_opts.set_voltages) {
cfg->configure_dp_tx(qmp);
qmp->dp_opts.set_voltages = 0;
}
+ mutex_unlock(&qmp->phy_mutex);
+
return 0;
}
@@ -2450,24 +2467,27 @@ static int qmp_combo_dp_calibrate(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
const struct qmp_phy_cfg *cfg = qmp->cfg;
+ int ret = 0;
+
+ mutex_lock(&qmp->phy_mutex);
if (cfg->calibrate_dp_phy)
- return cfg->calibrate_dp_phy(qmp);
+ ret = cfg->calibrate_dp_phy(qmp);
- return 0;
+ mutex_unlock(&qmp->phy_mutex);
+
+ return ret;
}
-static int qmp_combo_com_init(struct qmp_combo *qmp)
+static int qmp_combo_com_init(struct qmp_combo *qmp, bool force)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *com = qmp->com;
int ret;
+ u32 val;
- mutex_lock(&qmp->phy_mutex);
- if (qmp->init_count++) {
- mutex_unlock(&qmp->phy_mutex);
+ if (!force && qmp->init_count++)
return 0;
- }
ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
if (ret) {
@@ -2498,10 +2518,12 @@ static int qmp_combo_com_init(struct qmp_combo *qmp)
SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
- /* Default type-c orientation, i.e CC1 */
- qphy_setbits(com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02);
-
- qphy_setbits(com, QPHY_V3_DP_COM_PHY_MODE_CTRL, USB3_MODE | DP_MODE);
+ /* Use software based port select and switch on typec orientation */
+ val = SW_PORTSELECT_MUX;
+ if (qmp->orientation == TYPEC_ORIENTATION_REVERSE)
+ val |= SW_PORTSELECT_VAL;
+ writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL);
+ writel(USB3_MODE | DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL);
/* bring both QMP USB and QMP DP PHYs PCS block out of reset */
qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL,
@@ -2514,8 +2536,6 @@ static int qmp_combo_com_init(struct qmp_combo *qmp)
qphy_setbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
SW_PWRDN);
- mutex_unlock(&qmp->phy_mutex);
-
return 0;
err_assert_reset:
@@ -2524,20 +2544,16 @@ err_disable_regulators:
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
err_decrement_count:
qmp->init_count--;
- mutex_unlock(&qmp->phy_mutex);
return ret;
}
-static int qmp_combo_com_exit(struct qmp_combo *qmp)
+static int qmp_combo_com_exit(struct qmp_combo *qmp, bool force)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
- mutex_lock(&qmp->phy_mutex);
- if (--qmp->init_count) {
- mutex_unlock(&qmp->phy_mutex);
+ if (!force && --qmp->init_count)
return 0;
- }
reset_control_bulk_assert(cfg->num_resets, qmp->resets);
@@ -2545,8 +2561,6 @@ static int qmp_combo_com_exit(struct qmp_combo *qmp)
regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
- mutex_unlock(&qmp->phy_mutex);
-
return 0;
}
@@ -2556,20 +2570,32 @@ static int qmp_combo_dp_init(struct phy *phy)
const struct qmp_phy_cfg *cfg = qmp->cfg;
int ret;
- ret = qmp_combo_com_init(qmp);
+ mutex_lock(&qmp->phy_mutex);
+
+ ret = qmp_combo_com_init(qmp, false);
if (ret)
- return ret;
+ goto out_unlock;
cfg->dp_aux_init(qmp);
- return 0;
+ qmp->dp_init_count++;
+
+out_unlock:
+ mutex_unlock(&qmp->phy_mutex);
+ return ret;
}
static int qmp_combo_dp_exit(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
- qmp_combo_com_exit(qmp);
+ mutex_lock(&qmp->phy_mutex);
+
+ qmp_combo_com_exit(qmp, false);
+
+ qmp->dp_init_count--;
+
+ mutex_unlock(&qmp->phy_mutex);
return 0;
}
@@ -2581,6 +2607,8 @@ static int qmp_combo_dp_power_on(struct phy *phy)
void __iomem *tx = qmp->dp_tx;
void __iomem *tx2 = qmp->dp_tx2;
+ mutex_lock(&qmp->phy_mutex);
+
qmp_combo_dp_serdes_init(qmp);
qmp_combo_configure_lane(tx, cfg->dp_tx_tbl, cfg->dp_tx_tbl_num, 1);
@@ -2592,6 +2620,8 @@ static int qmp_combo_dp_power_on(struct phy *phy)
/* Configure link rate, swing, etc. */
cfg->configure_dp_phy(qmp);
+ mutex_unlock(&qmp->phy_mutex);
+
return 0;
}
@@ -2599,9 +2629,13 @@ static int qmp_combo_dp_power_off(struct phy *phy)
{
struct qmp_combo *qmp = phy_get_drvdata(phy);
+ mutex_lock(&qmp->phy_mutex);
+
/* Assert DP PHY power down */
writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
+ mutex_unlock(&qmp->phy_mutex);
+
return 0;
}
@@ -2687,14 +2721,21 @@ static int qmp_combo_usb_init(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
- ret = qmp_combo_com_init(qmp);
+ mutex_lock(&qmp->phy_mutex);
+ ret = qmp_combo_com_init(qmp, false);
if (ret)
- return ret;
+ goto out_unlock;
ret = qmp_combo_usb_power_on(phy);
- if (ret)
- qmp_combo_com_exit(qmp);
+ if (ret) {
+ qmp_combo_com_exit(qmp, false);
+ goto out_unlock;
+ }
+
+ qmp->usb_init_count++;
+out_unlock:
+ mutex_unlock(&qmp->phy_mutex);
return ret;
}
@@ -2703,11 +2744,20 @@ static int qmp_combo_usb_exit(struct phy *phy)
struct qmp_combo *qmp = phy_get_drvdata(phy);
int ret;
+ mutex_lock(&qmp->phy_mutex);
ret = qmp_combo_usb_power_off(phy);
if (ret)
- return ret;
+ goto out_unlock;
+
+ ret = qmp_combo_com_exit(qmp, false);
+ if (ret)
+ goto out_unlock;
+
+ qmp->usb_init_count--;
- return qmp_combo_com_exit(qmp);
+out_unlock:
+ mutex_unlock(&qmp->phy_mutex);
+ return ret;
}
static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode)
@@ -3173,6 +3223,103 @@ static int qmp_combo_register_clocks(struct qmp_combo *qmp, struct device_node *
return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, dp_np);
}
+#if IS_ENABLED(CONFIG_TYPEC)
+static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct qmp_combo *qmp = typec_switch_get_drvdata(sw);
+ const struct qmp_phy_cfg *cfg = qmp->cfg;
+
+ if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE)
+ return 0;
+
+ mutex_lock(&qmp->phy_mutex);
+ qmp->orientation = orientation;
+
+ if (qmp->init_count) {
+ if (qmp->usb_init_count)
+ qmp_combo_usb_power_off(qmp->usb_phy);
+ qmp_combo_com_exit(qmp, true);
+
+ qmp_combo_com_init(qmp, true);
+ if (qmp->usb_init_count)
+ qmp_combo_usb_power_on(qmp->usb_phy);
+ if (qmp->dp_init_count)
+ cfg->dp_aux_init(qmp);
+ }
+ mutex_unlock(&qmp->phy_mutex);
+
+ return 0;
+}
+
+static void qmp_combo_typec_unregister(void *data)
+{
+ struct qmp_combo *qmp = data;
+
+ typec_switch_unregister(qmp->sw);
+}
+
+static int qmp_combo_typec_switch_register(struct qmp_combo *qmp)
+{
+ struct typec_switch_desc sw_desc = {};
+ struct device *dev = qmp->dev;
+
+ sw_desc.drvdata = qmp;
+ sw_desc.fwnode = dev->fwnode;
+ sw_desc.set = qmp_combo_typec_switch_set;
+ qmp->sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(qmp->sw)) {
+ dev_err(dev, "Unable to register typec switch: %pe\n", qmp->sw);
+ return PTR_ERR(qmp->sw);
+ }
+
+ return devm_add_action_or_reset(dev, qmp_combo_typec_unregister, qmp);
+}
+#else
+static int qmp_combo_typec_switch_register(struct qmp_combo *qmp)
+{
+ return 0;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_DRM)
+static int qmp_combo_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct qmp_combo *qmp = container_of(bridge, struct qmp_combo, bridge);
+ struct drm_bridge *next_bridge;
+
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
+ return -EINVAL;
+
+ next_bridge = devm_drm_of_get_bridge(qmp->dev, qmp->dev->of_node, 0, 0);
+ if (IS_ERR(next_bridge)) {
+ dev_err(qmp->dev, "failed to acquire drm_bridge: %pe\n", next_bridge);
+ return PTR_ERR(next_bridge);
+ }
+
+ return drm_bridge_attach(bridge->encoder, next_bridge, bridge,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+static const struct drm_bridge_funcs qmp_combo_bridge_funcs = {
+ .attach = qmp_combo_bridge_attach,
+};
+
+static int qmp_combo_dp_register_bridge(struct qmp_combo *qmp)
+{
+ qmp->bridge.funcs = &qmp_combo_bridge_funcs;
+ qmp->bridge.of_node = qmp->dev->of_node;
+
+ return devm_drm_bridge_add(qmp->dev, &qmp->bridge);
+}
+#else
+static int qmp_combo_dp_register_bridge(struct qmp_combo *qmp)
+{
+ return 0;
+}
+#endif
+
static int qmp_combo_parse_dt_lecacy_dp(struct qmp_combo *qmp, struct device_node *np)
{
struct device *dev = qmp->dev;
@@ -3353,6 +3500,8 @@ static int qmp_combo_probe(struct platform_device *pdev)
qmp->dev = dev;
+ qmp->orientation = TYPEC_ORIENTATION_NORMAL;
+
qmp->cfg = of_device_get_match_data(dev);
if (!qmp->cfg)
return -EINVAL;
@@ -3371,6 +3520,14 @@ static int qmp_combo_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = qmp_combo_typec_switch_register(qmp);
+ if (ret)
+ return ret;
+
+ ret = qmp_combo_dp_register_bridge(qmp);
+ if (ret)
+ return ret;
+
/* Check for legacy binding with child nodes. */
usb_np = of_get_child_by_name(dev->of_node, "usb3-phy");
if (usb_np) {
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
index a49711c5a63d..466f0a56c82e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c
@@ -139,6 +139,88 @@ static const unsigned int qmp_v5_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR,
};
+static const struct qmp_phy_init_tbl ipq9574_usb3_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06),
+ /* PLL and Loop filter settings */
+ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x68),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0xab),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0xa0),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xaa),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x29),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+ /* SSC settings */
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x7d),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x05),
+};
+
+static const struct qmp_phy_init_tbl ipq9574_usb3_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+ QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
+};
+
+static const struct qmp_phy_init_tbl ipq9574_usb3_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x6c),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+ QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x0c),
+};
+
+static const struct qmp_phy_init_tbl ipq9574_usb3_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0e),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f),
+};
+
static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a),
QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
@@ -1408,12 +1490,36 @@ static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21),
};
+static const struct qmp_phy_init_tbl sa8775p_usb3_uniphy_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG1, 0xc4),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG2, 0x89),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG3, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG6, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0xaa),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCS_TX_RX_CONFIG, 0x0c),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_POWER_STATE_CONFIG1, 0x6f),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_CDR_RESET_TIME, 0x0a),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG1, 0x4b),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG5, 0x10),
+ QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21),
+};
+
struct qmp_usb_offsets {
u16 serdes;
u16 pcs;
+ u16 pcs_misc;
u16 pcs_usb;
u16 tx;
u16 rx;
+ /* for PHYs with >= 2 lanes */
+ u16 tx2;
+ u16 rx2;
};
/* struct qmp_phy_cfg - per-PHY initialization config */
@@ -1558,6 +1664,24 @@ static const char * const qmp_phy_vreg_l[] = {
"vdda-phy", "vdda-pll",
};
+static const struct qmp_usb_offsets qmp_usb_offsets_ipq9574 = {
+ .serdes = 0,
+ .pcs = 0x800,
+ .pcs_usb = 0x800,
+ .tx = 0x200,
+ .rx = 0x400,
+};
+
+static const struct qmp_usb_offsets qmp_usb_offsets_v3 = {
+ .serdes = 0,
+ .pcs = 0xc00,
+ .pcs_misc = 0xa00,
+ .tx = 0x200,
+ .rx = 0x400,
+ .tx2 = 0x600,
+ .rx2 = 0x800,
+};
+
static const struct qmp_usb_offsets qmp_usb_offsets_v5 = {
.serdes = 0,
.pcs = 0x0200,
@@ -1586,6 +1710,28 @@ static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = {
.regs = qmp_v3_usb3phy_regs_layout,
};
+static const struct qmp_phy_cfg ipq9574_usb3phy_cfg = {
+ .lanes = 1,
+
+ .offsets = &qmp_usb_offsets_ipq9574,
+
+ .serdes_tbl = ipq9574_usb3_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(ipq9574_usb3_serdes_tbl),
+ .tx_tbl = ipq9574_usb3_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(ipq9574_usb3_tx_tbl),
+ .rx_tbl = ipq9574_usb3_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(ipq9574_usb3_rx_tbl),
+ .pcs_tbl = ipq9574_usb3_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(ipq9574_usb3_pcs_tbl),
+ .clk_list = msm8996_phy_clk_l,
+ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
+ .reset_list = qcm2290_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v3_usb3phy_regs_layout,
+};
+
static const struct qmp_phy_cfg msm8996_usb3phy_cfg = {
.lanes = 1,
@@ -1629,6 +1775,28 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
.has_phy_dp_com_ctrl = true,
};
+static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = {
+ .lanes = 1,
+
+ .offsets = &qmp_usb_offsets_v5,
+
+ .serdes_tbl = sc8280xp_usb3_uniphy_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_serdes_tbl),
+ .tx_tbl = sc8280xp_usb3_uniphy_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_tx_tbl),
+ .rx_tbl = sc8280xp_usb3_uniphy_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_rx_tbl),
+ .pcs_tbl = sa8775p_usb3_uniphy_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(sa8775p_usb3_uniphy_pcs_tbl),
+ .clk_list = qmp_v4_phy_clk_l,
+ .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l),
+ .reset_list = qcm2290_usb3phy_reset_l,
+ .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = qmp_v5_usb3phy_regs_layout,
+};
+
static const struct qmp_phy_cfg sc7180_usb3phy_cfg = {
.lanes = 2,
@@ -1922,6 +2090,8 @@ static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = {
static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = {
.lanes = 2,
+ .offsets = &qmp_usb_offsets_v3,
+
.serdes_tbl = qcm2290_usb3_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl),
.tx_tbl = qcm2290_usb3_tx_tbl,
@@ -2493,10 +2663,16 @@ static int qmp_usb_parse_dt(struct qmp_usb *qmp)
qmp->serdes = base + offs->serdes;
qmp->pcs = base + offs->pcs;
+ qmp->pcs_misc = base + offs->pcs_misc;
qmp->pcs_usb = base + offs->pcs_usb;
qmp->tx = base + offs->tx;
qmp->rx = base + offs->rx;
+ if (cfg->lanes >= 2) {
+ qmp->tx2 = base + offs->tx2;
+ qmp->rx2 = base + offs->rx2;
+ }
+
qmp->pipe_clk = devm_clk_get(dev, "pipe");
if (IS_ERR(qmp->pipe_clk)) {
return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk),
@@ -2589,6 +2765,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = {
.compatible = "qcom,ipq8074-qmp-usb3-phy",
.data = &ipq8074_usb3phy_cfg,
}, {
+ .compatible = "qcom,ipq9574-qmp-usb3-phy",
+ .data = &ipq9574_usb3phy_cfg,
+ }, {
.compatible = "qcom,msm8996-qmp-usb3-phy",
.data = &msm8996_usb3phy_cfg,
}, {
@@ -2598,6 +2777,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = {
.compatible = "qcom,qcm2290-qmp-usb3-phy",
.data = &qcm2290_usb3phy_cfg,
}, {
+ .compatible = "qcom,sa8775p-qmp-usb3-uni-phy",
+ .data = &sa8775p_usb3_uniphy_cfg,
+ }, {
.compatible = "qcom,sc7180-qmp-usb3-phy",
.data = &sc7180_usb3phy_cfg,
}, {
diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index 2ef638b32e8f..bec6e40d5280 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -912,6 +912,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
.compatible = "qcom,ipq8074-qusb2-phy",
.data = &msm8996_phy_cfg,
}, {
+ .compatible = "qcom,ipq9574-qusb2-phy",
+ .data = &ipq6018_phy_cfg,
+ }, {
.compatible = "qcom,msm8953-qusb2-phy",
.data = &msm8996_phy_cfg,
}, {
diff --git a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
new file mode 100644
index 000000000000..03dc753f0de1
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023, Linaro Limited
+ */
+
+#include <linux/clk.h>
+#include <linux/ethtool.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define QSERDES_QMP_PLL 0x0
+#define QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0 (QSERDES_QMP_PLL + 0x1ac)
+#define QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0 (QSERDES_QMP_PLL + 0x1b0)
+#define QSERDES_COM_BIN_VCOCAL_HSCLK_SEL (QSERDES_QMP_PLL + 0x1bc)
+#define QSERDES_COM_CORE_CLK_EN (QSERDES_QMP_PLL + 0x174)
+#define QSERDES_COM_CORECLK_DIV_MODE0 (QSERDES_QMP_PLL + 0x168)
+#define QSERDES_COM_CP_CTRL_MODE0 (QSERDES_QMP_PLL + 0x74)
+#define QSERDES_COM_DEC_START_MODE0 (QSERDES_QMP_PLL + 0xbc)
+#define QSERDES_COM_DIV_FRAC_START1_MODE0 (QSERDES_QMP_PLL + 0xcc)
+#define QSERDES_COM_DIV_FRAC_START2_MODE0 (QSERDES_QMP_PLL + 0xd0)
+#define QSERDES_COM_DIV_FRAC_START3_MODE0 (QSERDES_QMP_PLL + 0xd4)
+#define QSERDES_COM_HSCLK_HS_SWITCH_SEL (QSERDES_QMP_PLL + 0x15c)
+#define QSERDES_COM_HSCLK_SEL (QSERDES_QMP_PLL + 0x158)
+#define QSERDES_COM_LOCK_CMP1_MODE0 (QSERDES_QMP_PLL + 0xac)
+#define QSERDES_COM_LOCK_CMP2_MODE0 (QSERDES_QMP_PLL + 0xb0)
+#define QSERDES_COM_PLL_CCTRL_MODE0 (QSERDES_QMP_PLL + 0x84)
+#define QSERDES_COM_PLL_IVCO (QSERDES_QMP_PLL + 0x58)
+#define QSERDES_COM_PLL_RCTRL_MODE0 (QSERDES_QMP_PLL + 0x7c)
+#define QSERDES_COM_SYSCLK_EN_SEL (QSERDES_QMP_PLL + 0x94)
+#define QSERDES_COM_VCO_TUNE1_MODE0 (QSERDES_QMP_PLL + 0x110)
+#define QSERDES_COM_VCO_TUNE2_MODE0 (QSERDES_QMP_PLL + 0x114)
+#define QSERDES_COM_VCO_TUNE_INITVAL2 (QSERDES_QMP_PLL + 0x124)
+#define QSERDES_COM_C_READY_STATUS (QSERDES_QMP_PLL + 0x178)
+#define QSERDES_COM_CMN_STATUS (QSERDES_QMP_PLL + 0x140)
+
+#define QSERDES_RX 0x600
+#define QSERDES_RX_UCDR_FO_GAIN (QSERDES_RX + 0x8)
+#define QSERDES_RX_UCDR_SO_GAIN (QSERDES_RX + 0x14)
+#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN (QSERDES_RX + 0x30)
+#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE (QSERDES_RX + 0x34)
+#define QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW (QSERDES_RX + 0x3c)
+#define QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH (QSERDES_RX + 0x40)
+#define QSERDES_RX_UCDR_PI_CONTROLS (QSERDES_RX + 0x44)
+#define QSERDES_RX_UCDR_PI_CTRL2 (QSERDES_RX + 0x48)
+#define QSERDES_RX_RX_TERM_BW (QSERDES_RX + 0x80)
+#define QSERDES_RX_VGA_CAL_CNTRL2 (QSERDES_RX + 0xd8)
+#define QSERDES_RX_GM_CAL (QSERDES_RX + 0xdc)
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 (QSERDES_RX + 0xe8)
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 (QSERDES_RX + 0xec)
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 (QSERDES_RX + 0xf0)
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 (QSERDES_RX + 0xf4)
+#define QSERDES_RX_RX_IDAC_TSETTLE_LOW (QSERDES_RX + 0xf8)
+#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH (QSERDES_RX + 0xfc)
+#define QSERDES_RX_RX_IDAC_MEASURE_TIME (QSERDES_RX + 0x100)
+#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 (QSERDES_RX + 0x110)
+#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 (QSERDES_RX + 0x114)
+#define QSERDES_RX_SIGDET_CNTRL (QSERDES_RX + 0x11c)
+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL (QSERDES_RX + 0x124)
+#define QSERDES_RX_RX_BAND (QSERDES_RX + 0x128)
+#define QSERDES_RX_RX_MODE_00_LOW (QSERDES_RX + 0x15c)
+#define QSERDES_RX_RX_MODE_00_HIGH (QSERDES_RX + 0x160)
+#define QSERDES_RX_RX_MODE_00_HIGH2 (QSERDES_RX + 0x164)
+#define QSERDES_RX_RX_MODE_00_HIGH3 (QSERDES_RX + 0x168)
+#define QSERDES_RX_RX_MODE_00_HIGH4 (QSERDES_RX + 0x16c)
+#define QSERDES_RX_RX_MODE_01_LOW (QSERDES_RX + 0x170)
+#define QSERDES_RX_RX_MODE_01_HIGH (QSERDES_RX + 0x174)
+#define QSERDES_RX_RX_MODE_01_HIGH2 (QSERDES_RX + 0x178)
+#define QSERDES_RX_RX_MODE_01_HIGH3 (QSERDES_RX + 0x17c)
+#define QSERDES_RX_RX_MODE_01_HIGH4 (QSERDES_RX + 0x180)
+#define QSERDES_RX_RX_MODE_10_LOW (QSERDES_RX + 0x184)
+#define QSERDES_RX_RX_MODE_10_HIGH (QSERDES_RX + 0x188)
+#define QSERDES_RX_RX_MODE_10_HIGH2 (QSERDES_RX + 0x18c)
+#define QSERDES_RX_RX_MODE_10_HIGH3 (QSERDES_RX + 0x190)
+#define QSERDES_RX_RX_MODE_10_HIGH4 (QSERDES_RX + 0x194)
+#define QSERDES_RX_DCC_CTRL1 (QSERDES_RX + 0x1a8)
+
+#define QSERDES_TX 0x400
+#define QSERDES_TX_TX_BAND (QSERDES_TX + 0x24)
+#define QSERDES_TX_SLEW_CNTL (QSERDES_TX + 0x28)
+#define QSERDES_TX_RES_CODE_LANE_OFFSET_TX (QSERDES_TX + 0x3c)
+#define QSERDES_TX_RES_CODE_LANE_OFFSET_RX (QSERDES_TX + 0x40)
+#define QSERDES_TX_LANE_MODE_1 (QSERDES_TX + 0x84)
+#define QSERDES_TX_LANE_MODE_3 (QSERDES_TX + 0x8c)
+#define QSERDES_TX_RCV_DETECT_LVL_2 (QSERDES_TX + 0xa4)
+#define QSERDES_TX_TRAN_DRVR_EMP_EN (QSERDES_TX + 0xc0)
+
+#define QSERDES_PCS 0xC00
+#define QSERDES_PCS_PHY_START (QSERDES_PCS + 0x0)
+#define QSERDES_PCS_POWER_DOWN_CONTROL (QSERDES_PCS + 0x4)
+#define QSERDES_PCS_SW_RESET (QSERDES_PCS + 0x8)
+#define QSERDES_PCS_LINE_RESET_TIME (QSERDES_PCS + 0xc)
+#define QSERDES_PCS_TX_LARGE_AMP_DRV_LVL (QSERDES_PCS + 0x20)
+#define QSERDES_PCS_TX_SMALL_AMP_DRV_LVL (QSERDES_PCS + 0x28)
+#define QSERDES_PCS_TX_MID_TERM_CTRL1 (QSERDES_PCS + 0xd8)
+#define QSERDES_PCS_TX_MID_TERM_CTRL2 (QSERDES_PCS + 0xdc)
+#define QSERDES_PCS_SGMII_MISC_CTRL8 (QSERDES_PCS + 0x118)
+#define QSERDES_PCS_PCS_READY_STATUS (QSERDES_PCS + 0x94)
+
+#define QSERDES_COM_C_READY BIT(0)
+#define QSERDES_PCS_READY BIT(0)
+#define QSERDES_PCS_SGMIIPHY_READY BIT(7)
+#define QSERDES_COM_C_PLL_LOCKED BIT(1)
+
+struct qcom_dwmac_sgmii_phy_data {
+ struct regmap *regmap;
+ struct clk *refclk;
+ int speed;
+};
+
+static void qcom_dwmac_sgmii_phy_init_1g(struct regmap *regmap)
+{
+ regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x01);
+ regmap_write(regmap, QSERDES_PCS_POWER_DOWN_CONTROL, 0x01);
+
+ regmap_write(regmap, QSERDES_COM_PLL_IVCO, 0x0F);
+ regmap_write(regmap, QSERDES_COM_CP_CTRL_MODE0, 0x06);
+ regmap_write(regmap, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+ regmap_write(regmap, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
+ regmap_write(regmap, QSERDES_COM_SYSCLK_EN_SEL, 0x1A);
+ regmap_write(regmap, QSERDES_COM_LOCK_CMP1_MODE0, 0x0A);
+ regmap_write(regmap, QSERDES_COM_LOCK_CMP2_MODE0, 0x1A);
+ regmap_write(regmap, QSERDES_COM_DEC_START_MODE0, 0x82);
+ regmap_write(regmap, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55);
+ regmap_write(regmap, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55);
+ regmap_write(regmap, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03);
+ regmap_write(regmap, QSERDES_COM_VCO_TUNE1_MODE0, 0x24);
+
+ regmap_write(regmap, QSERDES_COM_VCO_TUNE2_MODE0, 0x02);
+ regmap_write(regmap, QSERDES_COM_VCO_TUNE_INITVAL2, 0x00);
+ regmap_write(regmap, QSERDES_COM_HSCLK_SEL, 0x04);
+ regmap_write(regmap, QSERDES_COM_HSCLK_HS_SWITCH_SEL, 0x00);
+ regmap_write(regmap, QSERDES_COM_CORECLK_DIV_MODE0, 0x0A);
+ regmap_write(regmap, QSERDES_COM_CORE_CLK_EN, 0x00);
+ regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xB9);
+ regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1E);
+ regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_HSCLK_SEL, 0x11);
+
+ regmap_write(regmap, QSERDES_TX_TX_BAND, 0x05);
+ regmap_write(regmap, QSERDES_TX_SLEW_CNTL, 0x0A);
+ regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_TX, 0x09);
+ regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_RX, 0x09);
+ regmap_write(regmap, QSERDES_TX_LANE_MODE_1, 0x05);
+ regmap_write(regmap, QSERDES_TX_LANE_MODE_3, 0x00);
+ regmap_write(regmap, QSERDES_TX_RCV_DETECT_LVL_2, 0x12);
+ regmap_write(regmap, QSERDES_TX_TRAN_DRVR_EMP_EN, 0x0C);
+
+ regmap_write(regmap, QSERDES_RX_UCDR_FO_GAIN, 0x0A);
+ regmap_write(regmap, QSERDES_RX_UCDR_SO_GAIN, 0x06);
+ regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0A);
+ regmap_write(regmap, QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7F);
+ regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00);
+ regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x01);
+ regmap_write(regmap, QSERDES_RX_UCDR_PI_CONTROLS, 0x81);
+ regmap_write(regmap, QSERDES_RX_UCDR_PI_CTRL2, 0x80);
+ regmap_write(regmap, QSERDES_RX_RX_TERM_BW, 0x04);
+ regmap_write(regmap, QSERDES_RX_VGA_CAL_CNTRL2, 0x08);
+ regmap_write(regmap, QSERDES_RX_GM_CAL, 0x0F);
+ regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04);
+ regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x00);
+ regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4A);
+ regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0A);
+ regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_LOW, 0x80);
+ regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_HIGH, 0x01);
+ regmap_write(regmap, QSERDES_RX_RX_IDAC_MEASURE_TIME, 0x20);
+ regmap_write(regmap, QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17);
+ regmap_write(regmap, QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00);
+ regmap_write(regmap, QSERDES_RX_SIGDET_CNTRL, 0x0F);
+ regmap_write(regmap, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E);
+ regmap_write(regmap, QSERDES_RX_RX_BAND, 0x05);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_LOW, 0xE0);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH2, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH3, 0x09);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH4, 0xB1);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_LOW, 0xE0);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH2, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH3, 0x09);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH4, 0xB1);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_LOW, 0xE0);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH2, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH3, 0x3B);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH4, 0xB7);
+ regmap_write(regmap, QSERDES_RX_DCC_CTRL1, 0x0C);
+
+ regmap_write(regmap, QSERDES_PCS_LINE_RESET_TIME, 0x0C);
+ regmap_write(regmap, QSERDES_PCS_TX_LARGE_AMP_DRV_LVL, 0x1F);
+ regmap_write(regmap, QSERDES_PCS_TX_SMALL_AMP_DRV_LVL, 0x03);
+ regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL1, 0x83);
+ regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08);
+ regmap_write(regmap, QSERDES_PCS_SGMII_MISC_CTRL8, 0x0C);
+ regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x00);
+
+ regmap_write(regmap, QSERDES_PCS_PHY_START, 0x01);
+}
+
+static void qcom_dwmac_sgmii_phy_init_2p5g(struct regmap *regmap)
+{
+ regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x01);
+ regmap_write(regmap, QSERDES_PCS_POWER_DOWN_CONTROL, 0x01);
+
+ regmap_write(regmap, QSERDES_COM_PLL_IVCO, 0x0F);
+ regmap_write(regmap, QSERDES_COM_CP_CTRL_MODE0, 0x06);
+ regmap_write(regmap, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+ regmap_write(regmap, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
+ regmap_write(regmap, QSERDES_COM_SYSCLK_EN_SEL, 0x1A);
+ regmap_write(regmap, QSERDES_COM_LOCK_CMP1_MODE0, 0x1A);
+ regmap_write(regmap, QSERDES_COM_LOCK_CMP2_MODE0, 0x41);
+ regmap_write(regmap, QSERDES_COM_DEC_START_MODE0, 0x7A);
+ regmap_write(regmap, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00);
+ regmap_write(regmap, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x20);
+ regmap_write(regmap, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x01);
+ regmap_write(regmap, QSERDES_COM_VCO_TUNE1_MODE0, 0xA1);
+
+ regmap_write(regmap, QSERDES_COM_VCO_TUNE2_MODE0, 0x02);
+ regmap_write(regmap, QSERDES_COM_VCO_TUNE_INITVAL2, 0x00);
+ regmap_write(regmap, QSERDES_COM_HSCLK_SEL, 0x03);
+ regmap_write(regmap, QSERDES_COM_HSCLK_HS_SWITCH_SEL, 0x00);
+ regmap_write(regmap, QSERDES_COM_CORECLK_DIV_MODE0, 0x05);
+ regmap_write(regmap, QSERDES_COM_CORE_CLK_EN, 0x00);
+ regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xCD);
+ regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1C);
+ regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_HSCLK_SEL, 0x11);
+
+ regmap_write(regmap, QSERDES_TX_TX_BAND, 0x04);
+ regmap_write(regmap, QSERDES_TX_SLEW_CNTL, 0x0A);
+ regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_TX, 0x09);
+ regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_RX, 0x02);
+ regmap_write(regmap, QSERDES_TX_LANE_MODE_1, 0x05);
+ regmap_write(regmap, QSERDES_TX_LANE_MODE_3, 0x00);
+ regmap_write(regmap, QSERDES_TX_RCV_DETECT_LVL_2, 0x12);
+ regmap_write(regmap, QSERDES_TX_TRAN_DRVR_EMP_EN, 0x0C);
+
+ regmap_write(regmap, QSERDES_RX_UCDR_FO_GAIN, 0x0A);
+ regmap_write(regmap, QSERDES_RX_UCDR_SO_GAIN, 0x06);
+ regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0A);
+ regmap_write(regmap, QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7F);
+ regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00);
+ regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x01);
+ regmap_write(regmap, QSERDES_RX_UCDR_PI_CONTROLS, 0x81);
+ regmap_write(regmap, QSERDES_RX_UCDR_PI_CTRL2, 0x80);
+ regmap_write(regmap, QSERDES_RX_RX_TERM_BW, 0x00);
+ regmap_write(regmap, QSERDES_RX_VGA_CAL_CNTRL2, 0x08);
+ regmap_write(regmap, QSERDES_RX_GM_CAL, 0x0F);
+ regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04);
+ regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x00);
+ regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4A);
+ regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0A);
+ regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_LOW, 0x80);
+ regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_HIGH, 0x01);
+ regmap_write(regmap, QSERDES_RX_RX_IDAC_MEASURE_TIME, 0x20);
+ regmap_write(regmap, QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17);
+ regmap_write(regmap, QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00);
+ regmap_write(regmap, QSERDES_RX_SIGDET_CNTRL, 0x0F);
+ regmap_write(regmap, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E);
+ regmap_write(regmap, QSERDES_RX_RX_BAND, 0x18);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_LOW, 0x18);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH2, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH3, 0x0C);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH4, 0xB8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_LOW, 0xE0);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH2, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH3, 0x09);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH4, 0xB1);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_LOW, 0xE0);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH2, 0xC8);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH3, 0x3B);
+ regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH4, 0xB7);
+ regmap_write(regmap, QSERDES_RX_DCC_CTRL1, 0x0C);
+
+ regmap_write(regmap, QSERDES_PCS_LINE_RESET_TIME, 0x0C);
+ regmap_write(regmap, QSERDES_PCS_TX_LARGE_AMP_DRV_LVL, 0x1F);
+ regmap_write(regmap, QSERDES_PCS_TX_SMALL_AMP_DRV_LVL, 0x03);
+ regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL1, 0x83);
+ regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08);
+ regmap_write(regmap, QSERDES_PCS_SGMII_MISC_CTRL8, 0x8C);
+ regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x00);
+
+ regmap_write(regmap, QSERDES_PCS_PHY_START, 0x01);
+}
+
+static inline int
+qcom_dwmac_sgmii_phy_poll_status(struct regmap *regmap, unsigned int reg,
+ unsigned int bit)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout(regmap, reg, val,
+ val & bit, 1500, 750000);
+}
+
+static int qcom_dwmac_sgmii_phy_calibrate(struct phy *phy)
+{
+ struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy);
+ struct device *dev = phy->dev.parent;
+
+ switch (data->speed) {
+ case SPEED_10:
+ case SPEED_100:
+ case SPEED_1000:
+ qcom_dwmac_sgmii_phy_init_1g(data->regmap);
+ break;
+ case SPEED_2500:
+ qcom_dwmac_sgmii_phy_init_2p5g(data->regmap);
+ break;
+ }
+
+ if (qcom_dwmac_sgmii_phy_poll_status(data->regmap,
+ QSERDES_COM_C_READY_STATUS,
+ QSERDES_COM_C_READY)) {
+ dev_err(dev, "QSERDES_COM_C_READY_STATUS timed-out");
+ return -ETIMEDOUT;
+ }
+
+ if (qcom_dwmac_sgmii_phy_poll_status(data->regmap,
+ QSERDES_PCS_PCS_READY_STATUS,
+ QSERDES_PCS_READY)) {
+ dev_err(dev, "PCS_READY timed-out");
+ return -ETIMEDOUT;
+ }
+
+ if (qcom_dwmac_sgmii_phy_poll_status(data->regmap,
+ QSERDES_PCS_PCS_READY_STATUS,
+ QSERDES_PCS_SGMIIPHY_READY)) {
+ dev_err(dev, "SGMIIPHY_READY timed-out");
+ return -ETIMEDOUT;
+ }
+
+ if (qcom_dwmac_sgmii_phy_poll_status(data->regmap,
+ QSERDES_COM_CMN_STATUS,
+ QSERDES_COM_C_PLL_LOCKED)) {
+ dev_err(dev, "PLL Lock Status timed-out");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int qcom_dwmac_sgmii_phy_power_on(struct phy *phy)
+{
+ struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy);
+
+ return clk_prepare_enable(data->refclk);
+}
+
+static int qcom_dwmac_sgmii_phy_power_off(struct phy *phy)
+{
+ struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy);
+
+ regmap_write(data->regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08);
+ regmap_write(data->regmap, QSERDES_PCS_SW_RESET, 0x01);
+ udelay(100);
+ regmap_write(data->regmap, QSERDES_PCS_SW_RESET, 0x00);
+ regmap_write(data->regmap, QSERDES_PCS_PHY_START, 0x01);
+
+ clk_disable_unprepare(data->refclk);
+
+ return 0;
+}
+
+static int qcom_dwmac_sgmii_phy_set_speed(struct phy *phy, int speed)
+{
+ struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy);
+
+ if (speed != data->speed)
+ data->speed = speed;
+
+ return qcom_dwmac_sgmii_phy_calibrate(phy);
+}
+
+static const struct phy_ops qcom_dwmac_sgmii_phy_ops = {
+ .power_on = qcom_dwmac_sgmii_phy_power_on,
+ .power_off = qcom_dwmac_sgmii_phy_power_off,
+ .set_speed = qcom_dwmac_sgmii_phy_set_speed,
+ .calibrate = qcom_dwmac_sgmii_phy_calibrate,
+ .owner = THIS_MODULE,
+};
+
+static const struct regmap_config qcom_dwmac_sgmii_phy_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .use_relaxed_mmio = true,
+ .disable_locking = true,
+};
+
+static int qcom_dwmac_sgmii_phy_probe(struct platform_device *pdev)
+{
+ struct qcom_dwmac_sgmii_phy_data *data;
+ struct device *dev = &pdev->dev;
+ struct phy_provider *provider;
+ void __iomem *base;
+ struct phy *phy;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->speed = SPEED_10;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ data->regmap = devm_regmap_init_mmio(dev, base,
+ &qcom_dwmac_sgmii_phy_regmap_cfg);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ phy = devm_phy_create(dev, NULL, &qcom_dwmac_sgmii_phy_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ data->refclk = devm_clk_get(dev, "sgmi_ref");
+ if (IS_ERR(data->refclk))
+ return PTR_ERR(data->refclk);
+
+ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(provider))
+ return PTR_ERR(provider);
+
+ phy_set_drvdata(phy, data);
+
+ return 0;
+}
+
+static const struct of_device_id qcom_dwmac_sgmii_phy_of_match[] = {
+ { .compatible = "qcom,sa8775p-dwmac-sgmii-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, qcom_dwmac_sgmii_phy_of_match);
+
+static struct platform_driver qcom_dwmac_sgmii_phy_driver = {
+ .probe = qcom_dwmac_sgmii_phy_probe,
+ .driver = {
+ .name = "qcom-dwmac-sgmii-phy",
+ .of_match_table = qcom_dwmac_sgmii_phy_of_match,
+ }
+};
+
+module_platform_driver(qcom_dwmac_sgmii_phy_driver);
+
+MODULE_DESCRIPTION("Qualcomm DWMAC SGMII PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig
index 3ccaabf2850a..f10afa3d7ff5 100644
--- a/drivers/phy/samsung/Kconfig
+++ b/drivers/phy/samsung/Kconfig
@@ -59,7 +59,7 @@ config PHY_EXYNOS4210_USB2
config PHY_EXYNOS4X12_USB2
bool
depends on PHY_SAMSUNG_USB2
- default SOC_EXYNOS3250 || SOC_EXYNOS4412
+ default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412
config PHY_EXYNOS5250_USB2
bool
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
index b55d4e9f42b5..a296b87dced1 100644
--- a/drivers/phy/tegra/xusb.c
+++ b/drivers/phy/tegra/xusb.c
@@ -568,6 +568,7 @@ static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
usb_role_switch_unregister(port->usb_role_sw);
cancel_work_sync(&port->usb_phy_work);
usb_remove_phy(&port->usb_phy);
+ port->usb_phy.dev->driver = NULL;
}
if (port->ops->remove)
@@ -675,6 +676,9 @@ static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
port->dev.driver = devm_kzalloc(&port->dev,
sizeof(struct device_driver),
GFP_KERNEL);
+ if (!port->dev.driver)
+ return -ENOMEM;
+
port->dev.driver->owner = THIS_MODULE;
port->usb_role_sw = usb_role_switch_register(&port->dev,
diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c
index 8c667819c39a..6286cf25a426 100644
--- a/drivers/phy/ti/phy-gmii-sel.c
+++ b/drivers/phy/ti/phy-gmii-sel.c
@@ -23,7 +23,9 @@
#define AM33XX_GMII_SEL_MODE_RGMII 2
/* J72xx SoC specific definitions for the CONTROL port */
+#define J72XX_GMII_SEL_MODE_SGMII 3
#define J72XX_GMII_SEL_MODE_QSGMII 4
+#define J72XX_GMII_SEL_MODE_USXGMII 5
#define J72XX_GMII_SEL_MODE_QSGMII_SUB 6
#define PHY_GMII_PORT(n) BIT((n) - 1)
@@ -106,6 +108,20 @@ static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode)
gmii_sel_mode = J72XX_GMII_SEL_MODE_QSGMII_SUB;
break;
+ case PHY_INTERFACE_MODE_SGMII:
+ if (!(soc_data->extra_modes & BIT(PHY_INTERFACE_MODE_SGMII)))
+ goto unsupported;
+ else
+ gmii_sel_mode = J72XX_GMII_SEL_MODE_SGMII;
+ break;
+
+ case PHY_INTERFACE_MODE_USXGMII:
+ if (!(soc_data->extra_modes & BIT(PHY_INTERFACE_MODE_USXGMII)))
+ goto unsupported;
+ else
+ gmii_sel_mode = J72XX_GMII_SEL_MODE_USXGMII;
+ break;
+
default:
goto unsupported;
}
@@ -213,7 +229,7 @@ static const
struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw5g_soc_j7200 = {
.use_of_data = true,
.regfields = phy_gmii_sel_fields_am654,
- .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII),
+ .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | BIT(PHY_INTERFACE_MODE_SGMII),
.num_ports = 4,
.num_qsgmii_main_ports = 1,
};
@@ -222,7 +238,17 @@ static const
struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw9g_soc_j721e = {
.use_of_data = true,
.regfields = phy_gmii_sel_fields_am654,
- .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII),
+ .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | BIT(PHY_INTERFACE_MODE_SGMII),
+ .num_ports = 8,
+ .num_qsgmii_main_ports = 2,
+};
+
+static const
+struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw9g_soc_j784s4 = {
+ .use_of_data = true,
+ .regfields = phy_gmii_sel_fields_am654,
+ .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) |
+ BIT(PHY_INTERFACE_MODE_USXGMII),
.num_ports = 8,
.num_qsgmii_main_ports = 2,
};
@@ -256,6 +282,10 @@ static const struct of_device_id phy_gmii_sel_id_table[] = {
.compatible = "ti,j721e-cpsw9g-phy-gmii-sel",
.data = &phy_gmii_sel_cpsw9g_soc_j721e,
},
+ {
+ .compatible = "ti,j784s4-cpsw9g-phy-gmii-sel",
+ .data = &phy_gmii_sel_cpsw9g_soc_j784s4,
+ },
{}
};
MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table);