From 133552bf03edbe3892767a4b64c56e3bed746374 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Mon, 25 May 2020 21:08:58 +0800 Subject: [PATCH 01/46] phy: Remove CONFIG_ARCH_* check for related subdir in Makefile If CONFIG_ARCH_ROCKCHIP is not set but COMPILE_TEST is set, the file in the subdir rockchip can not be built due to CONFIG_ARCH_ROCKCHIP check in drivers/phy/Makefile. Since the related configs in drivers/phy/rockchip/Kconfig depend on ARCH_ROCKCHIP, so remove CONFIG_ARCH_ROCKCHIP check for subdir rockchip in drivers/phy/Makefile. The other CONFIG_ARCH_* about allwinner, amlogic, mediatek, renesas and tegra have the same situation, so remove them too. Signed-off-by: Tiezhu Yang Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/1590412138-13903-2-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Vinod Koul --- drivers/phy/Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 310c149a9df5..16e2622277d7 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -8,24 +8,24 @@ obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY) += phy-core-mipi-dphy.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o -obj-$(CONFIG_ARCH_SUNXI) += allwinner/ -obj-$(CONFIG_ARCH_MESON) += amlogic/ -obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ -obj-$(CONFIG_ARCH_RENESAS) += renesas/ -obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ -obj-$(CONFIG_ARCH_TEGRA) += tegra/ -obj-y += broadcom/ \ +obj-y += allwinner/ \ + amlogic/ \ + broadcom/ \ cadence/ \ freescale/ \ hisilicon/ \ intel/ \ lantiq/ \ marvell/ \ + mediatek/ \ motorola/ \ mscc/ \ qualcomm/ \ ralink/ \ + renesas/ \ + rockchip/ \ samsung/ \ socionext/ \ st/ \ + tegra/ \ ti/ From c233a2edf80de537d195f80fd46d92a7348d9a33 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 17 Jun 2020 17:28:03 +0200 Subject: [PATCH 02/46] phy: exynos: Rename Exynos to lowercase Fix up inconsistent usage of upper and lowercase letters in "Exynos" name. "EXYNOS" is not an abbreviation but a regular trademarked name. Therefore it should be written with lowercase letters starting with capital letter. The lowercase "Exynos" name is promoted by its manufacturer Samsung Electronics Co., Ltd., in advertisement materials and on website. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200617152803.17941-1-krzk@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/allwinner/phy-sun4i-usb.c | 2 +- drivers/phy/samsung/Kconfig | 8 ++++---- drivers/phy/samsung/phy-exynos-dp-video.c | 4 ++-- drivers/phy/samsung/phy-exynos-mipi-video.c | 4 ++-- drivers/phy/samsung/phy-exynos-pcie.c | 2 +- drivers/phy/samsung/phy-exynos5-usbdrd.c | 6 +++--- drivers/phy/samsung/phy-samsung-usb2.c | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 856927382248..7e09ad6a0b42 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -7,7 +7,7 @@ * Based on code from * Allwinner Technology Co., Ltd. * - * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver + * Modelled after: Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver * Copyright (C) 2013 Samsung Electronics Co., Ltd. * Author: Sylwester Nawrocki */ diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index 9e483d1fdaf2..19f2e3119343 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -3,23 +3,23 @@ # Phy drivers for Samsung platforms # config PHY_EXYNOS_DP_VIDEO - tristate "EXYNOS SoC series Display Port PHY driver" + tristate "Exynos SoC series Display Port PHY driver" depends on OF depends on ARCH_EXYNOS || COMPILE_TEST default ARCH_EXYNOS select GENERIC_PHY help - Support for Display Port PHY found on Samsung EXYNOS SoCs. + Support for Display Port PHY found on Samsung Exynos SoCs. config PHY_EXYNOS_MIPI_VIDEO - tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" + tristate "S5P/Exynos SoC series MIPI CSI-2/DSI PHY driver" depends on HAS_IOMEM depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select GENERIC_PHY default y if ARCH_S5PV210 || ARCH_EXYNOS help Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P - and EXYNOS SoCs. + and Exynos SoCs. config PHY_EXYNOS_PCIE bool "Exynos PCIe PHY driver" diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c index 6c607df1dc9a..2b670ef91deb 100644 --- a/drivers/phy/samsung/phy-exynos-dp-video.c +++ b/drivers/phy/samsung/phy-exynos-dp-video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS SoC series Display Port PHY driver + * Samsung Exynos SoC series Display Port PHY driver * * Copyright (C) 2013 Samsung Electronics Co., Ltd. * Author: Jingoo Han @@ -115,5 +115,5 @@ static struct platform_driver exynos_dp_video_phy_driver = { module_platform_driver(exynos_dp_video_phy_driver); MODULE_AUTHOR("Jingoo Han "); -MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); +MODULE_DESCRIPTION("Samsung Exynos SoC DP PHY driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c index bb51195f189f..c1df1ef3ee3c 100644 --- a/drivers/phy/samsung/phy-exynos-mipi-video.c +++ b/drivers/phy/samsung/phy-exynos-mipi-video.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver + * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver * * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. * Author: Sylwester Nawrocki @@ -364,6 +364,6 @@ static struct platform_driver exynos_mipi_video_phy_driver = { }; module_platform_driver(exynos_mipi_video_phy_driver); -MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver"); +MODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver"); MODULE_AUTHOR("Sylwester Nawrocki "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-exynos-pcie.c b/drivers/phy/samsung/phy-exynos-pcie.c index 659e7ae0a6cf..7e28b1aea0d1 100644 --- a/drivers/phy/samsung/phy-exynos-pcie.c +++ b/drivers/phy/samsung/phy-exynos-pcie.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS SoC series PCIe PHY driver + * Samsung Exynos SoC series PCIe PHY driver * * Phy provider for PCIe controller on Exynos SoC series * diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index e510732afb8b..eb06ce9f748f 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Samsung EXYNOS5 SoC series USB DRD PHY driver + * Samsung Exynos5 SoC series USB DRD PHY driver * * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series * @@ -33,7 +33,7 @@ #define EXYNOS5_FSEL_24MHZ 0x5 #define EXYNOS5_FSEL_50MHZ 0x7 -/* EXYNOS5: USB 3.0 DRD PHY registers */ +/* Exynos5: USB 3.0 DRD PHY registers */ #define EXYNOS5_DRD_LINKSYSTEM 0x04 #define LINKSYSTEM_FLADJ_MASK (0x3f << 1) @@ -958,7 +958,7 @@ static struct platform_driver exynos5_usb3drd_phy = { }; module_platform_driver(exynos5_usb3drd_phy); -MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver"); +MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver"); MODULE_AUTHOR("Vivek Gautam "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:exynos5_usb3drd_phy"); diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c index 090aa02e02de..a3ed3ff04690 100644 --- a/drivers/phy/samsung/phy-samsung-usb2.c +++ b/drivers/phy/samsung/phy-samsung-usb2.c @@ -255,7 +255,7 @@ static struct platform_driver samsung_usb2_phy_driver = { }; module_platform_driver(samsung_usb2_phy_driver); -MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver"); +MODULE_DESCRIPTION("Samsung S5P/Exynos SoC USB PHY driver"); MODULE_AUTHOR("Kamil Debski "); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:samsung-usb2-phy"); From 066571516bea4dd903ae077a00df802a19b71248 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 8 Jun 2020 19:41:15 +0530 Subject: [PATCH 03/46] dt-bindings: phy: qcom,qmp: Add ipq8074 usb dt bindings Add ipq8074 qmp phy device compatible for super speed usb support. Signed-off-by: Sivaprakash Murugesan Tested-by: Sricharan R Acked-by: Rob Herring Link: https://lore.kernel.org/r/1591625479-4483-2-git-send-email-sivaprak@codeaurora.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index f80f8896d527..e4cd4a1deae9 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -18,6 +18,7 @@ properties: compatible: enum: - qcom,ipq8074-qmp-pcie-phy + - qcom,ipq8074-qmp-usb3-phy - qcom,msm8996-qmp-pcie-phy - qcom,msm8996-qmp-ufs-phy - qcom,msm8996-qmp-usb3-phy @@ -161,6 +162,7 @@ allOf: compatible: contains: enum: + - qcom,ipq8074-qmp-usb3-phy - qcom,msm8996-qmp-usb3-phy - qcom,msm8998-qmp-pcie-phy - qcom,msm8998-qmp-usb3-phy From ba971c2ff59954c47de8c864adf9fc8f27863208 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 8 Jun 2020 19:41:16 +0530 Subject: [PATCH 04/46] dt-bindings: phy: qcom,qusb2: Add ipq8074 device compatible Add ipq8074 compatible in QUSB2 PHY for high speed USB support. Signed-off-by: Sivaprakash Murugesan Tested-by: Sricharan R Acked-by: Rob Herring Link: https://lore.kernel.org/r/1591625479-4483-3-git-send-email-sivaprak@codeaurora.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index b5a6195de7ff..9ba62dcb1e5d 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -18,6 +18,7 @@ properties: oneOf: - items: - enum: + - qcom,ipq8074-qusb2-phy - qcom,msm8996-qusb2-phy - qcom,msm8998-qusb2-phy - items: From 507156f5a99fa03c0dce8281ff3d26fbf473630c Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 8 Jun 2020 19:41:17 +0530 Subject: [PATCH 05/46] phy: qcom-qmp: Add USB QMP PHY support for IPQ8074 Add QMP USB PHY found in IPQ8074 Co-developed-by: Balaji Prakash J Signed-off-by: Balaji Prakash J Signed-off-by: Sivaprakash Murugesan Tested-by: Sricharan R Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/1591625479-4483-4-git-send-email-sivaprak@codeaurora.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 102 ++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index e91040af3394..544f140553d4 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -198,6 +198,81 @@ static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET, }; +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), + 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, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + 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, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), + 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, 0x31), + 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, 0xde), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl ipq8074_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, 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, 0x0), +}; + +static const struct qmp_phy_init_tbl ipq8074_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 msm8996_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), @@ -1593,6 +1668,30 @@ static const char * const qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", }; +static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = ipq8074_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq8074_usb3_serdes_tbl), + .tx_tbl = msm8996_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), + .rx_tbl = ipq8074_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq8074_usb3_rx_tbl), + .pcs_tbl = ipq8074_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq8074_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, +}; + static const struct qmp_phy_cfg msm8996_pciephy_cfg = { .type = PHY_TYPE_PCIE, .nlanes = 3, @@ -2699,6 +2798,9 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) static const struct of_device_id qcom_qmp_phy_of_match_table[] = { { + .compatible = "qcom,ipq8074-qmp-usb3-phy", + .data = &ipq8074_usb3phy_cfg, + }, { .compatible = "qcom,msm8996-qmp-pcie-phy", .data = &msm8996_pciephy_cfg, }, { From 1d99d491dc96f4690534c67fd7361e2e74dd4c70 Mon Sep 17 00:00:00 2001 From: Sivaprakash Murugesan Date: Mon, 8 Jun 2020 19:41:18 +0530 Subject: [PATCH 06/46] phy: qcom-qusb2: Add ipq8074 device compatible Add ipq8074 qusb2 device compatible for high speed usb support. Signed-off-by: Sivaprakash Murugesan Tested-by: Sricharan R Reviewed-by: Sricharan R Link: https://lore.kernel.org/r/1591625479-4483-5-git-send-email-sivaprak@codeaurora.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qusb2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 393011a05b48..557547dabfd5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -810,6 +810,9 @@ static const struct phy_ops qusb2_phy_gen_ops = { static const struct of_device_id qusb2_phy_of_match_table[] = { { + .compatible = "qcom,ipq8074-qusb2-phy", + .data = &msm8996_phy_cfg, + }, { .compatible = "qcom,msm8996-qusb2-phy", .data = &msm8996_phy_cfg, }, { From 5dcbc71126e1dc41e32810af3f24b4a318eba5b3 Mon Sep 17 00:00:00 2001 From: Jonathan Marek Date: Sat, 23 May 2020 22:14:13 -0400 Subject: [PATCH 07/46] phy: qcom-qmp: Allow different values for second lane The primary USB PHY on sm8250 sets some values differently for the second lane. This makes it possible to represent that. Signed-off-by: Jonathan Marek Tested-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200524021416.17049-2-jonathan@marek.ca Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 52 ++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 544f140553d4..058650b0f018 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -82,20 +82,34 @@ struct qmp_phy_init_tbl { * register part of layout ? * if yes, then offset gives index in the reg-layout */ - int in_layout; + bool in_layout; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; }; #define QMP_PHY_INIT_CFG(o, v) \ { \ .offset = o, \ .val = v, \ + .lane_mask = 0xff, \ } #define QMP_PHY_INIT_CFG_L(o, v) \ { \ .offset = o, \ .val = v, \ - .in_layout = 1, \ + .in_layout = true, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = l, \ } /* set of registers with offsets different per-PHY */ @@ -2085,10 +2099,11 @@ static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { .is_dual_lane_phy = true, }; -static void qcom_qmp_phy_configure(void __iomem *base, - const unsigned int *regs, - const struct qmp_phy_init_tbl tbl[], - int num) +static void qcom_qmp_phy_configure_lane(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num, + u8 lane_mask) { int i; const struct qmp_phy_init_tbl *t = tbl; @@ -2097,6 +2112,9 @@ static void qcom_qmp_phy_configure(void __iomem *base, return; for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + if (t->in_layout) writel(t->val, base + regs[t->offset]); else @@ -2104,6 +2122,14 @@ static void qcom_qmp_phy_configure(void __iomem *base, } } +static void qcom_qmp_phy_configure(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + qcom_qmp_phy_configure_lane(base, regs, tbl, num, 0xff); +} + static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) { struct qcom_qmp *qmp = qphy->qmp; @@ -2318,16 +2344,18 @@ static int qcom_qmp_phy_enable(struct phy *phy) } /* Tx, Rx, and PCS configurations */ - qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); + qcom_qmp_phy_configure_lane(tx, cfg->regs, + cfg->tx_tbl, cfg->tx_tbl_num, 1); /* Configuration for other LANE for USB-DP combo PHY */ if (cfg->is_dual_lane_phy) - qcom_qmp_phy_configure(qphy->tx2, cfg->regs, - cfg->tx_tbl, cfg->tx_tbl_num); + qcom_qmp_phy_configure_lane(qphy->tx2, cfg->regs, + cfg->tx_tbl, cfg->tx_tbl_num, 2); - qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); + qcom_qmp_phy_configure_lane(rx, cfg->regs, + cfg->rx_tbl, cfg->rx_tbl_num, 1); if (cfg->is_dual_lane_phy) - qcom_qmp_phy_configure(qphy->rx2, cfg->regs, - cfg->rx_tbl, cfg->rx_tbl_num); + qcom_qmp_phy_configure_lane(qphy->rx2, cfg->regs, + cfg->rx_tbl, cfg->rx_tbl_num, 2); qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); ret = reset_control_deassert(qmp->ufs_reset); From 7b675ba1d27854abde20c52c47f0957756e04304 Mon Sep 17 00:00:00 2001 From: Jonathan Marek Date: Sat, 23 May 2020 22:14:14 -0400 Subject: [PATCH 08/46] phy: qcom-qmp: Add QMP V4 USB3 UNIPHY Add support for the USB3 PHY used by the secondary usb controller on sm8150 Signed-off-by: Jonathan Marek Tested-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200524021416.17049-3-jonathan@marek.ca Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 150 ++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 5 + 2 files changed, 155 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 058650b0f018..753997db0317 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -199,6 +199,17 @@ static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = 0x44, [QPHY_PCS_STATUS] = 0x14, [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x308, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x314, +}; + +static const unsigned int qmp_v4_usb3_uniphy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x608, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x614, }; static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { @@ -1488,6 +1499,114 @@ static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), }; +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0x95), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x05), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xef), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_UNI_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_UNI_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), +}; + /* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { /* phy-type - PCIE/UFS/USB */ @@ -2099,6 +2218,34 @@ static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { .is_dual_lane_phy = true, }; +static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sm8150_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_tx_tbl), + .rx_tbl = sm8150_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8150_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3_uniphy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + static void qcom_qmp_phy_configure_lane(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -2876,6 +3023,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8150-qmp-usb3-phy", .data = &sm8150_usb3phy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-usb3-uni-phy", + .data = &sm8150_usb3_uniphy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 6d017a0c0c8d..f39f7a968228 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -363,6 +363,7 @@ /* Only for QMP V4 PHY - TX registers */ #define QSERDES_V4_TX_RES_CODE_LANE_TX 0x34 #define QSERDES_V4_TX_RES_CODE_LANE_RX 0x38 +#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX 0x3c #define QSERDES_V4_TX_LANE_MODE_1 0x84 #define QSERDES_V4_TX_RCV_DETECT_LVL_2 0x9c #define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8 @@ -709,6 +710,10 @@ #define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x354 #define QPHY_V4_PCS_USB3_TEST_CONTROL 0x358 +/* Only for QMP V4 PHY - UNI has 0x300 offset for PCS_USB3 regs */ +#define QPHY_V4_PCS_USB3_UNI_LFPS_DET_HIGH_COUNT_VAL 0x618 +#define QPHY_V4_PCS_USB3_UNI_RXEQTRAINING_DFE_TIME_S2 0x638 + /* Only for QMP V4 PHY - PCS_MISC registers */ #define QPHY_V4_PCS_MISC_TYPEC_CTRL 0x00 #define QPHY_V4_PCS_MISC_TYPEC_PWRDN_CTRL 0x04 From 90b65347cfc5c812b4c3fba4c5f55cbb2c19d286 Mon Sep 17 00:00:00 2001 From: Jonathan Marek Date: Sat, 23 May 2020 22:14:15 -0400 Subject: [PATCH 09/46] phy: qcom-qmp: Add QMP V4 USB3 PHY support for sm8250 Add both the DP and UNI PHY for primary/secondary usb controllers. The tables are very similar to sm8150 (serdes_tbl is identical), but there are some differences. Signed-off-by: Jonathan Marek Tested-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200524021416.17049-4-jonathan@marek.ca Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 206 ++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 2 + 2 files changed, 208 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 753997db0317..562053ce9455 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -1607,6 +1607,142 @@ static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), }; +static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_2, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x40), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_UNI_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_UNI_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), +}; + /* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { /* phy-type - PCIE/UFS/USB */ @@ -1775,6 +1911,11 @@ static const char * const qmp_v4_phy_clk_l[] = { "aux", "ref_clk_src", "ref", "com_aux", }; +/* the primary usb3 phy on sm8250 doesn't have a ref clock */ +static const char * const qmp_v4_sm8250_usbphy_clk_l[] = { + "aux", "ref_clk_src", "com_aux" +}; + static const char * const sdm845_ufs_phy_clk_l[] = { "ref", "ref_aux", }; @@ -2246,6 +2387,65 @@ static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = { .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, }; +static const struct qmp_phy_cfg sm8250_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8250_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl), + .rx_tbl = sm8250_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl), + .pcs_tbl = sm8250_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl), + .clk_list = qmp_v4_sm8250_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, + .is_dual_lane_phy = true, +}; + +static const struct qmp_phy_cfg sm8250_usb3_uniphy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = sm8150_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_uniphy_serdes_tbl), + .tx_tbl = sm8250_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_tx_tbl), + .rx_tbl = sm8250_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_rx_tbl), + .pcs_tbl = sm8250_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_uniphy_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3_uniphy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + static void qcom_qmp_phy_configure_lane(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -3026,6 +3226,12 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8150-qmp-usb3-uni-phy", .data = &sm8150_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-usb3-phy", + .data = &sm8250_usb3phy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-usb3-uni-phy", + .data = &sm8250_usb3_uniphy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index f39f7a968228..4277f592684b 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -364,7 +364,9 @@ #define QSERDES_V4_TX_RES_CODE_LANE_TX 0x34 #define QSERDES_V4_TX_RES_CODE_LANE_RX 0x38 #define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX 0x3c +#define QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX 0x40 #define QSERDES_V4_TX_LANE_MODE_1 0x84 +#define QSERDES_V4_TX_LANE_MODE_2 0x88 #define QSERDES_V4_TX_RCV_DETECT_LVL_2 0x9c #define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8 #define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0xdC From 0d75f508a9d56133a2834215959cde4605c804d1 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Thu, 25 Jun 2020 12:54:43 -0700 Subject: [PATCH 10/46] phy: qcom-snps: Add runtime suspend and resume handlers Allow for the PHY to be put into a powered down state when possible. Add the required suspend and resume callbacks, which will determine what resources can be turned off depending on the cable status. Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20200625195444.15130-2-wcheng@codeaurora.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index 4d74045271eb..152d8633f4ea 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -104,6 +104,63 @@ static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset, readl_relaxed(base + offset); } +static int qcom_snps_hsphy_suspend(struct qcom_snps_hsphy *hsphy) +{ + dev_dbg(&hsphy->phy->dev, "Suspend QCOM SNPS PHY\n"); + + if (hsphy->mode == PHY_MODE_USB_HOST) { + /* Enable auto-resume to meet remote wakeup timing */ + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL2, + USB2_AUTO_RESUME, + USB2_AUTO_RESUME); + usleep_range(500, 1000); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL2, + 0, USB2_AUTO_RESUME); + } + + clk_disable_unprepare(hsphy->cfg_ahb_clk); + return 0; +} + +static int qcom_snps_hsphy_resume(struct qcom_snps_hsphy *hsphy) +{ + int ret; + + dev_dbg(&hsphy->phy->dev, "Resume QCOM SNPS PHY, mode\n"); + + ret = clk_prepare_enable(hsphy->cfg_ahb_clk); + if (ret) { + dev_err(&hsphy->phy->dev, "failed to enable cfg ahb clock\n"); + return ret; + } + + return 0; +} + +static int __maybe_unused qcom_snps_hsphy_runtime_suspend(struct device *dev) +{ + struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev); + + if (!hsphy->phy_initialized) + return 0; + + qcom_snps_hsphy_suspend(hsphy); + return 0; +} + +static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev) +{ + struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev); + + if (!hsphy->phy_initialized) + return 0; + + qcom_snps_hsphy_resume(hsphy); + return 0; +} + static int qcom_snps_hsphy_init(struct phy *phy) { struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); @@ -212,6 +269,11 @@ static const struct of_device_id qcom_snps_hsphy_of_match_table[] = { }; MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table); +static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = { + SET_RUNTIME_PM_OPS(qcom_snps_hsphy_runtime_suspend, + qcom_snps_hsphy_runtime_resume, NULL) +}; + static int qcom_snps_hsphy_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -255,6 +317,14 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev) return ret; } + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + /* + * Prevent runtime pm from being ON by default. Users can enable + * it using power/control in sysfs. + */ + pm_runtime_forbid(dev); + generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops); if (IS_ERR(generic_phy)) { ret = PTR_ERR(generic_phy); @@ -269,6 +339,8 @@ static int qcom_snps_hsphy_probe(struct platform_device *pdev) phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); if (!IS_ERR(phy_provider)) dev_dbg(dev, "Registered Qcom-SNPS HS phy\n"); + else + pm_runtime_disable(dev); return PTR_ERR_OR_ZERO(phy_provider); } @@ -277,6 +349,7 @@ static struct platform_driver qcom_snps_hsphy_driver = { .probe = qcom_snps_hsphy_probe, .driver = { .name = "qcom-snps-hs-femto-v2-phy", + .pm = &qcom_snps_hsphy_pm_ops, .of_match_table = qcom_snps_hsphy_of_match_table, }, }; From dcbec046507615d7c4b5f6682dc11a1be9a2924c Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Thu, 25 Jun 2020 12:54:44 -0700 Subject: [PATCH 11/46] phy: qcom-snps: Add a set mode callback The set mode handler is used to keep track of the current role of the device. This is used for enabling certain resources within the PHY depending on if the device is behaving as a host or device. Signed-off-by: Wesley Cheng Link: https://lore.kernel.org/r/20200625195444.15130-3-wcheng@codeaurora.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index 152d8633f4ea..ae4bac024c7b 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -77,6 +77,7 @@ static const char * const qcom_snps_hsphy_vreg_names[] = { * @phy_reset: phy reset control * @vregs: regulator supplies bulk data * @phy_initialized: if PHY has been initialized correctly + * @mode: contains the current mode the PHY is in */ struct qcom_snps_hsphy { struct phy *phy; @@ -88,6 +89,7 @@ struct qcom_snps_hsphy { struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS]; bool phy_initialized; + enum phy_mode mode; }; static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset, @@ -161,6 +163,15 @@ static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev) return 0; } +static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode, + int submode) +{ + struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); + + hsphy->mode = mode; + return 0; +} + static int qcom_snps_hsphy_init(struct phy *phy) { struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); @@ -258,6 +269,7 @@ static int qcom_snps_hsphy_exit(struct phy *phy) static const struct phy_ops qcom_snps_hsphy_gen_ops = { .init = qcom_snps_hsphy_init, .exit = qcom_snps_hsphy_exit, + .set_mode = qcom_snps_hsphy_set_mode, .owner = THIS_MODULE, }; From cea0f76a483d1270ac6f6513964e3e75193dda48 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Mon, 29 Jun 2020 15:00:52 +0300 Subject: [PATCH 12/46] dt-bindings: phy: Add DT bindings for Xilinx ZynqMP PSGTR PHY Add DT bindings for the Xilinx ZynqMP PHY. ZynqMP SoCs have a High Speed Processing System Gigabit Transceiver which provides PHY capabilities to USB, SATA, PCIE, Display Port and Ehernet SGMII controllers. Signed-off-by: Anurag Kumar Vulisha Signed-off-by: Laurent Pinchart Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200629120054.29338-2-laurent.pinchart@ideasonboard.com Signed-off-by: Vinod Koul --- .../bindings/phy/xlnx,zynqmp-psgtr.yaml | 105 ++++++++++++++++++ include/dt-bindings/phy/phy.h | 1 + 2 files changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml diff --git a/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml b/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml new file mode 100644 index 000000000000..09e3cde7ebca --- /dev/null +++ b/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/xlnx,zynqmp-psgtr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx ZynqMP Gigabit Transceiver PHY Device Tree Bindings + +maintainers: + - Laurent Pinchart + +description: | + This binding describes the Xilinx ZynqMP Gigabit Transceiver (GTR) PHY. The + GTR provides four lanes and is used by USB, SATA, PCIE, Display port and + Ethernet SGMII controllers. + +properties: + "#phy-cells": + const: 4 + description: | + The cells contain the following arguments. + + - description: The GTR lane + minimum: 0 + maximum: 3 + - description: The PHY type + enum: + - PHY_TYPE_DP + - PHY_TYPE_PCIE + - PHY_TYPE_SATA + - PHY_TYPE_SGMII + - PHY_TYPE_USB + - description: The PHY instance + minimum: 0 + maximum: 1 # for DP, SATA or USB + maximum: 3 # for PCIE or SGMII + - description: The reference clock number + minimum: 0 + maximum: 3 + + compatible: + enum: + - xlnx,zynqmp-psgtr-v1.1 + - xlnx,zynqmp-psgtr + + clocks: + minItems: 1 + maxItems: 4 + description: | + Clock for each PS_MGTREFCLK[0-3] reference clock input. Unconnected + inputs shall not have an entry. + + clock-names: + minItems: 1 + maxItems: 4 + items: + pattern: "^ref[0-3]$" + + reg: + items: + - description: SERDES registers block + - description: SIOU registers block + + reg-names: + items: + - const: serdes + - const: siou + + xlnx,tx-termination-fix: + description: | + Include this for fixing functional issue with the TX termination + resistance in GT, which can be out of spec for the XCZU9EG silicon + version. + type: boolean + +required: + - "#phy-cells" + - compatible + - reg + - reg-names + +if: + properties: + compatible: + const: xlnx,zynqmp-psgtr-v1.1 + +then: + properties: + xlnx,tx-termination-fix: false + +additionalProperties: false + +examples: + - | + phy: phy@fd400000 { + compatible = "xlnx,zynqmp-psgtr-v1.1"; + reg = <0x0 0xfd400000 0x0 0x40000>, + <0x0 0xfd3d0000 0x0 0x1000>; + reg-names = "serdes", "siou"; + clocks = <&refclks 3>, <&refclks 2>, <&refclks 0>; + clock-names = "ref1", "ref2", "ref3"; + #phy-cells = <4>; + }; + +... diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h index 3727ef72138b..36e8c241cf48 100644 --- a/include/dt-bindings/phy/phy.h +++ b/include/dt-bindings/phy/phy.h @@ -18,5 +18,6 @@ #define PHY_TYPE_UFS 5 #define PHY_TYPE_DP 6 #define PHY_TYPE_XPCS 7 +#define PHY_TYPE_SGMII 8 #endif /* _DT_BINDINGS_PHY */ From 4a33bea003144e217d8a3ae666f171dfc2e97bd6 Mon Sep 17 00:00:00 2001 From: Anurag Kumar Vulisha Date: Mon, 29 Jun 2020 15:00:53 +0300 Subject: [PATCH 13/46] phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver Xilinx ZynqMP SoCs have a Gigabit Transceiver with four lanes. All the high speed peripherals such as USB, SATA, PCIE, Display Port and Ethernet SGMII can rely on any of the four GT lanes for PHY layer. This patch adds driver for that ZynqMP GT core. Signed-off-by: Anurag Kumar Vulisha Signed-off-by: Laurent Pinchart Link: https://lore.kernel.org/r/20200629120054.29338-3-laurent.pinchart@ideasonboard.com Signed-off-by: Vinod Koul --- MAINTAINERS | 9 + drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 3 +- drivers/phy/xilinx/Kconfig | 13 + drivers/phy/xilinx/Makefile | 3 + drivers/phy/xilinx/phy-zynqmp.c | 995 ++++++++++++++++++++++++++++++++ 6 files changed, 1023 insertions(+), 1 deletion(-) create mode 100644 drivers/phy/xilinx/Kconfig create mode 100644 drivers/phy/xilinx/Makefile create mode 100644 drivers/phy/xilinx/phy-zynqmp.c diff --git a/MAINTAINERS b/MAINTAINERS index 68f21d46614c..82a12abe24c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18852,6 +18852,15 @@ F: Documentation/devicetree/bindings/media/xilinx/ F: drivers/media/platform/xilinx/ F: include/uapi/linux/xilinx-v4l2-controls.h +XILINX ZYNQMP PSGTR PHY DRIVER +M: Anurag Kumar Vulisha +M: Laurent Pinchart +L: linux-kernel@vger.kernel.org +S: Supported +T: git https://github.com/Xilinx/linux-xlnx.git +F: Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml +F: drivers/phy/xilinx/phy-zynqmp.c + XILLYBUS DRIVER M: Eli Billauer L: linux-kernel@vger.kernel.org diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index b3ed94b98d9b..de9362c25c07 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -70,5 +70,6 @@ source "drivers/phy/st/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" source "drivers/phy/intel/Kconfig" +source "drivers/phy/xilinx/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 16e2622277d7..c27408e4daae 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -28,4 +28,5 @@ obj-y += allwinner/ \ socionext/ \ st/ \ tegra/ \ - ti/ + ti/ \ + xilinx/ diff --git a/drivers/phy/xilinx/Kconfig b/drivers/phy/xilinx/Kconfig new file mode 100644 index 000000000000..d8b0d46b2b4d --- /dev/null +++ b/drivers/phy/xilinx/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# +# PHY drivers for Xilinx platforms +# + +config PHY_XILINX_ZYNQMP + tristate "Xilinx ZynqMP PHY driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support ZynqMP High Speed Gigabit Transceiver + that is part of ZynqMP SoC. diff --git a/drivers/phy/xilinx/Makefile b/drivers/phy/xilinx/Makefile new file mode 100644 index 000000000000..3f1f6a2a9b45 --- /dev/null +++ b/drivers/phy/xilinx/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_PHY_XILINX_ZYNQMP) += phy-zynqmp.o diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c new file mode 100644 index 000000000000..8babee2ce9ec --- /dev/null +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -0,0 +1,995 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * phy-zynqmp.c - PHY driver for Xilinx ZynqMP GT. + * + * Copyright (C) 2018-2020 Xilinx Inc. + * + * Author: Anurag Kumar Vulisha + * Author: Subbaraya Sundeep + * Author: Laurent Pinchart + * + * This driver is tested for USB, SATA and Display Port currently. + * Other controllers PCIe and SGMII should also work but that is + * experimental as of now. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Lane Registers + */ + +/* TX De-emphasis parameters */ +#define L0_TX_ANA_TM_18 0x0048 +#define L0_TX_ANA_TM_118 0x01d8 +#define L0_TX_ANA_TM_118_FORCE_17_0 BIT(0) + +/* DN Resistor calibration code parameters */ +#define L0_TXPMA_ST_3 0x0b0c +#define L0_DN_CALIB_CODE 0x3f + +/* PMA control parameters */ +#define L0_TXPMD_TM_45 0x0cb4 +#define L0_TXPMD_TM_48 0x0cc0 +#define L0_TXPMD_TM_45_OVER_DP_MAIN BIT(0) +#define L0_TXPMD_TM_45_ENABLE_DP_MAIN BIT(1) +#define L0_TXPMD_TM_45_OVER_DP_POST1 BIT(2) +#define L0_TXPMD_TM_45_ENABLE_DP_POST1 BIT(3) +#define L0_TXPMD_TM_45_OVER_DP_POST2 BIT(4) +#define L0_TXPMD_TM_45_ENABLE_DP_POST2 BIT(5) + +/* PCS control parameters */ +#define L0_TM_DIG_6 0x106c +#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f +#define L0_TX_DIG_61 0x00f4 +#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f + +/* PLL Test Mode register parameters */ +#define L0_TM_PLL_DIG_37 0x2094 +#define L0_TM_COARSE_CODE_LIMIT 0x10 + +/* PLL SSC step size offsets */ +#define L0_PLL_SS_STEPS_0_LSB 0x2368 +#define L0_PLL_SS_STEPS_1_MSB 0x236c +#define L0_PLL_SS_STEP_SIZE_0_LSB 0x2370 +#define L0_PLL_SS_STEP_SIZE_1 0x2374 +#define L0_PLL_SS_STEP_SIZE_2 0x2378 +#define L0_PLL_SS_STEP_SIZE_3_MSB 0x237c +#define L0_PLL_STATUS_READ_1 0x23e4 + +/* SSC step size parameters */ +#define STEP_SIZE_0_MASK 0xff +#define STEP_SIZE_1_MASK 0xff +#define STEP_SIZE_2_MASK 0xff +#define STEP_SIZE_3_MASK 0x3 +#define STEP_SIZE_SHIFT 8 +#define FORCE_STEP_SIZE 0x10 +#define FORCE_STEPS 0x20 +#define STEPS_0_MASK 0xff +#define STEPS_1_MASK 0x07 + +/* Reference clock selection parameters */ +#define L0_Ln_REF_CLK_SEL(n) (0x2860 + (n) * 4) +#define L0_REF_CLK_SEL_MASK 0x8f + +/* Calibration digital logic parameters */ +#define L3_TM_CALIB_DIG19 0xec4c +#define L3_CALIB_DONE_STATUS 0xef14 +#define L3_TM_CALIB_DIG18 0xec48 +#define L3_TM_CALIB_DIG19_NSW 0x07 +#define L3_TM_CALIB_DIG18_NSW 0xe0 +#define L3_TM_OVERRIDE_NSW_CODE 0x20 +#define L3_CALIB_DONE 0x02 +#define L3_NSW_SHIFT 5 +#define L3_NSW_PIPE_SHIFT 4 +#define L3_NSW_CALIB_SHIFT 3 + +#define PHY_REG_OFFSET 0x4000 + +/* + * Global Registers + */ + +/* Refclk selection parameters */ +#define PLL_REF_SEL(n) (0x10000 + (n) * 4) +#define PLL_FREQ_MASK 0x1f +#define PLL_STATUS_LOCKED 0x10 + +/* Inter Connect Matrix parameters */ +#define ICM_CFG0 0x10010 +#define ICM_CFG1 0x10014 +#define ICM_CFG0_L0_MASK 0x07 +#define ICM_CFG0_L1_MASK 0x70 +#define ICM_CFG1_L2_MASK 0x07 +#define ICM_CFG2_L3_MASK 0x70 +#define ICM_CFG_SHIFT 4 + +/* Inter Connect Matrix allowed protocols */ +#define ICM_PROTOCOL_PD 0x0 +#define ICM_PROTOCOL_PCIE 0x1 +#define ICM_PROTOCOL_SATA 0x2 +#define ICM_PROTOCOL_USB 0x3 +#define ICM_PROTOCOL_DP 0x4 +#define ICM_PROTOCOL_SGMII 0x5 + +/* Test Mode common reset control parameters */ +#define TM_CMN_RST 0x10018 +#define TM_CMN_RST_EN 0x1 +#define TM_CMN_RST_SET 0x2 +#define TM_CMN_RST_MASK 0x3 + +/* Bus width parameters */ +#define TX_PROT_BUS_WIDTH 0x10040 +#define RX_PROT_BUS_WIDTH 0x10044 +#define PROT_BUS_WIDTH_10 0x0 +#define PROT_BUS_WIDTH_20 0x1 +#define PROT_BUS_WIDTH_40 0x2 +#define PROT_BUS_WIDTH_SHIFT 2 + +/* Number of GT lanes */ +#define NUM_LANES 4 + +/* SIOU SATA control register */ +#define SATA_CONTROL_OFFSET 0x0100 + +/* Total number of controllers */ +#define CONTROLLERS_PER_LANE 5 + +/* Protocol Type parameters */ +#define XPSGTR_TYPE_USB0 0 /* USB controller 0 */ +#define XPSGTR_TYPE_USB1 1 /* USB controller 1 */ +#define XPSGTR_TYPE_SATA_0 2 /* SATA controller lane 0 */ +#define XPSGTR_TYPE_SATA_1 3 /* SATA controller lane 1 */ +#define XPSGTR_TYPE_PCIE_0 4 /* PCIe controller lane 0 */ +#define XPSGTR_TYPE_PCIE_1 5 /* PCIe controller lane 1 */ +#define XPSGTR_TYPE_PCIE_2 6 /* PCIe controller lane 2 */ +#define XPSGTR_TYPE_PCIE_3 7 /* PCIe controller lane 3 */ +#define XPSGTR_TYPE_DP_0 8 /* Display Port controller lane 0 */ +#define XPSGTR_TYPE_DP_1 9 /* Display Port controller lane 1 */ +#define XPSGTR_TYPE_SGMII0 10 /* Ethernet SGMII controller 0 */ +#define XPSGTR_TYPE_SGMII1 11 /* Ethernet SGMII controller 1 */ +#define XPSGTR_TYPE_SGMII2 12 /* Ethernet SGMII controller 2 */ +#define XPSGTR_TYPE_SGMII3 13 /* Ethernet SGMII controller 3 */ + +/* Timeout values */ +#define TIMEOUT_US 1000 + +struct xpsgtr_dev; + +/** + * struct xpsgtr_ssc - structure to hold SSC settings for a lane + * @refclk_rate: PLL reference clock frequency + * @pll_ref_clk: value to be written to register for corresponding ref clk rate + * @steps: number of steps of SSC (Spread Spectrum Clock) + * @step_size: step size of each step + */ +struct xpsgtr_ssc { + u32 refclk_rate; + u8 pll_ref_clk; + u32 steps; + u32 step_size; +}; + +/** + * struct xpsgtr_phy - representation of a lane + * @phy: pointer to the kernel PHY device + * @type: controller which uses this lane + * @lane: lane number + * @protocol: protocol in which the lane operates + * @skip_phy_init: skip phy_init() if true + * @dev: pointer to the xpsgtr_dev instance + * @refclk: reference clock index + */ +struct xpsgtr_phy { + struct phy *phy; + u8 type; + u8 lane; + u8 protocol; + bool skip_phy_init; + struct xpsgtr_dev *dev; + unsigned int refclk; +}; + +/** + * struct xpsgtr_dev - representation of a ZynMP GT device + * @dev: pointer to device + * @serdes: serdes base address + * @siou: siou base address + * @gtr_mutex: mutex for locking + * @phys: PHY lanes + * @refclk_sscs: spread spectrum settings for the reference clocks + * @tx_term_fix: fix for GT issue + * @saved_icm_cfg0: stored value of ICM CFG0 register + * @saved_icm_cfg1: stored value of ICM CFG1 register + */ +struct xpsgtr_dev { + struct device *dev; + void __iomem *serdes; + void __iomem *siou; + struct mutex gtr_mutex; /* mutex for locking */ + struct xpsgtr_phy phys[NUM_LANES]; + const struct xpsgtr_ssc *refclk_sscs[NUM_LANES]; + bool tx_term_fix; + unsigned int saved_icm_cfg0; + unsigned int saved_icm_cfg1; +}; + +/* + * Configuration Data + */ + +/* lookup table to hold all settings needed for a ref clock frequency */ +static const struct xpsgtr_ssc ssc_lookup[] = { + { 19200000, 0x05, 608, 264020 }, + { 20000000, 0x06, 634, 243454 }, + { 24000000, 0x07, 760, 168973 }, + { 26000000, 0x08, 824, 143860 }, + { 27000000, 0x09, 856, 86551 }, + { 38400000, 0x0a, 1218, 65896 }, + { 40000000, 0x0b, 634, 243454 }, + { 52000000, 0x0c, 824, 143860 }, + { 100000000, 0x0d, 1058, 87533 }, + { 108000000, 0x0e, 856, 86551 }, + { 125000000, 0x0f, 992, 119497 }, + { 135000000, 0x10, 1070, 55393 }, + { 150000000, 0x11, 792, 187091 } +}; + +/* + * I/O Accessors + */ + +static inline u32 xpsgtr_read(struct xpsgtr_dev *gtr_dev, u32 reg) +{ + return readl(gtr_dev->serdes + reg); +} + +static inline void xpsgtr_write(struct xpsgtr_dev *gtr_dev, u32 reg, u32 value) +{ + writel(value, gtr_dev->serdes + reg); +} + +static inline void xpsgtr_clr_set(struct xpsgtr_dev *gtr_dev, u32 reg, + u32 clr, u32 set) +{ + u32 value = xpsgtr_read(gtr_dev, reg); + + value &= ~clr; + value |= set; + xpsgtr_write(gtr_dev, reg, value); +} + +static inline u32 xpsgtr_read_phy(struct xpsgtr_phy *gtr_phy, u32 reg) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + return readl(addr); +} + +static inline void xpsgtr_write_phy(struct xpsgtr_phy *gtr_phy, + u32 reg, u32 value) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + writel(value, addr); +} + +static inline void xpsgtr_clr_set_phy(struct xpsgtr_phy *gtr_phy, + u32 reg, u32 clr, u32 set) +{ + void __iomem *addr = gtr_phy->dev->serdes + + gtr_phy->lane * PHY_REG_OFFSET + reg; + + writel((readl(addr) & ~clr) | set, addr); +} + +/* + * Hardware Configuration + */ + +/* Wait for the PLL to lock (with a timeout). */ +static int xpsgtr_wait_pll_lock(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + unsigned int timeout = TIMEOUT_US; + int ret; + + dev_dbg(gtr_dev->dev, "Waiting for PLL lock\n"); + + while (1) { + u32 reg = xpsgtr_read_phy(gtr_phy, L0_PLL_STATUS_READ_1); + + if ((reg & PLL_STATUS_LOCKED) == PLL_STATUS_LOCKED) { + ret = 0; + break; + } + + if (--timeout == 0) { + ret = -ETIMEDOUT; + break; + } + + udelay(1); + } + + if (ret == -ETIMEDOUT) + dev_err(gtr_dev->dev, + "lane %u (type %u, protocol %u): PLL lock timeout\n", + gtr_phy->lane, gtr_phy->type, gtr_phy->protocol); + + return ret; +} + +/* Configure PLL and spread-sprectrum clock. */ +static void xpsgtr_configure_pll(struct xpsgtr_phy *gtr_phy) +{ + const struct xpsgtr_ssc *ssc; + u32 step_size; + + ssc = gtr_phy->dev->refclk_sscs[gtr_phy->refclk]; + step_size = ssc->step_size; + + xpsgtr_clr_set(gtr_phy->dev, PLL_REF_SEL(gtr_phy->lane), + PLL_FREQ_MASK, ssc->pll_ref_clk); + + /* Enable lane clock sharing, if required */ + if (gtr_phy->refclk != gtr_phy->lane) { + /* Lane3 Ref Clock Selection Register */ + xpsgtr_clr_set(gtr_phy->dev, L0_Ln_REF_CLK_SEL(gtr_phy->lane), + L0_REF_CLK_SEL_MASK, 1 << gtr_phy->refclk); + } + + /* SSC step size [7:0] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_0_LSB, + STEP_SIZE_0_MASK, step_size & STEP_SIZE_0_MASK); + + /* SSC step size [15:8] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_1, + STEP_SIZE_1_MASK, step_size & STEP_SIZE_1_MASK); + + /* SSC step size [23:16] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_2, + STEP_SIZE_2_MASK, step_size & STEP_SIZE_2_MASK); + + /* SSC steps [7:0] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_0_LSB, + STEPS_0_MASK, ssc->steps & STEPS_0_MASK); + + /* SSC steps [10:8] */ + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEPS_1_MSB, + STEPS_1_MASK, + (ssc->steps >> STEP_SIZE_SHIFT) & STEPS_1_MASK); + + /* SSC step size [24:25] */ + step_size >>= STEP_SIZE_SHIFT; + xpsgtr_clr_set_phy(gtr_phy, L0_PLL_SS_STEP_SIZE_3_MSB, + STEP_SIZE_3_MASK, (step_size & STEP_SIZE_3_MASK) | + FORCE_STEP_SIZE | FORCE_STEPS); +} + +/* Configure the lane protocol. */ +static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + u8 protocol = gtr_phy->protocol; + + switch (gtr_phy->lane) { + case 0: + xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L0_MASK, protocol); + break; + case 1: + xpsgtr_clr_set(gtr_dev, ICM_CFG0, ICM_CFG0_L1_MASK, + protocol << ICM_CFG_SHIFT); + break; + case 2: + xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L0_MASK, protocol); + break; + case 3: + xpsgtr_clr_set(gtr_dev, ICM_CFG1, ICM_CFG0_L1_MASK, + protocol << ICM_CFG_SHIFT); + break; + default: + /* We already checked 0 <= lane <= 3 */ + break; + } +} + +/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ +static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) +{ + xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER); +} + +/* DP-specific initialization. */ +static void xpsgtr_phy_init_dp(struct xpsgtr_phy *gtr_phy) +{ + xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_45, + L0_TXPMD_TM_45_OVER_DP_MAIN | + L0_TXPMD_TM_45_ENABLE_DP_MAIN | + L0_TXPMD_TM_45_OVER_DP_POST1 | + L0_TXPMD_TM_45_OVER_DP_POST2 | + L0_TXPMD_TM_45_ENABLE_DP_POST2); + xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_118, + L0_TX_ANA_TM_118_FORCE_17_0); +} + +/* SATA-specific initialization. */ +static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + + xpsgtr_bypass_scrambler_8b10b(gtr_phy); + + writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET); +} + +/* SGMII-specific initialization. */ +static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + + /* Set SGMII protocol TX and RX bus width to 10 bits. */ + xpsgtr_write(gtr_dev, TX_PROT_BUS_WIDTH, + PROT_BUS_WIDTH_10 << (gtr_phy->lane * PROT_BUS_WIDTH_SHIFT)); + xpsgtr_write(gtr_dev, RX_PROT_BUS_WIDTH, + PROT_BUS_WIDTH_10 << (gtr_phy->lane * PROT_BUS_WIDTH_SHIFT)); + + xpsgtr_bypass_scrambler_8b10b(gtr_phy); +} + +/* Configure TX de-emphasis and margining for DP. */ +static void xpsgtr_phy_configure_dp(struct xpsgtr_phy *gtr_phy, unsigned int pre, + unsigned int voltage) +{ + static const u8 voltage_swing[4][4] = { + { 0x2a, 0x27, 0x24, 0x20 }, + { 0x27, 0x23, 0x20, 0xff }, + { 0x24, 0x20, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0xff } + }; + static const u8 pre_emphasis[4][4] = { + { 0x02, 0x02, 0x02, 0x02 }, + { 0x01, 0x01, 0x01, 0xff }, + { 0x00, 0x00, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0xff } + }; + + xpsgtr_write_phy(gtr_phy, L0_TXPMD_TM_48, voltage_swing[pre][voltage]); + xpsgtr_write_phy(gtr_phy, L0_TX_ANA_TM_18, pre_emphasis[pre][voltage]); +} + +/* + * PHY Operations + */ + +static bool xpsgtr_phy_init_required(struct xpsgtr_phy *gtr_phy) +{ + /* + * As USB may save the snapshot of the states during hibernation, doing + * phy_init() will put the USB controller into reset, resulting in the + * losing of the saved snapshot. So try to avoid phy_init() for USB + * except when gtr_phy->skip_phy_init is false (this happens when FPD is + * shutdown during suspend or when gt lane is changed from current one) + */ + if (gtr_phy->protocol == ICM_PROTOCOL_USB && gtr_phy->skip_phy_init) + return false; + else + return true; +} + +/* + * There is a functional issue in the GT. The TX termination resistance can be + * out of spec due to a issue in the calibration logic. This is the workaround + * to fix it, required for XCZU9EG silicon. + */ +static int xpsgtr_phy_tx_term_fix(struct xpsgtr_phy *gtr_phy) +{ + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + u32 timeout = TIMEOUT_US; + u32 nsw; + + /* Enabling Test Mode control for CMN Rest */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + /* Set Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN); + + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, 0x00); + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, L3_TM_OVERRIDE_NSW_CODE); + + /* + * As a part of work around sequence for PMOS calibration fix, + * we need to configure any lane ICM_CFG to valid protocol. This + * will deassert the CMN_Resetn signal. + */ + xpsgtr_lane_set_protocol(gtr_phy); + + /* Clear Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + dev_dbg(gtr_dev->dev, "calibrating...\n"); + + do { + u32 reg = xpsgtr_read(gtr_dev, L3_CALIB_DONE_STATUS); + + if ((reg & L3_CALIB_DONE) == L3_CALIB_DONE) + break; + + if (!--timeout) { + dev_err(gtr_dev->dev, "calibration time out\n"); + return -ETIMEDOUT; + } + + udelay(1); + } while (timeout > 0); + + dev_dbg(gtr_dev->dev, "calibration done\n"); + + /* Reading NMOS Register Code */ + nsw = xpsgtr_read(gtr_dev, L0_TXPMA_ST_3) & L0_DN_CALIB_CODE; + + /* Set Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_EN); + + /* Writing NMOS register values back [5:3] */ + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG19, nsw >> L3_NSW_CALIB_SHIFT); + + /* Writing NMOS register value [2:0] */ + xpsgtr_write(gtr_dev, L3_TM_CALIB_DIG18, + ((nsw & L3_TM_CALIB_DIG19_NSW) << L3_NSW_SHIFT) | + (1 << L3_NSW_PIPE_SHIFT)); + + /* Clear Test Mode reset */ + xpsgtr_clr_set(gtr_dev, TM_CMN_RST, TM_CMN_RST_MASK, TM_CMN_RST_SET); + + return 0; +} + +static int xpsgtr_phy_init(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; + int ret = 0; + + mutex_lock(>r_dev->gtr_mutex); + + /* Skip initialization if not required. */ + if (!xpsgtr_phy_init_required(gtr_phy)) + goto out; + + if (gtr_dev->tx_term_fix) { + ret = xpsgtr_phy_tx_term_fix(gtr_phy); + if (ret < 0) + goto out; + + gtr_dev->tx_term_fix = false; + } + + /* Enable coarse code saturation limiting logic. */ + xpsgtr_write_phy(gtr_phy, L0_TM_PLL_DIG_37, L0_TM_COARSE_CODE_LIMIT); + + /* + * Configure the PLL, the lane protocol, and perform protocol-specific + * initialization. + */ + xpsgtr_configure_pll(gtr_phy); + xpsgtr_lane_set_protocol(gtr_phy); + + switch (gtr_phy->protocol) { + case ICM_PROTOCOL_DP: + xpsgtr_phy_init_dp(gtr_phy); + break; + + case ICM_PROTOCOL_SATA: + xpsgtr_phy_init_sata(gtr_phy); + break; + + case ICM_PROTOCOL_SGMII: + xpsgtr_phy_init_sgmii(gtr_phy); + break; + } + +out: + mutex_unlock(>r_dev->gtr_mutex); + return ret; +} + +static int xpsgtr_phy_exit(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + + gtr_phy->skip_phy_init = false; + + return 0; +} + +static int xpsgtr_phy_power_on(struct phy *phy) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + int ret = 0; + + /* + * Wait for the PLL to lock. For DP, only wait on DP0 to avoid + * cumulating waits for both lanes. The user is expected to initialize + * lane 0 last. + */ + if (gtr_phy->protocol != ICM_PROTOCOL_DP || + gtr_phy->type == XPSGTR_TYPE_DP_0) + ret = xpsgtr_wait_pll_lock(phy); + + return ret; +} + +static int xpsgtr_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + + if (gtr_phy->protocol != ICM_PROTOCOL_DP) + return 0; + + xpsgtr_phy_configure_dp(gtr_phy, opts->dp.pre[0], opts->dp.voltage[0]); + + return 0; +} + +static const struct phy_ops xpsgtr_phyops = { + .init = xpsgtr_phy_init, + .exit = xpsgtr_phy_exit, + .power_on = xpsgtr_phy_power_on, + .configure = xpsgtr_phy_configure, + .owner = THIS_MODULE, +}; + +/* + * OF Xlate Support + */ + +/* Set the lane type and protocol based on the PHY type and instance number. */ +static int xpsgtr_set_lane_type(struct xpsgtr_phy *gtr_phy, u8 phy_type, + unsigned int phy_instance) +{ + unsigned int num_phy_types; + const int *phy_types; + + switch (phy_type) { + case PHY_TYPE_SATA: { + static const int types[] = { + XPSGTR_TYPE_SATA_0, + XPSGTR_TYPE_SATA_1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_SATA; + break; + } + case PHY_TYPE_USB3: { + static const int types[] = { + XPSGTR_TYPE_USB0, + XPSGTR_TYPE_USB1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_USB; + break; + } + case PHY_TYPE_DP: { + static const int types[] = { + XPSGTR_TYPE_DP_0, + XPSGTR_TYPE_DP_1, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_DP; + break; + } + case PHY_TYPE_PCIE: { + static const int types[] = { + XPSGTR_TYPE_PCIE_0, + XPSGTR_TYPE_PCIE_1, + XPSGTR_TYPE_PCIE_2, + XPSGTR_TYPE_PCIE_3, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_PCIE; + break; + } + case PHY_TYPE_SGMII: { + static const int types[] = { + XPSGTR_TYPE_SGMII0, + XPSGTR_TYPE_SGMII1, + XPSGTR_TYPE_SGMII2, + XPSGTR_TYPE_SGMII3, + }; + + phy_types = types; + num_phy_types = ARRAY_SIZE(types); + gtr_phy->protocol = ICM_PROTOCOL_SGMII; + break; + } + default: + return -EINVAL; + } + + if (phy_instance >= num_phy_types) + return -EINVAL; + + gtr_phy->type = phy_types[phy_instance]; + return 0; +} + +/* + * Valid combinations of controllers and lanes (Interconnect Matrix). + */ +static const unsigned int icm_matrix[NUM_LANES][CONTROLLERS_PER_LANE] = { + { XPSGTR_TYPE_PCIE_0, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII0 }, + { XPSGTR_TYPE_PCIE_1, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII1 }, + { XPSGTR_TYPE_PCIE_2, XPSGTR_TYPE_SATA_0, XPSGTR_TYPE_USB0, + XPSGTR_TYPE_DP_1, XPSGTR_TYPE_SGMII2 }, + { XPSGTR_TYPE_PCIE_3, XPSGTR_TYPE_SATA_1, XPSGTR_TYPE_USB1, + XPSGTR_TYPE_DP_0, XPSGTR_TYPE_SGMII3 } +}; + +/* Translate OF phandle and args to PHY instance. */ +static struct phy *xpsgtr_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + struct xpsgtr_phy *gtr_phy; + unsigned int phy_instance; + unsigned int phy_lane; + unsigned int phy_type; + unsigned int refclk; + unsigned int i; + int ret; + + if (args->args_count != 4) { + dev_err(dev, "Invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + /* + * Get the PHY parameters from the OF arguments and derive the lane + * type. + */ + phy_lane = args->args[0]; + if (phy_lane >= ARRAY_SIZE(gtr_dev->phys)) { + dev_err(dev, "Invalid lane number %u\n", phy_lane); + return ERR_PTR(-ENODEV); + } + + gtr_phy = >r_dev->phys[phy_lane]; + phy_type = args->args[1]; + phy_instance = args->args[2]; + + ret = xpsgtr_set_lane_type(gtr_phy, phy_type, phy_instance); + if (ret < 0) { + dev_err(gtr_dev->dev, "Invalid PHY type and/or instance\n"); + return ERR_PTR(ret); + } + + refclk = args->args[3]; + if (refclk >= ARRAY_SIZE(gtr_dev->refclk_sscs) || + !gtr_dev->refclk_sscs[refclk]) { + dev_err(dev, "Invalid reference clock number %u\n", refclk); + return ERR_PTR(-EINVAL); + } + + gtr_phy->refclk = refclk; + + /* + * Ensure that the Interconnect Matrix is obeyed, i.e a given lane type + * is allowed to operate on the lane. + */ + for (i = 0; i < CONTROLLERS_PER_LANE; i++) { + if (icm_matrix[phy_lane][i] == gtr_phy->type) + return gtr_phy->phy; + } + + return ERR_PTR(-EINVAL); +} + +/* + * Power Management + */ + +#ifdef CONFIG_PM +static int xpsgtr_suspend(struct device *dev) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + + /* Save the snapshot ICM_CFG registers. */ + gtr_dev->saved_icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); + gtr_dev->saved_icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); + + return 0; +} + +static int xpsgtr_resume(struct device *dev) +{ + struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); + unsigned int icm_cfg0, icm_cfg1; + unsigned int i; + bool skip_phy_init; + + icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); + icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); + + /* Return if no GT lanes got configured before suspend. */ + if (!gtr_dev->saved_icm_cfg0 && !gtr_dev->saved_icm_cfg1) + return 0; + + /* Check if the ICM configurations changed after suspend. */ + if (icm_cfg0 == gtr_dev->saved_icm_cfg0 && + icm_cfg1 == gtr_dev->saved_icm_cfg1) + skip_phy_init = true; + else + skip_phy_init = false; + + /* Update the skip_phy_init for all gtr_phy instances. */ + for (i = 0; i < ARRAY_SIZE(gtr_dev->phys); i++) + gtr_dev->phys[i].skip_phy_init = skip_phy_init; + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops xpsgtr_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume) +}; + +/* + * Probe & Platform Driver + */ + +static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) +{ + unsigned int refclk; + + for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { + unsigned long rate; + unsigned int i; + struct clk *clk; + char name[8]; + + snprintf(name, sizeof(name), "ref%u", refclk); + clk = devm_clk_get_optional(gtr_dev->dev, name); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(gtr_dev->dev, + "Failed to get reference clock %u: %ld\n", + refclk, PTR_ERR(clk)); + return PTR_ERR(clk); + } + + if (!clk) + continue; + + /* + * Get the spread spectrum (SSC) settings for the reference + * clock rate. + */ + rate = clk_get_rate(clk); + + for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { + if (rate == ssc_lookup[i].refclk_rate) { + gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; + break; + } + } + + if (i == ARRAY_SIZE(ssc_lookup)) { + dev_err(gtr_dev->dev, + "Invalid rate %lu for reference clock %u\n", + rate, refclk); + return -EINVAL; + } + } + + return 0; +} + +static int xpsgtr_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct xpsgtr_dev *gtr_dev; + struct phy_provider *provider; + unsigned int port; + int ret; + + gtr_dev = devm_kzalloc(&pdev->dev, sizeof(*gtr_dev), GFP_KERNEL); + if (!gtr_dev) + return -ENOMEM; + + gtr_dev->dev = &pdev->dev; + platform_set_drvdata(pdev, gtr_dev); + + mutex_init(>r_dev->gtr_mutex); + + if (of_device_is_compatible(np, "xlnx,zynqmp-psgtr")) + gtr_dev->tx_term_fix = + of_property_read_bool(np, "xlnx,tx-termination-fix"); + + /* Acquire resources. */ + gtr_dev->serdes = devm_platform_ioremap_resource_byname(pdev, "serdes"); + if (IS_ERR(gtr_dev->serdes)) + return PTR_ERR(gtr_dev->serdes); + + gtr_dev->siou = devm_platform_ioremap_resource_byname(pdev, "siou"); + if (IS_ERR(gtr_dev->siou)) + return PTR_ERR(gtr_dev->siou); + + ret = xpsgtr_get_ref_clocks(gtr_dev); + if (ret) + return ret; + + /* Create PHYs. */ + for (port = 0; port < ARRAY_SIZE(gtr_dev->phys); ++port) { + struct xpsgtr_phy *gtr_phy = >r_dev->phys[port]; + struct phy *phy; + + gtr_phy->lane = port; + gtr_phy->dev = gtr_dev; + + phy = devm_phy_create(&pdev->dev, np, &xpsgtr_phyops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + gtr_phy->phy = phy; + phy_set_drvdata(phy, gtr_phy); + } + + /* Register the PHY provider. */ + provider = devm_of_phy_provider_register(&pdev->dev, xpsgtr_xlate); + if (IS_ERR(provider)) { + dev_err(&pdev->dev, "registering provider failed\n"); + return PTR_ERR(provider); + } + return 0; +} + +static const struct of_device_id xpsgtr_of_match[] = { + { .compatible = "xlnx,zynqmp-psgtr", }, + { .compatible = "xlnx,zynqmp-psgtr-v1.1", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xpsgtr_of_match); + +static struct platform_driver xpsgtr_driver = { + .probe = xpsgtr_probe, + .driver = { + .name = "xilinx-psgtr", + .of_match_table = xpsgtr_of_match, + .pm = &xpsgtr_pm_ops, + }, +}; + +module_platform_driver(xpsgtr_driver); + +MODULE_AUTHOR("Xilinx Inc."); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Xilinx ZynqMP High speed Gigabit Transceiver"); From 574ba3667891b432f92478a02f686bb779a1e593 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 1 Jul 2020 16:48:53 +0300 Subject: [PATCH 14/46] dt-bindings: phy: zynqmp-psgtr: Fix example's numbers of cells in reg The DT examples are by default compiled in a parent that has #address-cells and #size-cells both set to 1. Fix the example accordingly, even if it doesn't match the actual hardware, as this is the recommended practice for DT bindings examples. Fixes: cea0f76a483d ("dt-bindings: phy: Add DT bindings for Xilinx ZynqMP PSGTR PHY") Signed-off-by: Laurent Pinchart Link: https://lore.kernel.org/r/20200701134853.30656-1-laurent.pinchart@ideasonboard.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml b/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml index 09e3cde7ebca..04d5654efb38 100644 --- a/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml +++ b/Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml @@ -94,8 +94,8 @@ examples: - | phy: phy@fd400000 { compatible = "xlnx,zynqmp-psgtr-v1.1"; - reg = <0x0 0xfd400000 0x0 0x40000>, - <0x0 0xfd3d0000 0x0 0x1000>; + reg = <0xfd400000 0x40000>, + <0xfd3d0000 0x1000>; reg-names = "serdes", "siou"; clocks = <&refclks 3>, <&refclks 2>, <&refclks 0>; clock-names = "ref1", "ref2", "ref3"; From 1c59c2d95d3160a2af4a8655d19f56648b2dffa3 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 1 Jul 2020 16:10:17 +0200 Subject: [PATCH 15/46] phy: zynqmp: Fix unused-function compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark xpsgtr_suspend and xpsgtr_resume as __maybe_unused to fix the following compiler warning when building with !CONFIG_PM_SLEEP: drivers/phy/xilinx/phy-zynqmp.c:830:12: warning: ‘xpsgtr_resume’ defined but not used [-Wunused-function] 830 | static int xpsgtr_resume(struct device *dev) | ^~~~~~~~~~~~~ drivers/phy/xilinx/phy-zynqmp.c:819:12: warning: ‘xpsgtr_suspend’ defined but not used [-Wunused-function] 819 | static int xpsgtr_suspend(struct device *dev) | ^~~~~~~~~~~~~~ Also drop the existing #ifdef CONFIG_PM so the functions are always compile-checked regardless of CONFIG_PM and/or CONFIG_PM_SLEEP being set. Reviewed-by: Laurent Pinchart Signed-off-by: Tobias Klauser Link: https://lore.kernel.org/r/20200701141017.26931-1-tklauser@distanz.ch Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 8babee2ce9ec..2b0f921b6ee3 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -815,8 +815,7 @@ static struct phy *xpsgtr_xlate(struct device *dev, * Power Management */ -#ifdef CONFIG_PM -static int xpsgtr_suspend(struct device *dev) +static int __maybe_unused xpsgtr_suspend(struct device *dev) { struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); @@ -827,7 +826,7 @@ static int xpsgtr_suspend(struct device *dev) return 0; } -static int xpsgtr_resume(struct device *dev) +static int __maybe_unused xpsgtr_resume(struct device *dev) { struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); unsigned int icm_cfg0, icm_cfg1; @@ -854,7 +853,6 @@ static int xpsgtr_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ static const struct dev_pm_ops xpsgtr_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume) From 02dca8c981b552ad4caaa2e858800c78cc20f41c Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:24:52 +0530 Subject: [PATCH 16/46] phy: qcom: remove ufs qmp phy driver The UFS specific QMP PHY driver started off supporting the 14nm and 20nm hardware. With the 20nm support marked broken for a long time and the 14nm support added to the common QMP PHY, this driver has not been used in a while. So delete it Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200629145452.123035-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/Kconfig | 24 - drivers/phy/qualcomm/Makefile | 3 - drivers/phy/qualcomm/phy-qcom-ufs-i.h | 131 ---- drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 172 ----- drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h | 168 ----- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 226 ------- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h | 226 ------- drivers/phy/qualcomm/phy-qcom-ufs.c | 648 ------------------- 8 files changed, 1598 deletions(-) delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-i.h delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h delete mode 100644 drivers/phy/qualcomm/phy-qcom-ufs.c diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index ca9ce7e84a5c..e6f0e514fb1f 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -59,30 +59,6 @@ config PHY_QCOM_QUSB2 PHY which is usually paired with either the ChipIdea or Synopsys DWC3 USB IPs on MSM SOCs. -config PHY_QCOM_UFS - tristate "Qualcomm UFS PHY driver" - depends on OF && ARCH_QCOM - select GENERIC_PHY - help - Support for UFS PHY on QCOM chipsets. - -if PHY_QCOM_UFS - -config PHY_QCOM_UFS_14NM - tristate - default PHY_QCOM_UFS - help - Support for 14nm UFS QMP phy present on QCOM chipsets. - -config PHY_QCOM_UFS_20NM - tristate - default PHY_QCOM_UFS - depends on BROKEN - help - Support for 20nm UFS QMP phy present on QCOM chipsets. - -endif - config PHY_QCOM_USB_HS tristate "Qualcomm USB HS PHY module" depends on USB_ULPI_BUS diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index 86fb32efab79..a767915decd0 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -6,9 +6,6 @@ obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o -obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o -obj-$(CONFIG_PHY_QCOM_UFS_14NM) += phy-qcom-ufs-qmp-14nm.o -obj-$(CONFIG_PHY_QCOM_UFS_20NM) += phy-qcom-ufs-qmp-20nm.o obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h deleted file mode 100644 index 9bf973a0d8c3..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ /dev/null @@ -1,131 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_I_H_ -#define UFS_QCOM_PHY_I_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ - { \ - .reg_offset = reg, \ - .cfg_value = val, \ - } - -#define UFS_QCOM_PHY_NAME_LEN 30 - -enum { - MASK_SERDES_START = 0x1, - MASK_PCS_READY = 0x1, -}; - -enum { - OFFSET_SERDES_START = 0x0, -}; - -struct ufs_qcom_phy_stored_attributes { - u32 att; - u32 value; -}; - - -struct ufs_qcom_phy_calibration { - u32 reg_offset; - u32 cfg_value; -}; - -struct ufs_qcom_phy_vreg { - const char *name; - struct regulator *reg; - int max_uA; - int min_uV; - int max_uV; - bool enabled; -}; - -struct ufs_qcom_phy { - struct list_head list; - struct device *dev; - void __iomem *mmio; - void __iomem *dev_ref_clk_ctrl_mmio; - struct clk *tx_iface_clk; - struct clk *rx_iface_clk; - bool is_iface_clk_enabled; - struct clk *ref_clk_src; - struct clk *ref_clk_parent; - struct clk *ref_clk; - bool is_ref_clk_enabled; - bool is_dev_ref_clk_enabled; - struct ufs_qcom_phy_vreg vdda_pll; - struct ufs_qcom_phy_vreg vdda_phy; - struct ufs_qcom_phy_vreg vddp_ref_clk; - unsigned int quirks; - - /* - * If UFS link is put into Hibern8 and if UFS PHY analog hardware is - * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8 - * exit might fail even after powering on UFS PHY analog hardware. - * Enabling this quirk will help to solve above issue by doing - * custom PHY settings just before PHY analog power collapse. - */ - #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0) - - u8 host_ctrl_rev_major; - u16 host_ctrl_rev_minor; - u16 host_ctrl_rev_step; - - char name[UFS_QCOM_PHY_NAME_LEN]; - struct ufs_qcom_phy_calibration *cached_regs; - int cached_regs_table_size; - struct ufs_qcom_phy_specific_ops *phy_spec_ops; - - enum phy_mode mode; - struct reset_control *ufs_reset; -}; - -/** - * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a - * specific implementation per phy. Each UFS phy, should implement - * those functions according to its spec and requirements - * @start_serdes: pointer to a function that starts the serdes - * @is_physical_coding_sublayer_ready: pointer to a function that - * checks pcs readiness. returns 0 for success and non-zero for error. - * @set_tx_lane_enable: pointer to a function that enable tx lanes - * @power_control: pointer to a function that controls analog rail of phy - * and writes to QSERDES_RX_SIGDET_CNTRL attribute - */ -struct ufs_qcom_phy_specific_ops { - int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B); - void (*start_serdes)(struct ufs_qcom_phy *phy); - int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); - void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); - void (*power_control)(struct ufs_qcom_phy *phy, bool val); -}; - -struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy); -int ufs_qcom_phy_power_on(struct phy *generic_phy); -int ufs_qcom_phy_power_off(struct phy *generic_phy); -int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common); -int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common); -int ufs_qcom_phy_remove(struct phy *generic_phy, - struct ufs_qcom_phy *ufs_qcom_phy); -struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, - struct ufs_qcom_phy *common_cfg, - const struct phy_ops *ufs_qcom_phy_gen_ops, - struct ufs_qcom_phy_specific_ops *phy_spec_ops); -int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, - struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, - bool is_rate_B); -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c deleted file mode 100644 index 54b355bfc24c..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-qmp-14nm.h" - -#define UFS_PHY_NAME "ufs_phy_qmp_14nm" -#define UFS_PHY_VDDA_PHY_UV (925000) - -static -int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) -{ - int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); - int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); - int err; - - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, - tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); - - if (err) - dev_err(ufs_qcom_phy->dev, - "%s: ufs_qcom_phy_calibrate() failed %d\n", - __func__, err); - return err; -} - -static -void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) -{ - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; -} - -static -int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, - enum phy_mode mode, int submode) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - - phy_common->mode = PHY_MODE_INVALID; - - if (mode > 0) - phy_common->mode = mode; - - return 0; -} - -static -void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) -{ - writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON (or OFF). - */ - mb(); -} - -static inline -void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) -{ - /* - * 14nm PHY does not have TX_LANE_ENABLE register. - * Implement this function so as not to propagate error to caller. - */ -} - -static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) -{ - u32 tmp; - - tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); - tmp &= ~MASK_SERDES_START; - tmp |= (1 << OFFSET_SERDES_START); - writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); - /* Ensure register value is committed */ - mb(); -} - -static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) -{ - int err = 0; - u32 val; - - err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, - val, (val & MASK_PCS_READY), 10, 1000000); - if (err) - dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", - __func__, err); - return err; -} - -static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { - .power_on = ufs_qcom_phy_power_on, - .power_off = ufs_qcom_phy_power_off, - .set_mode = ufs_qcom_phy_qmp_14nm_set_mode, - .owner = THIS_MODULE, -}; - -static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { - .calibrate = ufs_qcom_phy_qmp_14nm_phy_calibrate, - .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, - .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, - .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, - .power_control = ufs_qcom_phy_qmp_14nm_power_control, -}; - -static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct ufs_qcom_phy_qmp_14nm *phy; - struct ufs_qcom_phy *phy_common; - int err = 0; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - err = -ENOMEM; - goto out; - } - phy_common = &phy->common_cfg; - - generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, - &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops); - - if (!generic_phy) { - err = -EIO; - goto out; - } - - err = ufs_qcom_phy_init_clks(phy_common); - if (err) - goto out; - - err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) - goto out; - - phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; - phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; - - ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); - - phy_set_drvdata(generic_phy, phy); - - strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); - -out: - return err; -} - -static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = { - {.compatible = "qcom,ufs-phy-qmp-14nm"}, - {.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"}, - {}, -}; -MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match); - -static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = { - .probe = ufs_qcom_phy_qmp_14nm_probe, - .driver = { - .of_match_table = ufs_qcom_phy_qmp_14nm_of_match, - .name = "ufs_qcom_phy_qmp_14nm", - }, -}; - -module_platform_driver(ufs_qcom_phy_qmp_14nm_driver); - -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h deleted file mode 100644 index ceca654b9338..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h +++ /dev/null @@ -1,168 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_QMP_14NM_H_ -#define UFS_QCOM_PHY_QMP_14NM_H_ - -#include "phy-qcom-ufs-i.h" - -/* QCOM UFS PHY control registers */ -#define COM_OFF(x) (0x000 + x) -#define PHY_OFF(x) (0xC00 + x) -#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) -#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) - -/* UFS PHY QSERDES COM registers */ -#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) -#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) -#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C) -#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50) -#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54) -#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58) -#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C) -#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60) -#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) -#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) -#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) -#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88) -#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90) -#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) -#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) -#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) -#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) -#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) -#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) -#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4) -#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC) -#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0) -#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4) -#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8) -#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC) -#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0) -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108) -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C) -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110) -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114) -#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124) -#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128) -#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C) -#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) -#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) -#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) -#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) -#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) -#define QSERDES_COM_CLK_SELECT COM_OFF(0x174) -#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) -#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) -#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) -#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) -#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) -#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) - -/* UFS PHY registers */ -#define UFS_PHY_PHY_START PHY_OFF(0x00) -#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) -#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) - -/* UFS PHY TX registers */ -#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68) -#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94) - -/* UFS PHY RX registers */ -#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40) -#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90) -#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4) -#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8) -#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC) -#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0) -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8) -#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114) -#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118) -#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C) -#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C) - -/* - * This structure represents the 14nm specific phy. - * common_cfg MUST remain the first field in this structure - * in case extra fields are added. This way, when calling - * get_ufs_qcom_phy() of generic phy, we can extract the - * common phy structure (struct ufs_qcom_phy) out of it - * regardless of the relevant specific phy. - */ -struct ufs_qcom_phy_qmp_14nm { - struct ufs_qcom_phy common_cfg; -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), - - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), - - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), -}; - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c deleted file mode 100644 index 3e9d8b71e995..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-qmp-20nm.h" - -#define UFS_PHY_NAME "ufs_phy_qmp_20nm" - -static -int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) -{ - struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; - int tbl_size_A, tbl_size_B; - u8 major = ufs_qcom_phy->host_ctrl_rev_major; - u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; - u16 step = ufs_qcom_phy->host_ctrl_rev_step; - int err; - - if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) { - tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0); - tbl_A = phy_cal_table_rate_A_1_2_0; - } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) { - tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); - tbl_A = phy_cal_table_rate_A_1_3_0; - } else { - dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n", - __func__); - err = -ENODEV; - goto out; - } - - tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); - tbl_B = phy_cal_table_rate_B; - - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, - tbl_B, tbl_size_B, is_rate_B); - - if (err) - dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", - __func__, err); - -out: - return err; -} - -static -void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) -{ - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; -} - -static -int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, - enum phy_mode mode, int submode) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - - phy_common->mode = PHY_MODE_INVALID; - - if (mode > 0) - phy_common->mode = mode; - - return 0; -} - -static -void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val) -{ - bool hibern8_exit_after_pwr_collapse = phy->quirks & - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; - - if (val) { - writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON. - */ - mb(); - - if (hibern8_exit_after_pwr_collapse) { - /* - * Give atleast 1us delay after restoring PHY analog - * power. - */ - usleep_range(1, 2); - writel_relaxed(0x0A, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - writel_relaxed(0x08, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - /* - * Make sure workaround is deactivated before proceeding - * with normal PHY operations. - */ - mb(); - } - } else { - if (hibern8_exit_after_pwr_collapse) { - writel_relaxed(0x0A, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - writel_relaxed(0x02, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - /* - * Make sure that above workaround is activated before - * PHY analog power collapse. - */ - mb(); - } - - writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * ensure that PHY knows its PHY analog rail is going - * to be powered down - */ - mb(); - } -} - -static -void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) -{ - writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK, - phy->mmio + UFS_PHY_TX_LANE_ENABLE); - mb(); -} - -static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy) -{ - u32 tmp; - - tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); - tmp &= ~MASK_SERDES_START; - tmp |= (1 << OFFSET_SERDES_START); - writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); - mb(); -} - -static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) -{ - int err = 0; - u32 val; - - err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, - val, (val & MASK_PCS_READY), 10, 1000000); - if (err) - dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", - __func__, err); - return err; -} - -static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { - .power_on = ufs_qcom_phy_power_on, - .power_off = ufs_qcom_phy_power_off, - .set_mode = ufs_qcom_phy_qmp_20nm_set_mode, - .owner = THIS_MODULE, -}; - -static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { - .calibrate = ufs_qcom_phy_qmp_20nm_phy_calibrate, - .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, - .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, - .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, - .power_control = ufs_qcom_phy_qmp_20nm_power_control, -}; - -static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct ufs_qcom_phy_qmp_20nm *phy; - struct ufs_qcom_phy *phy_common; - int err = 0; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - err = -ENOMEM; - goto out; - } - phy_common = &phy->common_cfg; - - generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, - &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops); - - if (!generic_phy) { - err = -EIO; - goto out; - } - - err = ufs_qcom_phy_init_clks(phy_common); - if (err) - goto out; - - err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) - goto out; - - ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common); - - phy_set_drvdata(generic_phy, phy); - - strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); - -out: - return err; -} - -static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = { - {.compatible = "qcom,ufs-phy-qmp-20nm"}, - {}, -}; -MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match); - -static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = { - .probe = ufs_qcom_phy_qmp_20nm_probe, - .driver = { - .of_match_table = ufs_qcom_phy_qmp_20nm_of_match, - .name = "ufs_qcom_phy_qmp_20nm", - }, -}; - -module_platform_driver(ufs_qcom_phy_qmp_20nm_driver); - -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h deleted file mode 100644 index 8ce79f524eed..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h +++ /dev/null @@ -1,226 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#ifndef UFS_QCOM_PHY_QMP_20NM_H_ -#define UFS_QCOM_PHY_QMP_20NM_H_ - -#include "phy-qcom-ufs-i.h" - -/* QCOM UFS PHY control registers */ - -#define COM_OFF(x) (0x000 + x) -#define PHY_OFF(x) (0xC00 + x) -#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) -#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) - -/* UFS PHY PLL block registers */ -#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0) -#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04) -#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14) -#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24) -#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28) -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30) -#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34) -#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38) -#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C) -#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48) -#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C) -#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50) -#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90) -#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94) -#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98) -#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C) -#define QSERDES_COM_BGTC COM_OFF(0xA0) -#define QSERDES_COM_DEC_START1 COM_OFF(0xAC) -#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0) -#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8) -#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC) -#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100) -#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104) -#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108) -#define QSERDES_COM_DEC_START2 COM_OFF(0x10C) -#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110) -#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114) -#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118) - -/* TX LANE n (0, 1) registers */ -#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08) -#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C) -#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54) - -/* RX LANE n (0, 1) registers */ -#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0) -#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8) -#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8) -#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC) -#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0) -#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4) -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC) -#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC) -#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100) - -/* UFS PHY registers */ -#define UFS_PHY_PHY_START PHY_OFF(0x00) -#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4) -#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44) -#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08) -#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C) -#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10) -#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14) -#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34) -#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38) -#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C) -#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40) -#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68) -#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28) -#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C) -#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48) -#define UFS_PHY_PLL_CNTL PHY_OFF(0x50) -#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54) -#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C) -#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58) -#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60) -#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64) -#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C) -#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4) -#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0) -#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8) -#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4) -#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC) -#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8) -#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC) -#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100) -#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c) -#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160) -#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7) -#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6) -#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5) -#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4) -#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3) -#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2) -#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1) -#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0) -#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164) -#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168) -#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C) -#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170) -#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174) - -#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3 - -/* - * This structure represents the 20nm specific phy. - * common_cfg MUST remain the first field in this structure - * in case extra fields are added. This way, when calling - * get_ufs_qcom_phy() of generic phy, we can extract the - * common phy structure (struct ufs_qcom_phy) out of it - * regardless of the relevant specific phy. - */ -struct ufs_qcom_phy_qmp_20nm { - struct ufs_qcom_phy common_cfg; -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e), -}; - -#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c deleted file mode 100644 index 763c8d396af1..000000000000 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ /dev/null @@ -1,648 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - */ - -#include "phy-qcom-ufs-i.h" - -#define MAX_PROP_NAME 32 -#define VDDA_PHY_MIN_UV 1000000 -#define VDDA_PHY_MAX_UV 1000000 -#define VDDA_PLL_MIN_UV 1800000 -#define VDDA_PLL_MAX_UV 1800000 -#define VDDP_REF_CLK_MIN_UV 1200000 -#define VDDP_REF_CLK_MAX_UV 1200000 - -int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - struct ufs_qcom_phy_calibration *tbl_A, - int tbl_size_A, - struct ufs_qcom_phy_calibration *tbl_B, - int tbl_size_B, bool is_rate_B) -{ - int i; - int ret = 0; - - if (!tbl_A) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); - ret = EINVAL; - goto out; - } - - for (i = 0; i < tbl_size_A; i++) - writel_relaxed(tbl_A[i].cfg_value, - ufs_qcom_phy->mmio + tbl_A[i].reg_offset); - - /* - * In case we would like to work in rate B, we need - * to override a registers that were configured in rate A table - * with registers of rate B table. - * table. - */ - if (is_rate_B) { - if (!tbl_B) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", - __func__); - ret = EINVAL; - goto out; - } - - for (i = 0; i < tbl_size_B; i++) - writel_relaxed(tbl_B[i].cfg_value, - ufs_qcom_phy->mmio + tbl_B[i].reg_offset); - } - - /* flush buffered writes */ - mb(); - -out: - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); - -/* - * This assumes the embedded phy structure inside generic_phy is of type - * struct ufs_qcom_phy. In order to function properly it's crucial - * to keep the embedded struct "struct ufs_qcom_phy common_cfg" - * as the first inside generic_phy. - */ -struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy) -{ - return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy); -} -EXPORT_SYMBOL_GPL(get_ufs_qcom_phy); - -static -int ufs_qcom_phy_base_init(struct platform_device *pdev, - struct ufs_qcom_phy *phy_common) -{ - struct device *dev = &pdev->dev; - struct resource *res; - int err = 0; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); - phy_common->mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->mmio)) { - err = PTR_ERR((void const *)phy_common->mmio); - phy_common->mmio = NULL; - dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", - __func__, err); - return err; - } - - /* "dev_ref_clk_ctrl_mem" is optional resource */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "dev_ref_clk_ctrl_mem"); - phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) - phy_common->dev_ref_clk_ctrl_mmio = NULL; - - return 0; -} - -struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, - struct ufs_qcom_phy *common_cfg, - const struct phy_ops *ufs_qcom_phy_gen_ops, - struct ufs_qcom_phy_specific_ops *phy_spec_ops) -{ - int err; - struct device *dev = &pdev->dev; - struct phy *generic_phy = NULL; - struct phy_provider *phy_provider; - - err = ufs_qcom_phy_base_init(pdev, common_cfg); - if (err) { - dev_err(dev, "%s: phy base init failed %d\n", __func__, err); - goto out; - } - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - err = PTR_ERR(phy_provider); - dev_err(dev, "%s: failed to register phy %d\n", __func__, err); - goto out; - } - - generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops); - if (IS_ERR(generic_phy)) { - err = PTR_ERR(generic_phy); - dev_err(dev, "%s: failed to create phy %d\n", __func__, err); - generic_phy = NULL; - goto out; - } - - common_cfg->phy_spec_ops = phy_spec_ops; - common_cfg->dev = dev; - -out: - return generic_phy; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); - -static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common) -{ - struct reset_control *reset; - - if (phy_common->ufs_reset) - return 0; - - reset = devm_reset_control_get_exclusive_by_index(phy_common->dev, 0); - if (IS_ERR(reset)) - return PTR_ERR(reset); - - phy_common->ufs_reset = reset; - return 0; -} - -static int __ufs_qcom_phy_clk_get(struct device *dev, - const char *name, struct clk **clk_out, bool err_print) -{ - struct clk *clk; - int err = 0; - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - err = PTR_ERR(clk); - if (err_print) - dev_err(dev, "failed to get %s err %d", name, err); - } else { - *clk_out = clk; - } - - return err; -} - -static int ufs_qcom_phy_clk_get(struct device *dev, - const char *name, struct clk **clk_out) -{ - return __ufs_qcom_phy_clk_get(dev, name, clk_out, true); -} - -int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) -{ - int err; - - if (of_device_is_compatible(phy_common->dev->of_node, - "qcom,msm8996-ufs-phy-qmp-14nm")) - goto skip_txrx_clk; - - err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk", - &phy_common->tx_iface_clk); - if (err) - goto out; - - err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk", - &phy_common->rx_iface_clk); - if (err) - goto out; - -skip_txrx_clk: - err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", - &phy_common->ref_clk_src); - if (err) - goto out; - - /* - * "ref_clk_parent" is optional hence don't abort init if it's not - * found. - */ - __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent", - &phy_common->ref_clk_parent, false); - - err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk", - &phy_common->ref_clk); - -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); - -static int ufs_qcom_phy_init_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, - const char *name) -{ - int err = 0; - - char prop_name[MAX_PROP_NAME]; - - vreg->name = name; - vreg->reg = devm_regulator_get(dev, name); - if (IS_ERR(vreg->reg)) { - err = PTR_ERR(vreg->reg); - dev_err(dev, "failed to get %s, %d\n", name, err); - goto out; - } - - if (dev->of_node) { - snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); - err = of_property_read_u32(dev->of_node, - prop_name, &vreg->max_uA); - if (err && err != -EINVAL) { - dev_err(dev, "%s: failed to read %s\n", - __func__, prop_name); - goto out; - } else if (err == -EINVAL || !vreg->max_uA) { - if (regulator_count_voltages(vreg->reg) > 0) { - dev_err(dev, "%s: %s is mandatory\n", - __func__, prop_name); - goto out; - } - err = 0; - } - } - - if (!strcmp(name, "vdda-pll")) { - vreg->max_uV = VDDA_PLL_MAX_UV; - vreg->min_uV = VDDA_PLL_MIN_UV; - } else if (!strcmp(name, "vdda-phy")) { - vreg->max_uV = VDDA_PHY_MAX_UV; - vreg->min_uV = VDDA_PHY_MIN_UV; - } else if (!strcmp(name, "vddp-ref-clk")) { - vreg->max_uV = VDDP_REF_CLK_MAX_UV; - vreg->min_uV = VDDP_REF_CLK_MIN_UV; - } - -out: - return err; -} - -int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) -{ - int err; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll, - "vdda-pll"); - if (err) - goto out; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy, - "vdda-phy"); - - if (err) - goto out; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, - "vddp-ref-clk"); - -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators); - -static int ufs_qcom_phy_cfg_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, bool on) -{ - int ret = 0; - struct regulator *reg = vreg->reg; - const char *name = vreg->name; - int min_uV; - int uA_load; - - if (regulator_count_voltages(reg) > 0) { - min_uV = on ? vreg->min_uV : 0; - ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); - if (ret) { - dev_err(dev, "%s: %s set voltage failed, err=%d\n", - __func__, name, ret); - goto out; - } - uA_load = on ? vreg->max_uA : 0; - ret = regulator_set_load(reg, uA_load); - if (ret >= 0) { - /* - * regulator_set_load() returns new regulator - * mode upon success. - */ - ret = 0; - } else { - dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", - __func__, name, uA_load, ret); - goto out; - } - } -out: - return ret; -} - -static int ufs_qcom_phy_enable_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg) -{ - int ret = 0; - - if (!vreg || vreg->enabled) - goto out; - - ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true); - if (ret) { - dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n", - __func__, ret); - goto out; - } - - ret = regulator_enable(vreg->reg); - if (ret) { - dev_err(dev, "%s: enable failed, err=%d\n", - __func__, ret); - goto out; - } - - vreg->enabled = true; -out: - return ret; -} - -static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy) -{ - int ret = 0; - - if (phy->is_ref_clk_enabled) - goto out; - - /* - * reference clock is propagated in a daisy-chained manner from - * source to phy, so ungate them at each stage. - */ - ret = clk_prepare_enable(phy->ref_clk_src); - if (ret) { - dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", - __func__, ret); - goto out; - } - - /* - * "ref_clk_parent" is optional clock hence make sure that clk reference - * is available before trying to enable the clock. - */ - if (phy->ref_clk_parent) { - ret = clk_prepare_enable(phy->ref_clk_parent); - if (ret) { - dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", - __func__, ret); - goto out_disable_src; - } - } - - ret = clk_prepare_enable(phy->ref_clk); - if (ret) { - dev_err(phy->dev, "%s: ref_clk enable failed %d\n", - __func__, ret); - goto out_disable_parent; - } - - phy->is_ref_clk_enabled = true; - goto out; - -out_disable_parent: - if (phy->ref_clk_parent) - clk_disable_unprepare(phy->ref_clk_parent); -out_disable_src: - clk_disable_unprepare(phy->ref_clk_src); -out: - return ret; -} - -static int ufs_qcom_phy_disable_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg) -{ - int ret = 0; - - if (!vreg || !vreg->enabled) - goto out; - - ret = regulator_disable(vreg->reg); - - if (!ret) { - /* ignore errors on applying disable config */ - ufs_qcom_phy_cfg_vreg(dev, vreg, false); - vreg->enabled = false; - } else { - dev_err(dev, "%s: %s disable failed, err=%d\n", - __func__, vreg->name, ret); - } -out: - return ret; -} - -static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy) -{ - if (phy->is_ref_clk_enabled) { - clk_disable_unprepare(phy->ref_clk); - /* - * "ref_clk_parent" is optional clock hence make sure that clk - * reference is available before trying to disable the clock. - */ - if (phy->ref_clk_parent) - clk_disable_unprepare(phy->ref_clk_parent); - clk_disable_unprepare(phy->ref_clk_src); - phy->is_ref_clk_enabled = false; - } -} - -/* Turn ON M-PHY RMMI interface clocks */ -static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy) -{ - int ret = 0; - - if (phy->is_iface_clk_enabled) - goto out; - - ret = clk_prepare_enable(phy->tx_iface_clk); - if (ret) { - dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", - __func__, ret); - goto out; - } - ret = clk_prepare_enable(phy->rx_iface_clk); - if (ret) { - clk_disable_unprepare(phy->tx_iface_clk); - dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n", - __func__, ret); - goto out; - } - phy->is_iface_clk_enabled = true; - -out: - return ret; -} - -/* Turn OFF M-PHY RMMI interface clocks */ -static void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) -{ - if (phy->is_iface_clk_enabled) { - clk_disable_unprepare(phy->tx_iface_clk); - clk_disable_unprepare(phy->rx_iface_clk); - phy->is_iface_clk_enabled = false; - } -} - -static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy) -{ - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { - dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy); - } - - return ret; -} - -int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { - dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, - tx_lanes); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); - -void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, - u8 major, u16 minor, u16 step) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - - ufs_qcom_phy->host_ctrl_rev_major = major; - ufs_qcom_phy->host_ctrl_rev_minor = minor; - ufs_qcom_phy->host_ctrl_rev_step = step; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); - -static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy) -{ - if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { - dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", - __func__); - return -ENOTSUPP; - } - - return ufs_qcom_phy->phy_spec_ops-> - is_physical_coding_sublayer_ready(ufs_qcom_phy); -} - -int ufs_qcom_phy_power_on(struct phy *generic_phy) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - struct device *dev = phy_common->dev; - bool is_rate_B = false; - int err; - - err = ufs_qcom_phy_get_reset(phy_common); - if (err) - return err; - - err = reset_control_assert(phy_common->ufs_reset); - if (err) - return err; - - if (phy_common->mode == PHY_MODE_UFS_HS_B) - is_rate_B = true; - - err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B); - if (err) - return err; - - err = reset_control_deassert(phy_common->ufs_reset); - if (err) { - dev_err(dev, "Failed to assert UFS PHY reset"); - return err; - } - - err = ufs_qcom_phy_start_serdes(phy_common); - if (err) - return err; - - err = ufs_qcom_phy_is_pcs_ready(phy_common); - if (err) - return err; - - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); - if (err) { - dev_err(dev, "%s enable vdda_phy failed, err=%d\n", - __func__, err); - goto out; - } - - phy_common->phy_spec_ops->power_control(phy_common, true); - - /* vdda_pll also enables ref clock LDOs so enable it first */ - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll); - if (err) { - dev_err(dev, "%s enable vdda_pll failed, err=%d\n", - __func__, err); - goto out_disable_phy; - } - - err = ufs_qcom_phy_enable_iface_clk(phy_common); - if (err) { - dev_err(dev, "%s enable phy iface clock failed, err=%d\n", - __func__, err); - goto out_disable_pll; - } - - err = ufs_qcom_phy_enable_ref_clk(phy_common); - if (err) { - dev_err(dev, "%s enable phy ref clock failed, err=%d\n", - __func__, err); - goto out_disable_iface_clk; - } - - /* enable device PHY ref_clk pad rail */ - if (phy_common->vddp_ref_clk.reg) { - err = ufs_qcom_phy_enable_vreg(dev, - &phy_common->vddp_ref_clk); - if (err) { - dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n", - __func__, err); - goto out_disable_ref_clk; - } - } - - goto out; - -out_disable_ref_clk: - ufs_qcom_phy_disable_ref_clk(phy_common); -out_disable_iface_clk: - ufs_qcom_phy_disable_iface_clk(phy_common); -out_disable_pll: - ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll); -out_disable_phy: - ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy); -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on); - -int ufs_qcom_phy_power_off(struct phy *generic_phy) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - - phy_common->phy_spec_ops->power_control(phy_common, false); - - if (phy_common->vddp_ref_clk.reg) - ufs_qcom_phy_disable_vreg(phy_common->dev, - &phy_common->vddp_ref_clk); - ufs_qcom_phy_disable_ref_clk(phy_common); - ufs_qcom_phy_disable_iface_clk(phy_common); - - ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); - ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); - reset_control_assert(phy_common->ufs_reset); - return 0; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); - -MODULE_AUTHOR("Yaniv Gardi "); -MODULE_AUTHOR("Vivek Gautam "); -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY"); -MODULE_LICENSE("GPL v2"); From b555f35f2f87f8a99ba8e65d3f51ae4294748b58 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:20:07 +0530 Subject: [PATCH 17/46] phy: core: fix code style in devm_of_phy_provider_unregister Documentation/process/coding-style.rst says: "functions: they have the opening brace at the beginning of the next line" devm_of_phy_provider_unregister() function has opening brace at same line, so fix it up. Link: https://lore.kernel.org/r/20200629145010.122675-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/phy-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index a27b8d578d7f..b54522f06245 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -1122,7 +1122,8 @@ EXPORT_SYMBOL_GPL(of_phy_provider_unregister); * of_phy_provider_unregister to unregister the phy provider. */ void devm_of_phy_provider_unregister(struct device *dev, - struct phy_provider *phy_provider) { + struct phy_provider *phy_provider) +{ int r; r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match, From aad075c155bcc4265e62e1f6fecab7db186d9572 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:20:08 +0530 Subject: [PATCH 18/46] phy: core: Document function args Some function arguments are missing from documentation prompting validation kernel doc script to complain: drivers/phy/phy-core.c:1078: warning: Function parameter or member 'children' not described in '__devm_of_phy_provider_register' drivers/phy/phy-core.c:1125: warning: Function parameter or member 'phy_provider' not described in 'devm_of_phy_provider_unregister' Add the documentation for these Link: https://lore.kernel.org/r/20200629145010.122675-2-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/phy-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index b54522f06245..71cb10826326 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -1062,6 +1062,7 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register); * __devm_of_phy_provider_register() - create/register phy provider with the * framework * @dev: struct device of the phy provider + * @children: device node containing children (if different from dev->of_node) * @owner: the module owner containing of_xlate * @of_xlate: function pointer to obtain phy instance from phy provider * @@ -1117,6 +1118,7 @@ EXPORT_SYMBOL_GPL(of_phy_provider_unregister); /** * devm_of_phy_provider_unregister() - remove phy provider from the framework * @dev: struct device of the phy provider + * @phy_provider: phy provider returned by of_phy_provider_register() * * destroys the devres associated with this phy provider and invokes * of_phy_provider_unregister to unregister the phy provider. From 3b0163bb34f15695feb0b87365bed3eba8f9804d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:20:09 +0530 Subject: [PATCH 19/46] phy: ti: dm816x: remove set but unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dm816x_usb_phy_init() sets variable 'error' but never uses it, so remove it. drivers/phy/ti/phy-dm816x-usb.c:85:6: warning: variable ‘error’ set but not used [-Wunused-but-set-variable] Link: https://lore.kernel.org/r/20200629145010.122675-3-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-dm816x-usb.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c index 26f194779064..57adc08a89b2 100644 --- a/drivers/phy/ti/phy-dm816x-usb.c +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -82,17 +82,16 @@ static int dm816x_usb_phy_init(struct phy *x) { struct dm816x_usb_phy *phy = phy_get_drvdata(x); unsigned int val; - int error; if (clk_get_rate(phy->refclk) != 24000000) dev_warn(phy->dev, "nonstandard phy refclk\n"); /* Set PLL ref clock and put phys to sleep */ - error = regmap_update_bits(phy->syscon, phy->usb_ctrl, - DM816X_USB_CTRL_PHYCLKSRC | - DM816X_USB_CTRL_PHYSLEEP1 | - DM816X_USB_CTRL_PHYSLEEP0, - 0); + regmap_update_bits(phy->syscon, phy->usb_ctrl, + DM816X_USB_CTRL_PHYCLKSRC | + DM816X_USB_CTRL_PHYSLEEP1 | + DM816X_USB_CTRL_PHYSLEEP0, + 0); regmap_read(phy->syscon, phy->usb_ctrl, &val); if ((val & 3) != 0) dev_info(phy->dev, From 3cc4502ce8f37b4f8a75bfc4eb6d99a74ce018e1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 29 Jun 2020 20:20:10 +0530 Subject: [PATCH 20/46] phy: ti-pipe3: remove set but unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ti_pipe3_power_on() sets variable 'ret' but never uses it, so remove it. drivers/phy/ti/phy-ti-pipe3.c:340:6: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable] Link: https://lore.kernel.org/r/20200629145010.122675-4-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-ti-pipe3.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c index a87946589eb7..e9332c90f75f 100644 --- a/drivers/phy/ti/phy-ti-pipe3.c +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -337,7 +337,6 @@ static int ti_pipe3_power_on(struct phy *x) { u32 val; u32 mask; - int ret; unsigned long rate; struct ti_pipe3 *phy = phy_get_drvdata(x); bool rx_pending = false; @@ -355,8 +354,8 @@ static int ti_pipe3_power_on(struct phy *x) rate = rate / 1000000; mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; val = rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; - ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, - mask, val); + regmap_update_bits(phy->phy_power_syscon, phy->power_reg, + mask, val); /* * For PCIe, TX and RX must be powered on simultaneously. * For USB and SATA, TX must be powered on before RX From 82c8d38699009697f3136f5347d0903befdce810 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Mon, 29 Jun 2020 21:57:27 +0200 Subject: [PATCH 21/46] phy: allwinner: phy-sun6i-mipi-dphy: Constify structs sun6i_dphy_ops and sun6i_dphy_regmap_config are not modified so make them const structs to allow the compiler to put them in read-only memory. Before: text data bss dec hex filename 4407 1944 64 6415 190f drivers/phy/allwinner/phy-sun6i-mipi-dphy.o After: text data bss dec hex filename 4835 1496 64 6395 18fb drivers/phy/allwinner/phy-sun6i-mipi-dphy.o Signed-off-by: Rikard Falkeborn Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20200629195727.9717-1-rikard.falkeborn@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c index 79c8af5c7c1d..1fa761ba6cbb 100644 --- a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c +++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c @@ -233,7 +233,7 @@ static int sun6i_dphy_exit(struct phy *phy) } -static struct phy_ops sun6i_dphy_ops = { +static const struct phy_ops sun6i_dphy_ops = { .configure = sun6i_dphy_configure, .power_on = sun6i_dphy_power_on, .power_off = sun6i_dphy_power_off, @@ -241,7 +241,7 @@ static struct phy_ops sun6i_dphy_ops = { .exit = sun6i_dphy_exit, }; -static struct regmap_config sun6i_dphy_regmap_config = { +static const struct regmap_config sun6i_dphy_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, From 270ff6048f45759d31a7b62d3983b084153837f5 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Fri, 3 Jul 2020 14:46:00 +0800 Subject: [PATCH 22/46] phy: cadence: salvo: fix wrong bit definition It fixes RX detect wakeup using USB3 device, otherwise, the USB3 device can't wakeup USB PHY when the PHY is in 32Khz clock. Fixes: 50d35aa8c15f ("phy: cadence: salvo: add salvo phy driver") Signed-off-by: Peter Chen Link: https://lore.kernel.org/r/20200703064600.14181-1-peter.chen@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-salvo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c index 1ecbb964cd21..016514e4aa54 100644 --- a/drivers/phy/cadence/phy-cadence-salvo.c +++ b/drivers/phy/cadence/phy-cadence-salvo.c @@ -88,7 +88,7 @@ #define TB_ADDR_TX_RCVDETSC_CTRL 0x4124 /* TB_ADDR_TX_RCVDETSC_CTRL */ -#define RXDET_IN_P3_32KHZ BIT(1) +#define RXDET_IN_P3_32KHZ BIT(0) struct cdns_reg_pairs { u16 val; From 010f0dff9f07fe4e4633807253b02126cf8981fa Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 7 Jul 2020 17:22:26 +0800 Subject: [PATCH 23/46] phy: allwinner: Make PHY_SUN6I_MIPI_DPHY depend on COMMON_CLK When CONFIG_ARCH_SUNXI is not set but CONFIG_COMPILE_TEST=y, CONFIG_HAVE_CLK=y, CONFIG_HAVE_LEGACY_CLK=y, there exists the following build errors with CONFIG_PHY_SUN6I_MIPI_DPHY=y: drivers/phy/allwinner/phy-sun6i-mipi-dphy.o: In function `sun6i_dphy_init': phy-sun6i-mipi-dphy.c:(.text+0x320): undefined reference to `clk_set_rate_exclusive' drivers/phy/allwinner/phy-sun6i-mipi-dphy.o: In function `sun6i_dphy_exit': phy-sun6i-mipi-dphy.c:(.text+0x2c8): undefined reference to `clk_rate_exclusive_put' clk_set_rate_exclusive() and clk_rate_exclusive_put() are defined in drivers/clk/clk.c, this file is built under CONFIG_COMMON_CLK, so in order to build drivers/phy/allwinner/phy-sun6i-mipi-dphy.c successful used with various configs, CONFIG_PHY_SUN6I_MIPI_DPHY should depend on CONFIG_COMMON_CLK. Fixes: 133552bf03ed ("phy: Remove CONFIG_ARCH_* check for related subdir in Makefile") Reported-by: kernel test robot Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/1594113746-25393-1-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Vinod Koul --- drivers/phy/allwinner/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig index e760d89d3976..fb584518b2d0 100644 --- a/drivers/phy/allwinner/Kconfig +++ b/drivers/phy/allwinner/Kconfig @@ -22,7 +22,7 @@ config PHY_SUN4I_USB config PHY_SUN6I_MIPI_DPHY tristate "Allwinner A31 MIPI D-PHY Support" depends on ARCH_SUNXI || COMPILE_TEST - depends on HAS_IOMEM + depends on HAS_IOMEM && COMMON_CLK depends on RESET_CONTROLLER select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY From 03815930c162561a5c204494b4160d6ccf631b0b Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Wed, 8 Jul 2020 17:57:44 +0900 Subject: [PATCH 24/46] dt-bindings: phy: uniphier: Fix incorrect clocks and clock-names for PXs3 usb3-hsphy The usb3-hsphy for PXs3 SoC needs to accept 3 clocks like usb3-ssphy. Fixes: 134ab2845acb ("dt-bindings: phy: Convert UniPhier USB3-PHY conroller to json-schema") Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1594198664-29381-1-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- .../bindings/phy/socionext,uniphier-usb3hs-phy.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml b/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml index f88d36207b87..c871d462c952 100644 --- a/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml @@ -31,12 +31,16 @@ properties: clocks: minItems: 1 - maxItems: 2 + maxItems: 3 clock-names: oneOf: - const: link # for PXs2 - - items: # for PXs3 + - items: # for PXs3 with phy-ext + - const: link + - const: phy + - const: phy-ext + - items: # for others - const: link - const: phy From dcbabfeb17c3c2fdb6bc92a3031ecd37df1834a8 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 8 Jul 2020 15:38:00 +0200 Subject: [PATCH 25/46] phy: exynos5-usbdrd: Calibrating makes sense only for USB2.0 PHY PHY calibration is needed only for USB2.0 (UTMI) PHY, so skip calling calibration code when phy_calibrate() is called for USB3.0 (PIPE3) PHY. Fixes: d8c80bb3b55b ("phy: exynos5-usbdrd: Calibrate LOS levels for exynos5420/5800") Signed-off-by: Marek Szyprowski Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200708133800.3336-1-m.szyprowski@samsung.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-exynos5-usbdrd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index eb06ce9f748f..9930d2027e94 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -714,7 +714,9 @@ static int exynos5_usbdrd_phy_calibrate(struct phy *phy) struct phy_usb_instance *inst = phy_get_drvdata(phy); struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - return exynos5420_usbdrd_phy_calibrate(phy_drd); + if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) + return exynos5420_usbdrd_phy_calibrate(phy_drd); + return 0; } static const struct phy_ops exynos5_usbdrd_phy_ops = { From 901e0a8f14a1497ce6229445daac4aec1dd93b92 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:04 +0530 Subject: [PATCH 26/46] phy-mvebu-a3700-utmi: correct typo in struct mvebu_a3700_utmi comments We get warning with W=1 build: drivers/phy/marvell/phy-mvebu-a3700-utmi.c:84: warning: Function parameter or member 'usb_misc' not described in 'mvebu_a3700_utmi' Fix that by correcting the typo usb_mis Link: https://lore.kernel.org/r/20200708132809.265967-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c index 23bc3bf5c4c0..8834436bc9db 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c @@ -72,7 +72,7 @@ struct mvebu_a3700_utmi_caps { * struct mvebu_a3700_utmi - PHY driver data * * @regs: PHY registers - * @usb_mis: Regmap with USB miscellaneous registers including PHY ones + * @usb_misc: Regmap with USB miscellaneous registers including PHY ones * @caps: PHY capabilities * @phy: PHY handle */ From c988b7c5fc056d3bec4cb337a57fc2fe6a04081a Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:05 +0530 Subject: [PATCH 27/46] phy: mapphone-mdm6600: Add missing description for some structure fields We get warning with W=1 build: drivers/phy/motorola/phy-mapphone-mdm6600.c:185: warning: Function parameter or member 'val' not described in 'phy_mdm6600_cmd' drivers/phy/motorola/phy-mapphone-mdm6600.c:200: warning: Function parameter or member 'work' not described in 'phy_mdm6600_status' Fix that by adding description for 'val' and 'work' Link: https://lore.kernel.org/r/20200708132809.265967-2-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/motorola/phy-mapphone-mdm6600.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c index 94a34cf75eb3..5172971f4c36 100644 --- a/drivers/phy/motorola/phy-mapphone-mdm6600.c +++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c @@ -178,6 +178,7 @@ static const struct phy_ops gpio_usb_ops = { /** * phy_mdm6600_cmd() - send a command request to mdm6600 * @ddata: device driver data + * @val: value of cmd to be set * * Configures the three command request GPIOs to the specified value. */ @@ -194,7 +195,7 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val) /** * phy_mdm6600_status() - read mdm6600 status lines - * @ddata: device driver data + * @work: work structure */ static void phy_mdm6600_status(struct work_struct *work) { From f0c47c74583cc55c7ff9bdafff63bfb33edbb940 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:06 +0530 Subject: [PATCH 28/46] phy: xgene: remove unsigned integer comparison with less than zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We get warning with W=1 build: drivers/phy/phy-xgene.c: In function ‘xgene_phy_xlate’: drivers/phy/phy-xgene.c:1618:20: warning: comparison of unsigned expression in ‘< 0’ is always false [-Wtype-limits] 1618 | if (args->args[0] < MODE_SATA || args->args[0] >= MODE_MAX) | args is uint32_t so can never be less than zero, so remove this check Link: https://lore.kernel.org/r/20200708132809.265967-3-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/phy-xgene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index 7a33ec12f71b..b88922e7de1d 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -1615,7 +1615,7 @@ static struct phy *xgene_phy_xlate(struct device *dev, if (args->args_count <= 0) return ERR_PTR(-EINVAL); - if (args->args[0] < MODE_SATA || args->args[0] >= MODE_MAX) + if (args->args[0] >= MODE_MAX) return ERR_PTR(-EINVAL); ctx->mode = args->args[0]; From 72fbf95f36218ec2a901e0eb7c3aa0bea6f1f396 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:07 +0530 Subject: [PATCH 29/46] phy: rockchip-typec: use correct format for structure description We get warning with W=1 build: drivers/phy/rockchip/phy-rockchip-typec.c:360: warning: cannot understand function prototype: 'struct rockchip_usb3phy_port_cfg ' The 'struct rockchip_usb3phy_port_cfg ' is commented properly but uses wrong format, so fix that up Link: https://lore.kernel.org/r/20200708132809.265967-4-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-typec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 24563160197f..70a31251b202 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -347,7 +347,7 @@ struct usb3phy_reg { }; /** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. + * struct rockchip_usb3phy_port_cfg - usb3-phy port configuration. * @reg: the base address for usb3-phy config. * @typec_conn_dir: the register of type-c connector direction. * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. From 0672fb0d422246725c3af0957cbdbedcc86008a1 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:08 +0530 Subject: [PATCH 30/46] phy: exynos5-usbdrd: use correct format for structure description We get warning with W=1 build: drivers/phy/samsung/phy-exynos5-usbdrd.c:211: warning: Function parameter or member 'phys' not described in 'exynos5_usbdrd_phy' drivers/phy/samsung/phy-exynos5-usbdrd.c:211: warning: Function parameter or member 'vbus' not described in 'exynos5_usbdrd_phy' drivers/phy/samsung/phy-exynos5-usbdrd.c:211: warning: Function parameter or member 'vbus_boost' not described in 'exynos5_usbdrd_phy' These members are provided with description but format is not quite right resulting in above warnings Acked-by: Marek Szyprowski Link: https://lore.kernel.org/r/20200708132809.265967-5-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-exynos5-usbdrd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index 9930d2027e94..0d818b77a0d8 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -180,14 +180,14 @@ struct exynos5_usbdrd_phy_drvdata { * @utmiclk: clock for utmi+ phy * @itpclk: clock for ITP generation * @drv_data: pointer to SoC level driver data structure - * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY + * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY * instances each with its 'phy' and 'phy_cfg'. * @extrefclk: frequency select settings when using 'separate * reference clocks' for SS and HS operations * @ref_clk: reference clock to PHY block from which PHY's * operational clocks are derived - * vbus: VBUS regulator for phy - * vbus_boost: Boost regulator for VBUS present on few Exynos boards + * @vbus: VBUS regulator for phy + * @vbus_boost: Boost regulator for VBUS present on few Exynos boards */ struct exynos5_usbdrd_phy { struct device *dev; From 0ff35966d171ec99b118df666c1687cc86ba8d7e Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 8 Jul 2020 18:58:09 +0530 Subject: [PATCH 31/46] phy: stm32: use NULL instead of zero devm_clk_get() and devm_reset_control_get() expect a const char *id for the last arg, but a value of zero was provided. This results in below sparse warning: drivers/phy/st/phy-stm32-usbphyc.c:330:42: warning: Using plain integer as NULL pointer drivers/phy/st/phy-stm32-usbphyc.c:343:52: warning: Using plain integer as NULL pointer Instead of zero, use NULL Reviewed-by: Amelie Delaunay Link: https://lore.kernel.org/r/20200708132809.265967-6-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/st/phy-stm32-usbphyc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 56bdea4b0bd9..2b3639cba51a 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -327,7 +327,7 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) if (IS_ERR(usbphyc->base)) return PTR_ERR(usbphyc->base); - usbphyc->clk = devm_clk_get(dev, 0); + usbphyc->clk = devm_clk_get(dev, NULL); if (IS_ERR(usbphyc->clk)) { ret = PTR_ERR(usbphyc->clk); dev_err(dev, "clk get failed: %d\n", ret); @@ -340,7 +340,7 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) return ret; } - usbphyc->rst = devm_reset_control_get(dev, 0); + usbphyc->rst = devm_reset_control_get(dev, NULL); if (!IS_ERR(usbphyc->rst)) { reset_control_assert(usbphyc->rst); udelay(2); From 8b34a2895bb7f791ce6bebc1737373e9475c58b2 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 16 Jul 2020 11:54:27 +0530 Subject: [PATCH 32/46] phy: sun4i-usb: explicitly include gpio/consumer.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver uses GPIO functions but doesn't include the header explcitly. Add that to fix build errors when GPIOLIB is disabled. drivers/phy/allwinner/phy-sun4i-usb.c:367:11: error: implicit declaration of function ‘gpiod_get_value_cansleep’; did you mean ‘gpio_get_value_cansleep’? [-Werror=implicit-function-declaration] drivers/phy/allwinner/phy-sun4i-usb.c:707:22: error: implicit declaration of function ‘devm_gpiod_get_optional’; did you mean ‘devm_clk_get_optional’? [-Werror=implicit-function-declaration] drivers/phy/allwinner/phy-sun4i-usb.c:708:11: error: ‘GPIOD_IN’ undeclared (first use in this function); did you mean ‘GPIOF_IN’? drivers/phy/allwinner/phy-sun4i-usb.c:815:21: error: implicit declaration of function ‘gpiod_to_irq’; did you mean ‘gpio_to_irq’? [-Werror=implicit-function-declaration] Reported-by: Randy Dunlap Signed-off-by: Vinod Koul Acked-by: Chen-Yu Tsai Acked-by: Randy Dunlap # build-tested Link: https://lore.kernel.org/r/20200716062427.71763-1-vkoul@kernel.org --- drivers/phy/allwinner/phy-sun4i-usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 7e09ad6a0b42..585d0a59e712 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include From 170ba9c23f0b813f9ba53a9cd3fec0f3f95f45aa Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Fri, 17 Jul 2020 00:52:16 +0530 Subject: [PATCH 33/46] dt-bindings: phy: Document Samsung UFS PHY bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch documents Samsung UFS PHY device tree bindings Reviewed-by: Rob Herring Signed-off-by: Alim Akhtar Tested-by: Paweł Chmiel Link: https://lore.kernel.org/r/20200716192217.35740-1-alim.akhtar@samsung.com Signed-off-by: Vinod Koul --- .../bindings/phy/samsung,ufs-phy.yaml | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml b/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml new file mode 100644 index 000000000000..636cc501b54f --- /dev/null +++ b/Documentation/devicetree/bindings/phy/samsung,ufs-phy.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/samsung,ufs-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung SoC series UFS PHY Device Tree Bindings + +maintainers: + - Alim Akhtar + +properties: + "#phy-cells": + const: 0 + + compatible: + enum: + - samsung,exynos7-ufs-phy + + reg: + maxItems: 1 + + reg-names: + items: + - const: phy-pma + + clocks: + items: + - description: PLL reference clock + - description: symbol clock for input symbol ( rx0-ch0 symbol clock) + - description: symbol clock for input symbol ( rx1-ch1 symbol clock) + - description: symbol clock for output symbol ( tx0 symbol clock) + + clock-names: + items: + - const: ref_clk + - const: rx1_symbol_clk + - const: rx0_symbol_clk + - const: tx0_symbol_clk + + samsung,pmu-syscon: + $ref: '/schemas/types.yaml#/definitions/phandle' + description: phandle for PMU system controller interface, used to + control pmu registers bits for ufs m-phy + +required: + - "#phy-cells" + - compatible + - reg + - reg-names + - clocks + - clock-names + - samsung,pmu-syscon + +additionalProperties: false + +examples: + - | + #include + + ufs_phy: ufs-phy@15571800 { + compatible = "samsung,exynos7-ufs-phy"; + reg = <0x15571800 0x240>; + reg-names = "phy-pma"; + samsung,pmu-syscon = <&pmu_system_controller>; + #phy-cells = <0>; + clocks = <&clock_fsys1 SCLK_COMBO_PHY_EMBEDDED_26M>, + <&clock_fsys1 PHYCLK_UFS20_RX1_SYMBOL_USER>, + <&clock_fsys1 PHYCLK_UFS20_RX0_SYMBOL_USER>, + <&clock_fsys1 PHYCLK_UFS20_TX0_SYMBOL_USER>; + clock-names = "ref_clk", "rx1_symbol_clk", + "rx0_symbol_clk", "tx0_symbol_clk"; + + }; +... From bca21e930451416f92c0c4c679feb9d8fa33c08f Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Fri, 17 Jul 2020 00:52:17 +0530 Subject: [PATCH 34/46] phy: samsung-ufs: add UFS PHY driver for samsung SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces Samsung UFS PHY driver. This driver supports to deal with phy calibration and power control according to UFS host driver's behavior. [Robot: -Wmissing-prototypes and -Wsometimes-uninitialized] Reported-by: kernel test robot Reviewed-by: Kiwoong Kim Signed-off-by: Seungwon Jeon Signed-off-by: Alim Akhtar Cc: Kishon Vijay Abraham I Cc: Vinod Koul Tested-by: Paweł Chmiel Link: https://lore.kernel.org/r/20200716192217.35740-2-alim.akhtar@samsung.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/Kconfig | 9 + drivers/phy/samsung/Makefile | 1 + drivers/phy/samsung/phy-exynos7-ufs.h | 81 ++++++ drivers/phy/samsung/phy-samsung-ufs.c | 366 ++++++++++++++++++++++++++ drivers/phy/samsung/phy-samsung-ufs.h | 139 ++++++++++ 5 files changed, 596 insertions(+) create mode 100644 drivers/phy/samsung/phy-exynos7-ufs.h create mode 100644 drivers/phy/samsung/phy-samsung-ufs.c create mode 100644 drivers/phy/samsung/phy-samsung-ufs.h diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index 19f2e3119343..e20d2fcc9fe7 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -29,6 +29,15 @@ config PHY_EXYNOS_PCIE Enable PCIe PHY support for Exynos SoC series. This driver provides PHY interface for Exynos PCIe controller. +config PHY_SAMSUNG_UFS + tristate "SAMSUNG SoC series UFS PHY driver" + depends on OF && (ARCH_EXYNOS || COMPILE_TEST) + select GENERIC_PHY + help + Enable this to support the Samsung UFS PHY driver for + Samsung SoCs. This driver provides the interface for UFS + host controller to do PHY related programming. + config PHY_SAMSUNG_USB2 tristate "Samsung USB 2.0 PHY driver" depends on HAS_IOMEM diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile index db9b1aa0de6e..3959100fe8a2 100644 --- a/drivers/phy/samsung/Makefile +++ b/drivers/phy/samsung/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o +obj-$(CONFIG_PHY_SAMSUNG_UFS) += phy-samsung-ufs.o obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o phy-exynos-usb2-y += phy-samsung-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o diff --git a/drivers/phy/samsung/phy-exynos7-ufs.h b/drivers/phy/samsung/phy-exynos7-ufs.h new file mode 100644 index 000000000000..518923141958 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos7-ufs.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * UFS PHY driver data for Samsung EXYNOS7 SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + */ +#ifndef _PHY_EXYNOS7_UFS_H_ +#define _PHY_EXYNOS7_UFS_H_ + +#include "phy-samsung-ufs.h" + +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL 0x720 +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1 +#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0) + +/* Calibration for phy initialization */ +static const struct samsung_ufs_phy_cfg exynos7_pre_init_cfg[] = { + PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY), + PHY_COMN_REG_CFG(0x017, 0x84, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY), + PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY), + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B */ +static const struct samsung_ufs_phy_cfg exynos7_pre_pwr_hs_cfg[] = { + PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_HS_ANY), + /* Setting order: 1st(0x16, 2nd(0x15) */ + PHY_COMN_REG_CFG(0x016, 0xff, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x015, 0x80, PWR_MODE_HS_ANY), + PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x037, 0x43, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x038, 0x3f, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x042, 0xbb, PWR_MODE_HS_G2_SER_B), + PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x034, 0x35, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x034, 0x36, PWR_MODE_HS_G2_SER_B), + PHY_TRSV_REG_CFG(0x035, 0x5b, PWR_MODE_HS_G2_SER_A), + PHY_TRSV_REG_CFG(0x035, 0x5c, PWR_MODE_HS_G2_SER_B), + END_UFS_PHY_CFG +}; + +/* Calibration for HS mode series A/B atfer PMC */ +static const struct samsung_ufs_phy_cfg exynos7_post_pwr_hs_cfg[] = { + PHY_COMN_REG_CFG(0x015, 0x00, PWR_MODE_HS_ANY), + PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_HS_ANY), + END_UFS_PHY_CFG +}; + +static const struct samsung_ufs_phy_cfg *exynos7_ufs_phy_cfgs[CFG_TAG_MAX] = { + [CFG_PRE_INIT] = exynos7_pre_init_cfg, + [CFG_PRE_PWR_HS] = exynos7_pre_pwr_hs_cfg, + [CFG_POST_PWR_HS] = exynos7_post_pwr_hs_cfg, +}; + +static struct samsung_ufs_phy_drvdata exynos7_ufs_phy = { + .cfg = exynos7_ufs_phy_cfgs, + .isol = { + .offset = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL, + .mask = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK, + .en = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN, + }, + .has_symbol_clk = 1, +}; + +#endif /* _PHY_EXYNOS7_UFS_H_ */ diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c new file mode 100644 index 000000000000..43ef77d1d96c --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-ufs.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * UFS PHY driver for Samsung SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author: Seungwon Jeon + * Author: Alim Akhtar + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-samsung-ufs.h" + +#define for_each_phy_lane(phy, i) \ + for (i = 0; i < (phy)->lane_cnt; i++) +#define for_each_phy_cfg(cfg) \ + for (; (cfg)->id; (cfg)++) + +#define PHY_DEF_LANE_CNT 1 + +static void samsung_ufs_phy_config(struct samsung_ufs_phy *phy, + const struct samsung_ufs_phy_cfg *cfg, + u8 lane) +{ + enum {LANE_0, LANE_1}; /* lane index */ + + switch (lane) { + case LANE_0: + writel(cfg->val, (phy)->reg_pma + cfg->off_0); + break; + case LANE_1: + if (cfg->id == PHY_TRSV_BLK) + writel(cfg->val, (phy)->reg_pma + cfg->off_1); + break; + } +} + +static int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy) +{ + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); + const unsigned int timeout_us = 100000; + const unsigned int sleep_us = 10; + u32 val; + int err; + + err = readl_poll_timeout( + ufs_phy->reg_pma + PHY_APB_ADDR(PHY_PLL_LOCK_STATUS), + val, (val & PHY_PLL_LOCK_BIT), sleep_us, timeout_us); + if (err) { + dev_err(ufs_phy->dev, + "failed to get phy pll lock acquisition %d\n", err); + goto out; + } + + err = readl_poll_timeout( + ufs_phy->reg_pma + PHY_APB_ADDR(PHY_CDR_LOCK_STATUS), + val, (val & PHY_CDR_LOCK_BIT), sleep_us, timeout_us); + if (err) + dev_err(ufs_phy->dev, + "failed to get phy cdr lock acquisition %d\n", err); +out: + return err; +} + +static int samsung_ufs_phy_calibrate(struct phy *phy) +{ + struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy); + struct samsung_ufs_phy_cfg **cfgs = ufs_phy->cfg; + const struct samsung_ufs_phy_cfg *cfg; + int err = 0; + int i; + + if (unlikely(ufs_phy->ufs_phy_state < CFG_PRE_INIT || + ufs_phy->ufs_phy_state >= CFG_TAG_MAX)) { + dev_err(ufs_phy->dev, "invalid phy config index %d\n", ufs_phy->ufs_phy_state); + return -EINVAL; + } + + cfg = cfgs[ufs_phy->ufs_phy_state]; + if (!cfg) + goto out; + + for_each_phy_cfg(cfg) { + for_each_phy_lane(ufs_phy, i) { + samsung_ufs_phy_config(ufs_phy, cfg, i); + } + } + + if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS) + err = samsung_ufs_phy_wait_for_lock_acq(phy); + + /** + * In Samsung ufshci, PHY need to be calibrated at different + * stages / state mainly before Linkstartup, after Linkstartup, + * before power mode change and after power mode change. + * Below state machine to make sure to calibrate PHY in each + * state. Here after configuring PHY in a given state, will + * change the state to next state so that next state phy + * calibration value can be programed + */ +out: + switch (ufs_phy->ufs_phy_state) { + case CFG_PRE_INIT: + ufs_phy->ufs_phy_state = CFG_POST_INIT; + break; + case CFG_POST_INIT: + ufs_phy->ufs_phy_state = CFG_PRE_PWR_HS; + break; + case CFG_PRE_PWR_HS: + ufs_phy->ufs_phy_state = CFG_POST_PWR_HS; + break; + case CFG_POST_PWR_HS: + /* Change back to INIT state */ + ufs_phy->ufs_phy_state = CFG_PRE_INIT; + break; + default: + dev_err(ufs_phy->dev, "wrong state for phy calibration\n"); + } + + return err; +} + +static int samsung_ufs_phy_symbol_clk_init(struct samsung_ufs_phy *phy) +{ + int ret; + + phy->tx0_symbol_clk = devm_clk_get(phy->dev, "tx0_symbol_clk"); + if (IS_ERR(phy->tx0_symbol_clk)) { + dev_err(phy->dev, "failed to get tx0_symbol_clk clock\n"); + return PTR_ERR(phy->tx0_symbol_clk); + } + + phy->rx0_symbol_clk = devm_clk_get(phy->dev, "rx0_symbol_clk"); + if (IS_ERR(phy->rx0_symbol_clk)) { + dev_err(phy->dev, "failed to get rx0_symbol_clk clock\n"); + return PTR_ERR(phy->rx0_symbol_clk); + } + + phy->rx1_symbol_clk = devm_clk_get(phy->dev, "rx1_symbol_clk"); + if (IS_ERR(phy->rx0_symbol_clk)) { + dev_err(phy->dev, "failed to get rx1_symbol_clk clock\n"); + return PTR_ERR(phy->rx1_symbol_clk); + } + + ret = clk_prepare_enable(phy->tx0_symbol_clk); + if (ret) { + dev_err(phy->dev, "%s: tx0_symbol_clk enable failed %d\n", __func__, ret); + goto out; + } + + ret = clk_prepare_enable(phy->rx0_symbol_clk); + if (ret) { + dev_err(phy->dev, "%s: rx0_symbol_clk enable failed %d\n", __func__, ret); + goto out_disable_tx0_clk; + } + + ret = clk_prepare_enable(phy->rx1_symbol_clk); + if (ret) { + dev_err(phy->dev, "%s: rx1_symbol_clk enable failed %d\n", __func__, ret); + goto out_disable_rx0_clk; + } + + return 0; + +out_disable_rx0_clk: + clk_disable_unprepare(phy->rx0_symbol_clk); +out_disable_tx0_clk: + clk_disable_unprepare(phy->tx0_symbol_clk); +out: + return ret; +} + +static int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy) +{ + int ret; + + phy->ref_clk = devm_clk_get(phy->dev, "ref_clk"); + if (IS_ERR(phy->ref_clk)) + dev_err(phy->dev, "failed to get ref_clk clock\n"); + + ret = clk_prepare_enable(phy->ref_clk); + if (ret) { + dev_err(phy->dev, "%s: ref_clk enable failed %d\n", __func__, ret); + return ret; + } + + dev_dbg(phy->dev, "UFS MPHY ref_clk_rate = %ld\n", clk_get_rate(phy->ref_clk)); + + return 0; +} + +static int samsung_ufs_phy_init(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + int ret; + + ss_phy->lane_cnt = phy->attrs.bus_width; + ss_phy->ufs_phy_state = CFG_PRE_INIT; + + if (ss_phy->drvdata->has_symbol_clk) { + ret = samsung_ufs_phy_symbol_clk_init(ss_phy); + if (ret) + dev_err(ss_phy->dev, "failed to set ufs phy symbol clocks\n"); + } + + ret = samsung_ufs_phy_clks_init(ss_phy); + if (ret) + dev_err(ss_phy->dev, "failed to set ufs phy clocks\n"); + + ret = samsung_ufs_phy_calibrate(phy); + if (ret) + dev_err(ss_phy->dev, "ufs phy calibration failed\n"); + + return ret; +} + +static int samsung_ufs_phy_power_on(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + samsung_ufs_phy_ctrl_isol(ss_phy, false); + return 0; +} + +static int samsung_ufs_phy_power_off(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + samsung_ufs_phy_ctrl_isol(ss_phy, true); + return 0; +} + +static int samsung_ufs_phy_set_mode(struct phy *generic_phy, + enum phy_mode mode, int submode) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(generic_phy); + + ss_phy->mode = PHY_MODE_INVALID; + + if (mode > 0) + ss_phy->mode = mode; + + return 0; +} + +static int samsung_ufs_phy_exit(struct phy *phy) +{ + struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy); + + clk_disable_unprepare(ss_phy->ref_clk); + + if (ss_phy->drvdata->has_symbol_clk) { + clk_disable_unprepare(ss_phy->tx0_symbol_clk); + clk_disable_unprepare(ss_phy->rx0_symbol_clk); + clk_disable_unprepare(ss_phy->rx1_symbol_clk); + } + + return 0; +} + +static struct phy_ops samsung_ufs_phy_ops = { + .init = samsung_ufs_phy_init, + .exit = samsung_ufs_phy_exit, + .power_on = samsung_ufs_phy_power_on, + .power_off = samsung_ufs_phy_power_off, + .calibrate = samsung_ufs_phy_calibrate, + .set_mode = samsung_ufs_phy_set_mode, + .owner = THIS_MODULE, +}; + +static const struct of_device_id samsung_ufs_phy_match[]; + +static int samsung_ufs_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct samsung_ufs_phy *phy; + struct phy *gen_phy; + struct phy_provider *phy_provider; + const struct samsung_ufs_phy_drvdata *drvdata; + int err = 0; + + match = of_match_node(samsung_ufs_phy_match, dev->of_node); + if (!match) { + err = -EINVAL; + dev_err(dev, "failed to get match_node\n"); + goto out; + } + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + + phy->reg_pma = devm_platform_ioremap_resource_byname(pdev, "phy-pma"); + if (IS_ERR(phy->reg_pma)) { + err = PTR_ERR(phy->reg_pma); + goto out; + } + + phy->reg_pmu = syscon_regmap_lookup_by_phandle( + dev->of_node, "samsung,pmu-syscon"); + if (IS_ERR(phy->reg_pmu)) { + err = PTR_ERR(phy->reg_pmu); + dev_err(dev, "failed syscon remap for pmu\n"); + goto out; + } + + gen_phy = devm_phy_create(dev, NULL, &samsung_ufs_phy_ops); + if (IS_ERR(gen_phy)) { + err = PTR_ERR(gen_phy); + dev_err(dev, "failed to create PHY for ufs-phy\n"); + goto out; + } + + drvdata = match->data; + phy->dev = dev; + phy->drvdata = drvdata; + phy->cfg = (struct samsung_ufs_phy_cfg **)drvdata->cfg; + phy->isol = &drvdata->isol; + phy->lane_cnt = PHY_DEF_LANE_CNT; + + phy_set_drvdata(gen_phy, phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + err = PTR_ERR(phy_provider); + dev_err(dev, "failed to register phy-provider\n"); + goto out; + } +out: + return err; +} + +static const struct of_device_id samsung_ufs_phy_match[] = { + { + .compatible = "samsung,exynos7-ufs-phy", + .data = &exynos7_ufs_phy, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_ufs_phy_match); + +static struct platform_driver samsung_ufs_phy_driver = { + .probe = samsung_ufs_phy_probe, + .driver = { + .name = "samsung-ufs-phy", + .of_match_table = samsung_ufs_phy_match, + }, +}; +module_platform_driver(samsung_ufs_phy_driver); +MODULE_DESCRIPTION("Samsung SoC UFS PHY Driver"); +MODULE_AUTHOR("Seungwon Jeon "); +MODULE_AUTHOR("Alim Akhtar "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-samsung-ufs.h b/drivers/phy/samsung/phy-samsung-ufs.h new file mode 100644 index 000000000000..5de78710524c --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-ufs.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * UFS PHY driver for Samsung EXYNOS SoC + * + * Copyright (C) 2020 Samsung Electronics Co., Ltd. + * Author: Seungwon Jeon + * Author: Alim Akhtar + * + */ +#ifndef _PHY_SAMSUNG_UFS_ +#define _PHY_SAMSUNG_UFS_ + +#define PHY_COMN_BLK 1 +#define PHY_TRSV_BLK 2 +#define END_UFS_PHY_CFG { 0 } +#define PHY_TRSV_CH_OFFSET 0x30 +#define PHY_APB_ADDR(off) ((off) << 2) + +#define PHY_COMN_REG_CFG(o, v, d) { \ + .off_0 = PHY_APB_ADDR((o)), \ + .off_1 = 0, \ + .val = (v), \ + .desc = (d), \ + .id = PHY_COMN_BLK, \ +} + +#define PHY_TRSV_REG_CFG(o, v, d) { \ + .off_0 = PHY_APB_ADDR((o)), \ + .off_1 = PHY_APB_ADDR((o) + PHY_TRSV_CH_OFFSET), \ + .val = (v), \ + .desc = (d), \ + .id = PHY_TRSV_BLK, \ +} + +/* UFS PHY registers */ +#define PHY_PLL_LOCK_STATUS 0x1e +#define PHY_CDR_LOCK_STATUS 0x5e + +#define PHY_PLL_LOCK_BIT BIT(5) +#define PHY_CDR_LOCK_BIT BIT(4) + +/* description for PHY calibration */ +enum { + /* applicable to any */ + PWR_DESC_ANY = 0, + /* mode */ + PWR_DESC_PWM = 1, + PWR_DESC_HS = 2, + /* series */ + PWR_DESC_SER_A = 1, + PWR_DESC_SER_B = 2, + /* gear */ + PWR_DESC_G1 = 1, + PWR_DESC_G2 = 2, + PWR_DESC_G3 = 3, + /* field mask */ + MD_MASK = 0x3, + SR_MASK = 0x3, + GR_MASK = 0x7, +}; + +#define PWR_MODE_HS_G1_ANY PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_ANY) +#define PWR_MODE_HS_G1_SER_A PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_A) +#define PWR_MODE_HS_G1_SER_B PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_B) +#define PWR_MODE_HS_G2_ANY PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_ANY) +#define PWR_MODE_HS_G2_SER_A PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_A) +#define PWR_MODE_HS_G2_SER_B PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_B) +#define PWR_MODE_HS_G3_ANY PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_ANY) +#define PWR_MODE_HS_G3_SER_A PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_A) +#define PWR_MODE_HS_G3_SER_B PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_B) +#define PWR_MODE(g, s, m) ((((g) & GR_MASK) << 4) |\ + (((s) & SR_MASK) << 2) | ((m) & MD_MASK)) +#define PWR_MODE_PWM_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_PWM) +#define PWR_MODE_HS(g, s) ((((g) & GR_MASK) << 4) |\ + (((s) & SR_MASK) << 2) | PWR_DESC_HS) +#define PWR_MODE_HS_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_HS) +#define PWR_MODE_ANY PWR_MODE(PWR_DESC_ANY,\ + PWR_DESC_ANY, PWR_DESC_ANY) +/* PHY calibration point/state */ +enum { + CFG_PRE_INIT, + CFG_POST_INIT, + CFG_PRE_PWR_HS, + CFG_POST_PWR_HS, + CFG_TAG_MAX, +}; + +struct samsung_ufs_phy_cfg { + u32 off_0; + u32 off_1; + u32 val; + u8 desc; + u8 id; +}; + +struct samsung_ufs_phy_drvdata { + const struct samsung_ufs_phy_cfg **cfg; + struct pmu_isol { + u32 offset; + u32 mask; + u32 en; + } isol; + bool has_symbol_clk; +}; + +struct samsung_ufs_phy { + struct device *dev; + void __iomem *reg_pma; + struct regmap *reg_pmu; + struct clk *ref_clk; + struct clk *ref_clk_parent; + struct clk *tx0_symbol_clk; + struct clk *rx0_symbol_clk; + struct clk *rx1_symbol_clk; + const struct samsung_ufs_phy_drvdata *drvdata; + struct samsung_ufs_phy_cfg **cfg; + const struct pmu_isol *isol; + u8 lane_cnt; + int ufs_phy_state; + enum phy_mode mode; +}; + +static inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *phy) +{ + return (struct samsung_ufs_phy *)phy_get_drvdata(phy); +} + +static inline void samsung_ufs_phy_ctrl_isol( + struct samsung_ufs_phy *phy, u32 isol) +{ + regmap_update_bits(phy->reg_pmu, phy->isol->offset, + phy->isol->mask, isol ? 0 : phy->isol->en); +} + +#include "phy-exynos7-ufs.h" + +#endif /* _PHY_SAMSUNG_UFS_ */ From ef19b117b83466e1c030368101a24367a34be7f0 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Fri, 17 Jul 2020 15:16:31 +0200 Subject: [PATCH 35/46] phy: qualcomm: add qcom ipq806x dwc usb phy driver This has lost in the original push for the dwc3 qcom driver. This is needed for ipq806x SoC as without this the usb ports doesn't work at all. Signed-off-by: Andy Gross Signed-off-by: Ansuel Smith Tested-by: Jonathan McDowell Link: https://lore.kernel.org/r/20200717131635.11076-1-ansuelsmth@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/Kconfig | 10 + drivers/phy/qualcomm/Makefile | 1 + drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c | 571 ++++++++++++++++++++ 3 files changed, 582 insertions(+) create mode 100644 drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index e6f0e514fb1f..928db510b86c 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -104,3 +104,13 @@ config PHY_QCOM_USB_SS help Enable this to support the Super-Speed USB transceiver on various Qualcomm chipsets. + +config PHY_QCOM_IPQ806X_USB + tristate "Qualcomm IPQ806x DWC3 USB PHY driver" + depends on HAS_IOMEM + depends on OF && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + 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. diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index a767915decd0..47acbd7daa3a 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -11,3 +11,4 @@ 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 diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c new file mode 100644 index 000000000000..a7241bf110d7 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB QSCRATCH Hardware registers */ +#define QSCRATCH_GENERAL_CFG (0x08) +#define HSUSB_PHY_CTRL_REG (0x10) + +/* PHY_CTRL_REG */ +#define HSUSB_CTRL_DMSEHV_CLAMP BIT(24) +#define HSUSB_CTRL_USB2_SUSPEND BIT(23) +#define HSUSB_CTRL_UTMI_CLK_EN BIT(21) +#define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20) +#define HSUSB_CTRL_USE_CLKCORE BIT(18) +#define HSUSB_CTRL_DPSEHV_CLAMP BIT(17) +#define HSUSB_CTRL_COMMONONN BIT(11) +#define HSUSB_CTRL_ID_HV_CLAMP BIT(9) +#define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8) +#define HSUSB_CTRL_CLAMP_EN BIT(7) +#define HSUSB_CTRL_RETENABLEN BIT(1) +#define HSUSB_CTRL_POR BIT(0) + +/* QSCRATCH_GENERAL_CFG */ +#define HSUSB_GCFG_XHCI_REV BIT(2) + +/* USB QSCRATCH Hardware registers */ +#define SSUSB_PHY_CTRL_REG (0x00) +#define SSUSB_PHY_PARAM_CTRL_1 (0x04) +#define SSUSB_PHY_PARAM_CTRL_2 (0x08) +#define CR_PROTOCOL_DATA_IN_REG (0x0c) +#define CR_PROTOCOL_DATA_OUT_REG (0x10) +#define CR_PROTOCOL_CAP_ADDR_REG (0x14) +#define CR_PROTOCOL_CAP_DATA_REG (0x18) +#define CR_PROTOCOL_READ_REG (0x1c) +#define CR_PROTOCOL_WRITE_REG (0x20) + +/* PHY_CTRL_REG */ +#define SSUSB_CTRL_REF_USE_PAD BIT(28) +#define SSUSB_CTRL_TEST_POWERDOWN BIT(27) +#define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24) +#define SSUSB_CTRL_SS_PHY_EN BIT(8) +#define SSUSB_CTRL_SS_PHY_RESET BIT(7) + +/* SSPHY control registers - Does this need 0x30? */ +#define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * (lane)) +#define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * (lane)) + +/* SSPHY SoC version specific values */ +#define SSPHY_RX_EQ_VALUE 4 /* Override value for rx_eq */ +/* Override value for transmit preemphasis */ +#define SSPHY_TX_DEEMPH_3_5DB 23 +/* Override value for mpll */ +#define SSPHY_MPLL_VALUE 0 + +/* QSCRATCH PHY_PARAM_CTRL1 fields */ +#define PHY_PARAM_CTRL1_TX_FULL_SWING_MASK GENMASK(26, 19) +#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK GENMASK(19, 13) +#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK GENMASK(13, 7) +#define PHY_PARAM_CTRL1_LOS_BIAS_MASK GENMASK(7, 2) + +#define PHY_PARAM_CTRL1_MASK \ + (PHY_PARAM_CTRL1_TX_FULL_SWING_MASK | \ + PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK | \ + PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK | \ + PHY_PARAM_CTRL1_LOS_BIAS_MASK) + +#define PHY_PARAM_CTRL1_TX_FULL_SWING(x) \ + (((x) << 20) & PHY_PARAM_CTRL1_TX_FULL_SWING_MASK) +#define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x) \ + (((x) << 14) & PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK) +#define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x) \ + (((x) << 8) & PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK) +#define PHY_PARAM_CTRL1_LOS_BIAS(x) \ + (((x) << 3) & PHY_PARAM_CTRL1_LOS_BIAS_MASK) + +/* RX OVRD IN HI bits */ +#define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13) +#define RX_OVRD_IN_HI_RX_RX_RESET BIT(12) +#define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11) +#define RX_OVRD_IN_HI_RX_EQ_MASK GENMASK(10, 7) +#define RX_OVRD_IN_HI_RX_EQ(x) ((x) << 8) +#define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7) +#define RX_OVRD_IN_HI_RX_EQ_EN BIT(6) +#define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5) +#define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK GENMASK(4, 2) +#define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2) +#define RX_OVRD_IN_HI_RX_RATE_MASK GENMASK(2, 0) + +/* TX OVRD DRV LO register bits */ +#define TX_OVRD_DRV_LO_AMPLITUDE_MASK GENMASK(6, 0) +#define TX_OVRD_DRV_LO_PREEMPH_MASK GENMASK(13, 6) +#define TX_OVRD_DRV_LO_PREEMPH(x) ((x) << 7) +#define TX_OVRD_DRV_LO_EN BIT(14) + +/* MPLL bits */ +#define SSPHY_MPLL_MASK GENMASK(8, 5) +#define SSPHY_MPLL(x) ((x) << 5) + +/* SS CAP register bits */ +#define SS_CR_CAP_ADDR_REG BIT(0) +#define SS_CR_CAP_DATA_REG BIT(0) +#define SS_CR_READ_REG BIT(0) +#define SS_CR_WRITE_REG BIT(0) + +struct usb_phy { + void __iomem *base; + struct device *dev; + struct clk *xo_clk; + struct clk *ref_clk; + u32 rx_eq; + u32 tx_deamp_3_5db; + u32 mpll; +}; + +struct phy_drvdata { + struct phy_ops ops; + u32 clk_rate; +}; + +/** + * Write register and read back masked value to confirm it is written + * + * @base - QCOM DWC3 PHY base virtual address. + * @offset - register offset. + * @mask - register bitmask specifying what should be updated + * @val - value to write. + */ +static inline void usb_phy_write_readback(struct usb_phy *phy_dwc3, + u32 offset, + const u32 mask, u32 val) +{ + u32 write_val, tmp = readl(phy_dwc3->base + offset); + + tmp &= ~mask; /* retain other bits */ + write_val = tmp | val; + + writel(write_val, phy_dwc3->base + offset); + + /* Read back to see if val was written */ + tmp = readl(phy_dwc3->base + offset); + tmp &= mask; /* clear other bits */ + + if (tmp != val) + dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", val, offset); +} + +static int wait_for_latch(void __iomem *addr) +{ + u32 retry = 10; + + while (true) { + if (!readl(addr)) + break; + + if (--retry == 0) + return -ETIMEDOUT; + + usleep_range(10, 20); + } + + return 0; +} + +/** + * Write SSPHY register + * + * @base - QCOM DWC3 PHY base virtual address. + * @addr - SSPHY address to write. + * @val - value to write. + */ +static int usb_ss_write_phycreg(struct usb_phy *phy_dwc3, + u32 addr, u32 val) +{ + int ret; + + writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_ADDR_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + if (ret) + goto err_wait; + + writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_DATA_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); + if (ret) + goto err_wait; + + writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); + +err_wait: + if (ret) + dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); + return ret; +} + +/** + * Read SSPHY register. + * + * @base - QCOM DWC3 PHY base virtual address. + * @addr - SSPHY address to read. + */ +static int usb_ss_read_phycreg(struct usb_phy *phy_dwc3, + u32 addr, u32 *val) +{ + int ret; + + writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); + writel(SS_CR_CAP_ADDR_REG, + phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); + if (ret) + goto err_wait; + + /* + * Due to hardware bug, first read of SSPHY register might be + * incorrect. Hence as workaround, SW should perform SSPHY register + * read twice, but use only second read and ignore first read. + */ + writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG); + if (ret) + goto err_wait; + + /* throwaway read */ + readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG); + + writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG); + + ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG); + if (ret) + goto err_wait; + + *val = readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG); + +err_wait: + return ret; +} + +static int qcom_ipq806x_usb_hs_phy_init(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + int ret; + u32 val; + + ret = clk_prepare_enable(phy_dwc3->xo_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy_dwc3->ref_clk); + if (ret) { + clk_disable_unprepare(phy_dwc3->xo_clk); + return ret; + } + + /* + * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel + * enable clamping, and disable RETENTION (power-on default is ENABLED) + */ + val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP | + HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN | + HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP | + HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID | + HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70; + + /* use core clock if external reference is not present */ + if (!phy_dwc3->xo_clk) + val |= HSUSB_CTRL_USE_CLKCORE; + + writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG); + usleep_range(2000, 2200); + + /* Disable (bypass) VBUS and ID filters */ + writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG); + + return 0; +} + +static int qcom_ipq806x_usb_hs_phy_exit(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + + clk_disable_unprepare(phy_dwc3->ref_clk); + clk_disable_unprepare(phy_dwc3->xo_clk); + + return 0; +} + +static int qcom_ipq806x_usb_ss_phy_init(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + int ret; + u32 data; + + ret = clk_prepare_enable(phy_dwc3->xo_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy_dwc3->ref_clk); + if (ret) { + clk_disable_unprepare(phy_dwc3->xo_clk); + return ret; + } + + /* reset phy */ + data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG); + writel(data | SSUSB_CTRL_SS_PHY_RESET, + phy_dwc3->base + SSUSB_PHY_CTRL_REG); + usleep_range(2000, 2200); + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* clear REF_PAD if we don't have XO clk */ + if (!phy_dwc3->xo_clk) + data &= ~SSUSB_CTRL_REF_USE_PAD; + else + data |= SSUSB_CTRL_REF_USE_PAD; + + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* wait for ref clk to become stable, this can take up to 30ms */ + msleep(30); + + data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; + writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); + + /* + * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates + * in HS mode instead of SS mode. Workaround it by asserting + * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode + */ + ret = usb_ss_read_phycreg(phy_dwc3, 0x102D, &data); + if (ret) + goto err_phy_trans; + + data |= (1 << 7); + ret = usb_ss_write_phycreg(phy_dwc3, 0x102D, data); + if (ret) + goto err_phy_trans; + + ret = usb_ss_read_phycreg(phy_dwc3, 0x1010, &data); + if (ret) + goto err_phy_trans; + + data &= ~0xff0; + data |= 0x20; + ret = usb_ss_write_phycreg(phy_dwc3, 0x1010, data); + if (ret) + goto err_phy_trans; + + /* + * Fix RX Equalization setting as follows + * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 + * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 + * LANE0.RX_OVRD_IN_HI.RX_EQ set based on SoC version + * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 + */ + ret = usb_ss_read_phycreg(phy_dwc3, SSPHY_CTRL_RX_OVRD_IN_HI(0), &data); + if (ret) + goto err_phy_trans; + + data &= ~RX_OVRD_IN_HI_RX_EQ_EN; + data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD; + data &= ~RX_OVRD_IN_HI_RX_EQ_MASK; + data |= RX_OVRD_IN_HI_RX_EQ(phy_dwc3->rx_eq); + data |= RX_OVRD_IN_HI_RX_EQ_OVRD; + ret = usb_ss_write_phycreg(phy_dwc3, + SSPHY_CTRL_RX_OVRD_IN_HI(0), data); + if (ret) + goto err_phy_trans; + + /* + * Set EQ and TX launch amplitudes as follows + * LANE0.TX_OVRD_DRV_LO.PREEMPH set based on SoC version + * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 110 + * LANE0.TX_OVRD_DRV_LO.EN set to 1. + */ + ret = usb_ss_read_phycreg(phy_dwc3, + SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data); + if (ret) + goto err_phy_trans; + + data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK; + data |= TX_OVRD_DRV_LO_PREEMPH(phy_dwc3->tx_deamp_3_5db); + data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; + data |= 0x6E; + data |= TX_OVRD_DRV_LO_EN; + ret = usb_ss_write_phycreg(phy_dwc3, + SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); + if (ret) + goto err_phy_trans; + + data = 0; + data &= ~SSPHY_MPLL_MASK; + data |= SSPHY_MPLL(phy_dwc3->mpll); + usb_ss_write_phycreg(phy_dwc3, 0x30, data); + + /* + * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows + * TX_FULL_SWING [26:20] amplitude to 110 + * TX_DEEMPH_6DB [19:14] to 32 + * TX_DEEMPH_3_5DB [13:8] set based on SoC version + * LOS_BIAS [7:3] to 9 + */ + data = readl(phy_dwc3->base + SSUSB_PHY_PARAM_CTRL_1); + + data &= ~PHY_PARAM_CTRL1_MASK; + + data |= PHY_PARAM_CTRL1_TX_FULL_SWING(0x6e) | + PHY_PARAM_CTRL1_TX_DEEMPH_6DB(0x20) | + PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(phy_dwc3->tx_deamp_3_5db) | + PHY_PARAM_CTRL1_LOS_BIAS(0x9); + + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, + PHY_PARAM_CTRL1_MASK, data); + +err_phy_trans: + return ret; +} + +static int qcom_ipq806x_usb_ss_phy_exit(struct phy *phy) +{ + struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); + + /* Sequence to put SSPHY in low power state: + * 1. Clear REF_PHY_EN in PHY_CTRL_REG + * 2. Clear REF_USE_PAD in PHY_CTRL_REG + * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention + */ + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_SS_PHY_EN, 0x0); + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_REF_USE_PAD, 0x0); + usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, + SSUSB_CTRL_TEST_POWERDOWN, 0x0); + + clk_disable_unprepare(phy_dwc3->ref_clk); + clk_disable_unprepare(phy_dwc3->xo_clk); + + return 0; +} + +static const struct phy_drvdata qcom_ipq806x_usb_hs_drvdata = { + .ops = { + .init = qcom_ipq806x_usb_hs_phy_init, + .exit = qcom_ipq806x_usb_hs_phy_exit, + .owner = THIS_MODULE, + }, + .clk_rate = 60000000, +}; + +static const struct phy_drvdata qcom_ipq806x_usb_ss_drvdata = { + .ops = { + .init = qcom_ipq806x_usb_ss_phy_init, + .exit = qcom_ipq806x_usb_ss_phy_exit, + .owner = THIS_MODULE, + }, + .clk_rate = 125000000, +}; + +static const struct of_device_id qcom_ipq806x_usb_phy_table[] = { + { .compatible = "qcom,ipq806x-usb-phy-hs", + .data = &qcom_ipq806x_usb_hs_drvdata }, + { .compatible = "qcom,ipq806x-usb-phy-ss", + .data = &qcom_ipq806x_usb_ss_drvdata }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, qcom_ipq806x_usb_phy_table); + +static int qcom_ipq806x_usb_phy_probe(struct platform_device *pdev) +{ + struct resource *res; + resource_size_t size; + struct phy *generic_phy; + struct usb_phy *phy_dwc3; + const struct phy_drvdata *data; + struct phy_provider *phy_provider; + + phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); + if (!phy_dwc3) + return -ENOMEM; + + data = of_device_get_match_data(&pdev->dev); + + phy_dwc3->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + size = resource_size(res); + phy_dwc3->base = devm_ioremap(phy_dwc3->dev, res->start, size); + + if (IS_ERR(phy_dwc3->base)) { + dev_err(phy_dwc3->dev, "failed to map reg\n"); + return PTR_ERR(phy_dwc3->base); + } + + phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref"); + if (IS_ERR(phy_dwc3->ref_clk)) { + dev_dbg(phy_dwc3->dev, "cannot get reference clock\n"); + return PTR_ERR(phy_dwc3->ref_clk); + } + + clk_set_rate(phy_dwc3->ref_clk, data->clk_rate); + + phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo"); + if (IS_ERR(phy_dwc3->xo_clk)) { + dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n"); + phy_dwc3->xo_clk = NULL; + } + + /* Parse device node to probe HSIO settings */ + if (device_property_read_u32(&pdev->dev, "qcom,rx-eq", + &phy_dwc3->rx_eq)) + phy_dwc3->rx_eq = SSPHY_RX_EQ_VALUE; + + if (device_property_read_u32(&pdev->dev, "qcom,tx-deamp_3_5db", + &phy_dwc3->tx_deamp_3_5db)) + phy_dwc3->rx_eq = SSPHY_TX_DEEMPH_3_5DB; + + if (device_property_read_u32(&pdev->dev, "qcom,mpll", &phy_dwc3->mpll)) + phy_dwc3->mpll = SSPHY_MPLL_VALUE; + + generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, &data->ops); + + if (IS_ERR(generic_phy)) + return PTR_ERR(generic_phy); + + phy_set_drvdata(generic_phy, phy_dwc3); + platform_set_drvdata(pdev, phy_dwc3); + + phy_provider = devm_of_phy_provider_register(phy_dwc3->dev, + of_phy_simple_xlate); + + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +static struct platform_driver qcom_ipq806x_usb_phy_driver = { + .probe = qcom_ipq806x_usb_phy_probe, + .driver = { + .name = "qcom-ipq806x-usb-phy", + .owner = THIS_MODULE, + .of_match_table = qcom_ipq806x_usb_phy_table, + }, +}; + +module_platform_driver(qcom_ipq806x_usb_phy_driver); + +MODULE_ALIAS("platform:phy-qcom-ipq806x-usb"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Andy Gross "); +MODULE_AUTHOR("Ivan T. Ivanov "); +MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); From dc171790c729db72774fd076485b86f92954544b Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Fri, 17 Jul 2020 15:16:32 +0200 Subject: [PATCH 36/46] devicetree: bindings: phy: Document ipq806x dwc3 qcom phy Document dwc3 qcom phy hs and ss phy bindings needed to correctly inizialize and use usb on ipq806x SoC. Signed-off-by: Ansuel Smith Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200717131635.11076-2-ansuelsmth@gmail.com Signed-off-by: Vinod Koul --- .../bindings/phy/qcom,ipq806x-usb-phy-hs.yaml | 55 ++++++++++++++ .../bindings/phy/qcom,ipq806x-usb-phy-ss.yaml | 73 +++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-hs.yaml create mode 100644 Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-ss.yaml diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-hs.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-hs.yaml new file mode 100644 index 000000000000..23887ebe08fd --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-hs.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,ipq806x-usb-phy-hs.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm ipq806x usb DWC3 HS PHY CONTROLLER + +maintainers: + - Ansuel Smith + +description: + DWC3 PHY nodes are defined to describe on-chip Synopsis Physical layer + controllers used in ipq806x. Each DWC3 PHY controller should have its + own node. + +properties: + compatible: + const: qcom,ipq806x-usb-phy-hs + + "#phy-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: ref + - const: xo + +required: + - compatible + - "#phy-cells" + - reg + - clocks + - clock-names + +examples: + - | + #include + + hs_phy_0: phy@110f8800 { + compatible = "qcom,ipq806x-usb-phy-hs"; + reg = <0x110f8800 0x30>; + clocks = <&gcc USB30_0_UTMI_CLK>; + clock-names = "ref"; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-ss.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-ss.yaml new file mode 100644 index 000000000000..fa30c24b4405 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,ipq806x-usb-phy-ss.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,ipq806x-usb-phy-ss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm ipq806x usb DWC3 SS PHY CONTROLLER + +maintainers: + - Ansuel Smith + +description: + DWC3 PHY nodes are defined to describe on-chip Synopsis Physical layer + controllers used in ipq806x. Each DWC3 PHY controller should have its + own node. + +properties: + compatible: + const: qcom,ipq806x-usb-phy-ss + + "#phy-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: ref + - const: xo + + qcom,rx-eq: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Override value for rx_eq. + default: 4 + maximum: 7 + + qcom,tx-deamp-3_5db: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Override value for transmit preemphasis. + default: 23 + maximum: 63 + + qcom,mpll: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Override value for mpll. + default: 0 + maximum: 7 + +required: + - compatible + - "#phy-cells" + - reg + - clocks + - clock-names + +examples: + - | + #include + + ss_phy_0: phy@110f8830 { + compatible = "qcom,ipq806x-usb-phy-ss"; + reg = <0x110f8830 0x30>; + clocks = <&gcc USB30_0_MASTER_CLK>; + clock-names = "ref"; + #phy-cells = <0>; + }; From 08b0ad375ca66181faee725b1b358bcae8d592ee Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 17 Jul 2020 20:44:56 +0900 Subject: [PATCH 37/46] phy: renesas: rcar-gen3-usb2: move irq registration to init If CONFIG_DEBUG_SHIRQ was enabled, r8a77951-salvator-xs could boot correctly. If we appended "earlycon keep_bootcon" to the kernel command like, we could get kernel log like below. SError Interrupt on CPU0, code 0xbf000002 -- SError CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.8.0-rc3-salvator-x-00505-g6c843129e6faaf01 #785 Hardware name: Renesas Salvator-X 2nd version board based on r8a77951 (DT) pstate: 60400085 (nZCv daIf +PAN -UAO BTYPE=--) pc : rcar_gen3_phy_usb2_irq+0x14/0x54 lr : free_irq+0xf4/0x27c This means free_irq() calls the interrupt handler while PM runtime is not getting if DEBUG_SHIRQ is enabled and rcar_gen3_phy_usb2_probe() failed. To fix the issue, move the irq registration place to rcar_gen3_phy_usb2_init() which is ready to handle the interrupts. Note that after the commit 549b6b55b005 ("phy: renesas: rcar-gen3-usb2: enable/disable independent irqs") which is merged into v5.2, since this driver creates multiple phy instances, needs to check whether one of phy instances is initialized. However, if we backport this patch to v5.1 or less, we don't need to check it because such kernel have single phy instance. Reported-by: Wolfram Sang Reported-by: Geert Uytterhoeven Fixes: 9f391c574efc ("phy: rcar-gen3-usb2: add runtime ID/VBUS pin detection") Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/1594986297-12434-2-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 61 +++++++++++++----------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index bfb22f868857..5087b7c44d55 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -111,6 +111,7 @@ struct rcar_gen3_chan { struct work_struct work; struct mutex lock; /* protects rphys[...].powered */ enum usb_dr_mode dr_mode; + int irq; bool extcon_host; bool is_otg_channel; bool uses_otg_pins; @@ -389,12 +390,38 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) rcar_gen3_device_recognition(ch); } +static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) +{ + struct rcar_gen3_chan *ch = _ch; + void __iomem *usb2_base = ch->base; + u32 status = readl(usb2_base + USB2_OBINTSTA); + irqreturn_t ret = IRQ_NONE; + + if (status & USB2_OBINT_BITS) { + dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); + writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); + rcar_gen3_device_recognition(ch); + ret = IRQ_HANDLED; + } + + return ret; +} + static int rcar_gen3_phy_usb2_init(struct phy *p) { struct rcar_gen3_phy *rphy = phy_get_drvdata(p); struct rcar_gen3_chan *channel = rphy->ch; void __iomem *usb2_base = channel->base; u32 val; + int ret; + + if (!rcar_gen3_is_any_rphy_initialized(channel) && channel->irq >= 0) { + INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); + ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq, + IRQF_SHARED, dev_name(channel->dev), channel); + if (ret < 0) + dev_err(channel->dev, "No irq handler (%d)\n", channel->irq); + } /* Initialize USB2 part */ val = readl(usb2_base + USB2_INT_ENABLE); @@ -433,6 +460,9 @@ static int rcar_gen3_phy_usb2_exit(struct phy *p) val &= ~USB2_INT_ENABLE_UCOM_INTEN; writel(val, usb2_base + USB2_INT_ENABLE); + if (channel->irq >= 0 && !rcar_gen3_is_any_rphy_initialized(channel)) + free_irq(channel->irq, channel); + return 0; } @@ -503,23 +533,6 @@ static const struct phy_ops rz_g1c_phy_usb2_ops = { .owner = THIS_MODULE, }; -static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) -{ - struct rcar_gen3_chan *ch = _ch; - void __iomem *usb2_base = ch->base; - u32 status = readl(usb2_base + USB2_OBINTSTA); - irqreturn_t ret = IRQ_NONE; - - if (status & USB2_OBINT_BITS) { - dev_vdbg(ch->dev, "%s: %08x\n", __func__, status); - writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); - rcar_gen3_device_recognition(ch); - ret = IRQ_HANDLED; - } - - return ret; -} - static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { { .compatible = "renesas,usb2-phy-r8a77470", @@ -598,7 +611,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) struct phy_provider *provider; struct resource *res; const struct phy_ops *phy_usb2_ops; - int irq, ret = 0, i; + int ret = 0, i; if (!dev->of_node) { dev_err(dev, "This driver needs device tree\n"); @@ -614,16 +627,8 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) if (IS_ERR(channel->base)) return PTR_ERR(channel->base); - /* call request_irq for OTG */ - irq = platform_get_irq_optional(pdev, 0); - if (irq >= 0) { - INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); - irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, - IRQF_SHARED, dev_name(dev), channel); - if (irq < 0) - dev_err(dev, "No irq handler (%d)\n", irq); - } - + /* get irq number here and request_irq for OTG in phy_init */ + channel->irq = platform_get_irq_optional(pdev, 0); channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { int ret; From b59aeb1a59ccece01f4f0c675d18cfac9cc825fb Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 17 Jul 2020 20:44:57 +0900 Subject: [PATCH 38/46] phy: renesas: rcar-gen3-usb2: exit if request_irq() failed To avoid unexpected behaviors, it's better to exit if request_irq() failed. Suggested-by: Vinod Koul Signed-off-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/1594986297-12434-3-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Vinod Koul --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 5087b7c44d55..e34e4475027c 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -419,8 +419,10 @@ static int rcar_gen3_phy_usb2_init(struct phy *p) INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); ret = request_irq(channel->irq, rcar_gen3_phy_usb2_irq, IRQF_SHARED, dev_name(channel->dev), channel); - if (ret < 0) + if (ret < 0) { dev_err(channel->dev, "No irq handler (%d)\n", channel->irq); + return ret; + } } /* Initialize USB2 part */ From f199d6a296dce05ba3de40865251320371e68e3d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 16 Jul 2020 18:18:20 +0100 Subject: [PATCH 39/46] dt-bindings: phy: renesas,usb2-phy: Add r8a774e1 support Document SoC specific bindings for RZ/G2H (r8a774e1) SoC. Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Link: https://lore.kernel.org/r/1594919915-5225-6-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml index 440f09fddf93..829e8c7e467a 100644 --- a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml @@ -21,6 +21,7 @@ properties: - renesas,usb2-phy-r8a774a1 # RZ/G2M - renesas,usb2-phy-r8a774b1 # RZ/G2N - renesas,usb2-phy-r8a774c0 # RZ/G2E + - renesas,usb2-phy-r8a774e1 # RZ/G2H - renesas,usb2-phy-r8a7795 # R-Car H3 - renesas,usb2-phy-r8a7796 # R-Car M3-W - renesas,usb2-phy-r8a77961 # R-Car M3-W+ From f13b89900d364ebb72ec2011dd0cf61b7c3bb3d5 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 16 Jul 2020 18:18:24 +0100 Subject: [PATCH 40/46] dt-bindings: phy: renesas,usb3-phy: Add r8a774e1 support Document RZ/G2H (R8A774E1) SoC bindings. Signed-off-by: Lad Prabhakar Reviewed-by: Marian-Cristian Rotariu Link: https://lore.kernel.org/r/1594919915-5225-10-git-send-email-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml index 68cf9dd0390d..f3ef738a3ff6 100644 --- a/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml +++ b/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml @@ -15,6 +15,7 @@ properties: - enum: - renesas,r8a774a1-usb3-phy # RZ/G2M - renesas,r8a774b1-usb3-phy # RZ/G2N + - renesas,r8a774e1-usb3-phy # RZ/G2H - renesas,r8a7795-usb3-phy # R-Car H3 - renesas,r8a7796-usb3-phy # R-Car M3-W - renesas,r8a77961-usb3-phy # R-Car M3-W+ From e9f84ec63c6f0f2fecd3c5bdec0178bb927c0c77 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 20 Jul 2020 08:27:18 -0500 Subject: [PATCH 41/46] phy: samsung-ufs: Fix IS_ERR argument Fix IS_ERR argument in samsung_ufs_phy_symbol_clk_init(). The proper argument to be passed to IS_ERR() is phy->rx1_symbol_clk. This bug was detected with the help of Coccinelle. Fixes: bca21e930451 ("phy: samsung-ufs: add UFS PHY driver for samsung SoC") Signed-off-by: Gustavo A. R. Silva Reviewed-by: Alim Akhtar Link: https://lore.kernel.org/r/20200720132718.GA13413@embeddedor Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-samsung-ufs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c index 43ef77d1d96c..9832599a0283 100644 --- a/drivers/phy/samsung/phy-samsung-ufs.c +++ b/drivers/phy/samsung/phy-samsung-ufs.c @@ -147,7 +147,7 @@ static int samsung_ufs_phy_symbol_clk_init(struct samsung_ufs_phy *phy) } phy->rx1_symbol_clk = devm_clk_get(phy->dev, "rx1_symbol_clk"); - if (IS_ERR(phy->rx0_symbol_clk)) { + if (IS_ERR(phy->rx1_symbol_clk)) { dev_err(phy->dev, "failed to get rx1_symbol_clk clock\n"); return PTR_ERR(phy->rx1_symbol_clk); } From 6c89533deeb36a8f2a7571e13d338edd2c76d473 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Jul 2020 15:40:33 +0100 Subject: [PATCH 42/46] dt: update Marvell Armada 38x COMPHY binding Update the Marvell Armada 38x COMPHY binding with an additional optional register pair describing the location of an undocumented system register controlling something to do with the Gigabit Ethernet and COMPHY. There is one bit for each COMPHY lane that may be using the serdes, but exactly what this register does is completely unknown. This register only appears to exist on Armada 38x devices, and not other SoCs using the NETA ethernet block, so it seems logical that it should be part of the COMPHY. This is also how u-boot groups this register; it is dealt with as part of the COMPHY initialisation there. However, at the end of the day, due to the undocumented nature of this register, we can only guess. Signed-off-by: Russell King Acked-by: Rob Herring Link: https://lore.kernel.org/r/E1jxtRZ-0003Ta-4h@rmk-PC.armlinux.org.uk Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/phy-armada38x-comphy.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt b/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt index ad49e5c01334..8b5a7a28a35b 100644 --- a/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt +++ b/Documentation/devicetree/bindings/phy/phy-armada38x-comphy.txt @@ -12,6 +12,13 @@ Required properties: - #address-cells: should be 1. - #size-cells: should be 0. +Optional properties: + +- reg-names: must be "comphy" as the first name, and "conf". +- reg: must contain the comphy register location and length as the first + pair, followed by an optional configuration register address and + length pair. + A sub-node is required for each comphy lane provided by the comphy. Required properties (child nodes): @@ -24,7 +31,8 @@ Example: comphy: phy@18300 { compatible = "marvell,armada-380-comphy"; - reg = <0x18300 0x100>; + reg-names = "comphy", "conf"; + reg = <0x18300 0x100>, <0x18460 4>; #address-cells = <1>; #size-cells = <0>; From 1dea06cd643da38931382ebdc151efced201ffad Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 21 Jul 2020 15:40:43 +0100 Subject: [PATCH 43/46] phy: armada-38x: fix NETA lockup when repeatedly switching speeds The mvneta hardware appears to lock up in various random ways when repeatedly switching speeds between 1G and 2.5G, which involves reprogramming the COMPHY. It is not entirely clear why this happens, but best guess is that reprogramming the COMPHY glitches mvneta clocks causing the hardware to fail. It seems that rebooting resolves the failure, but not down/up cycling the interface alone. Various other approaches have been tried, such as trying to cleanly power down the COMPHY and then take it back through the power up initialisation, but this does not seem to help. It was finally noticed that u-boot's last step when configuring a COMPHY for "SGMII" mode was to poke at a register described as "GBE_CONFIGURATION_REG", which is undocumented in any external documentation. All that we have is the fact that u-boot sets a bit corresponding to the "SGMII" lane at the end of COMPHY initialisation. Experimentation shows that if we clear this bit prior to changing the speed, and then set it afterwards, mvneta does not suffer this problem on the SolidRun Clearfog when switching speeds between 1G and 2.5G. This problem was found while script-testing phylink. This fix also requires the corresponding change to DT to be effective. See "ARM: dts: armada-38x: fix NETA lockup when repeatedly switching speeds". Fixes: 14dc100b4411 ("phy: armada38x: add common phy support") Signed-off-by: Russell King Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/E1jxtRj-0003Tz-CG@rmk-PC.armlinux.org.uk Signed-off-by: Vinod Koul --- drivers/phy/marvell/phy-armada38x-comphy.c | 45 ++++++++++++++++++---- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c index 6960dfd8ad8c..0fe408964334 100644 --- a/drivers/phy/marvell/phy-armada38x-comphy.c +++ b/drivers/phy/marvell/phy-armada38x-comphy.c @@ -41,6 +41,7 @@ struct a38x_comphy_lane { struct a38x_comphy { void __iomem *base; + void __iomem *conf; struct device *dev; struct a38x_comphy_lane lane[MAX_A38X_COMPHY]; }; @@ -54,6 +55,21 @@ static const u8 gbe_mux[MAX_A38X_COMPHY][MAX_A38X_PORTS] = { { 0, 0, 3 }, }; +static void a38x_set_conf(struct a38x_comphy_lane *lane, bool enable) +{ + struct a38x_comphy *priv = lane->priv; + u32 conf; + + if (priv->conf) { + conf = readl_relaxed(priv->conf); + if (enable) + conf |= BIT(lane->port); + else + conf &= ~BIT(lane->port); + writel(conf, priv->conf); + } +} + static void a38x_comphy_set_reg(struct a38x_comphy_lane *lane, unsigned int offset, u32 mask, u32 value) { @@ -97,6 +113,7 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) { struct a38x_comphy_lane *lane = phy_get_drvdata(phy); unsigned int gen; + int ret; if (mode != PHY_MODE_ETHERNET) return -EINVAL; @@ -115,13 +132,20 @@ static int a38x_comphy_set_mode(struct phy *phy, enum phy_mode mode, int sub) return -EINVAL; } + a38x_set_conf(lane, false); + a38x_comphy_set_speed(lane, gen, gen); - return a38x_comphy_poll(lane, COMPHY_STAT1, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX, - COMPHY_STAT1_PLL_RDY_TX | - COMPHY_STAT1_PLL_RDY_RX); + ret = a38x_comphy_poll(lane, COMPHY_STAT1, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX, + COMPHY_STAT1_PLL_RDY_TX | + COMPHY_STAT1_PLL_RDY_RX); + + if (ret == 0) + a38x_set_conf(lane, true); + + return ret; } static const struct phy_ops a38x_comphy_ops = { @@ -174,14 +198,21 @@ static int a38x_comphy_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); priv->dev = &pdev->dev; priv->base = base; + /* Optional */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf"); + if (res) { + priv->conf = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->conf)) + return PTR_ERR(priv->conf); + } + for_each_available_child_of_node(pdev->dev.of_node, child) { struct phy *phy; int ret; From 960004121075241330457a3306ad735dd27d76c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 20 Jul 2020 15:12:08 +0200 Subject: [PATCH 44/46] dt-bindings: phy: add bcm63xx-usbh bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document BCM63xx USBH PHY bindings. Signed-off-by: Álvaro Fernández Rojas Reviewed-by: Florian Fainelli Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200720131209.1236590-2-noltari@gmail.com Signed-off-by: Vinod Koul --- .../bindings/phy/brcm,bcm63xx-usbh-phy.yaml | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/brcm,bcm63xx-usbh-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/brcm,bcm63xx-usbh-phy.yaml b/Documentation/devicetree/bindings/phy/brcm,bcm63xx-usbh-phy.yaml new file mode 100644 index 000000000000..9a2e779e6d38 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,bcm63xx-usbh-phy.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/brcm,bcm63xx-usbh-phy.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: BCM63xx USBH PHY + +maintainers: + - Álvaro Fernández Rojas + +properties: + compatible: + enum: + - brcm,bcm6318-usbh-phy + - brcm,bcm6328-usbh-phy + - brcm,bcm6358-usbh-phy + - brcm,bcm6362-usbh-phy + - brcm,bcm6368-usbh-phy + - brcm,bcm63268-usbh-phy + + reg: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + maxItems: 2 + items: + - const: usbh + - const: usb_ref + + resets: + maxItems: 1 + + "#phy-cells": + const: 1 + +additionalProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - "#phy-cells" + +if: + properties: + compatible: + enum: + - brcm,bcm6318-usbh-phy + - brcm,bcm6328-usbh-phy + - brcm,bcm6362-usbh-phy + - brcm,bcm63268-usbh-phy +then: + properties: + power-domains: + maxItems: 1 + required: + - power-domains +else: + properties: + power-domains: false + +examples: + - | + usbh: usb-phy@10001700 { + compatible = "brcm,bcm6368-usbh-phy"; + reg = <0x10001700 0x38>; + clocks = <&periph_clk 15>; + clock-names = "usbh"; + resets = <&periph_rst 12>; + #phy-cells = <1>; + }; From 783f6d3dcf35f7b63783756710bebcecb3067f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Mon, 20 Jul 2020 15:12:09 +0200 Subject: [PATCH 45/46] phy: bcm63xx-usbh: Add BCM63xx USBH driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add BCM63xx USBH PHY driver for BMIPS. Signed-off-by: Álvaro Fernández Rojas Signed-off-by: Simon Arlott Link: https://lore.kernel.org/r/20200720131209.1236590-3-noltari@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/broadcom/Kconfig | 8 + drivers/phy/broadcom/Makefile | 1 + drivers/phy/broadcom/phy-bcm63xx-usbh.c | 457 ++++++++++++++++++++++++ 3 files changed, 466 insertions(+) create mode 100644 drivers/phy/broadcom/phy-bcm63xx-usbh.c diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index b29f11c19155..a1f1a9c90d0d 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -2,6 +2,14 @@ # # Phy drivers for Broadcom platforms # +config PHY_BCM63XX_USBH + tristate "BCM63xx USBH PHY driver" + depends on BMIPS_GENERIC || COMPILE_TEST + select GENERIC_PHY + help + Enable this to support the BCM63xx USBH PHY driver. + If unsure, say N. + config PHY_CYGNUS_PCIE tristate "Broadcom Cygnus PCIe PHY driver" depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile index c78de546135c..7024127f86ad 100644 --- a/drivers/phy/broadcom/Makefile +++ b/drivers/phy/broadcom/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_BCM63XX_USBH) += phy-bcm63xx-usbh.o obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o diff --git a/drivers/phy/broadcom/phy-bcm63xx-usbh.c b/drivers/phy/broadcom/phy-bcm63xx-usbh.c new file mode 100644 index 000000000000..6c05ba8b08be --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm63xx-usbh.c @@ -0,0 +1,457 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * BCM6328 USBH PHY Controller Driver + * + * Copyright (C) 2020 Álvaro Fernández Rojas + * Copyright (C) 2015 Simon Arlott + * + * Derived from bcm963xx_4.12L.06B_consumer/kernel/linux/arch/mips/bcm963xx/setup.c: + * Copyright (C) 2002 Broadcom Corporation + * + * Derived from OpenWrt patches: + * Copyright (C) 2013 Jonas Gorski + * Copyright (C) 2013 Florian Fainelli + * Copyright (C) 2008 Maxime Bizon + */ + +#include +#include +#include +#include +#include +#include + +/* USBH control register offsets */ +enum usbh_regs { + USBH_BRT_CONTROL1 = 0, + USBH_BRT_CONTROL2, + USBH_BRT_STATUS1, + USBH_BRT_STATUS2, + USBH_UTMI_CONTROL1, +#define USBH_UC1_DEV_MODE_SEL BIT(0) + USBH_TEST_PORT_CONTROL, + USBH_PLL_CONTROL1, +#define USBH_PLLC_REFCLKSEL_SHIFT 0 +#define USBH_PLLC_REFCLKSEL_MASK (0x3 << USBH_PLLC_REFCLKSEL_SHIFT) +#define USBH_PLLC_CLKSEL_SHIFT 2 +#define USBH_PLLC_CLKSEL_MASK (0x3 << USBH_PLLC_CLKSEL_MASK) +#define USBH_PLLC_XTAL_PWRDWNB BIT(4) +#define USBH_PLLC_PLL_PWRDWNB BIT(5) +#define USBH_PLLC_PLL_CALEN BIT(6) +#define USBH_PLLC_PHYPLL_BYP BIT(7) +#define USBH_PLLC_PLL_RESET BIT(8) +#define USBH_PLLC_PLL_IDDQ_PWRDN BIT(9) +#define USBH_PLLC_PLL_PWRDN_DELAY BIT(10) +#define USBH_6318_PLLC_PLL_SUSPEND_EN BIT(27) +#define USBH_6318_PLLC_PHYPLL_BYP BIT(29) +#define USBH_6318_PLLC_PLL_RESET BIT(30) +#define USBH_6318_PLLC_PLL_IDDQ_PWRDN BIT(31) + USBH_SWAP_CONTROL, +#define USBH_SC_OHCI_DATA_SWAP BIT(0) +#define USBH_SC_OHCI_ENDIAN_SWAP BIT(1) +#define USBH_SC_OHCI_LOGICAL_ADDR_EN BIT(2) +#define USBH_SC_EHCI_DATA_SWAP BIT(3) +#define USBH_SC_EHCI_ENDIAN_SWAP BIT(4) +#define USBH_SC_EHCI_LOGICAL_ADDR_EN BIT(5) +#define USBH_SC_USB_DEVICE_SEL BIT(6) + USBH_GENERIC_CONTROL, +#define USBH_GC_PLL_SUSPEND_EN BIT(1) + USBH_FRAME_ADJUST_VALUE, + USBH_SETUP, +#define USBH_S_IOC BIT(4) +#define USBH_S_IPP BIT(5) + USBH_MDIO, + USBH_MDIO32, + USBH_USB_SIM_CONTROL, +#define USBH_USC_LADDR_SEL BIT(5) + + __USBH_ENUM_SIZE +}; + +struct bcm63xx_usbh_phy_variant { + /* Registers */ + long regs[__USBH_ENUM_SIZE]; + + /* PLLC bits to set/clear for power on */ + u32 power_pllc_clr; + u32 power_pllc_set; + + /* Setup bits to set/clear for power on */ + u32 setup_clr; + u32 setup_set; + + /* Swap Control bits to set */ + u32 swapctl_dev_set; + + /* Test Port Control value to set if non-zero */ + u32 tpc_val; + + /* USB Sim Control bits to set */ + u32 usc_set; + + /* UTMI Control 1 bits to set */ + u32 utmictl1_dev_set; +}; + +struct bcm63xx_usbh_phy { + void __iomem *base; + struct clk *usbh_clk; + struct clk *usb_ref_clk; + struct reset_control *reset; + const struct bcm63xx_usbh_phy_variant *variant; + bool device_mode; +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6318 = { + .regs = { + [USBH_BRT_CONTROL1] = -1, + [USBH_BRT_CONTROL2] = -1, + [USBH_BRT_STATUS1] = -1, + [USBH_BRT_STATUS2] = -1, + [USBH_UTMI_CONTROL1] = 0x2c, + [USBH_TEST_PORT_CONTROL] = 0x1c, + [USBH_PLL_CONTROL1] = 0x04, + [USBH_SWAP_CONTROL] = 0x0c, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = 0x08, + [USBH_SETUP] = 0x00, + [USBH_MDIO] = 0x14, + [USBH_MDIO32] = 0x18, + [USBH_USB_SIM_CONTROL] = 0x20, + }, + .power_pllc_clr = USBH_6318_PLLC_PLL_IDDQ_PWRDN, + .power_pllc_set = USBH_6318_PLLC_PLL_SUSPEND_EN, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .usc_set = USBH_USC_LADDR_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6328 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = 0x20, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6358 = { + .regs = { + [USBH_BRT_CONTROL1] = -1, + [USBH_BRT_CONTROL2] = -1, + [USBH_BRT_STATUS1] = -1, + [USBH_BRT_STATUS2] = -1, + [USBH_UTMI_CONTROL1] = -1, + [USBH_TEST_PORT_CONTROL] = 0x24, + [USBH_PLL_CONTROL1] = -1, + [USBH_SWAP_CONTROL] = 0x00, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = -1, + [USBH_SETUP] = -1, + [USBH_MDIO] = -1, + [USBH_MDIO32] = -1, + [USBH_USB_SIM_CONTROL] = -1, + }, + /* + * The magic value comes for the original vendor BSP + * and is needed for USB to work. Datasheet does not + * help, so the magic value is used as-is. + */ + .tpc_val = 0x1c0020, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm6368 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = -1, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static const struct bcm63xx_usbh_phy_variant usbh_bcm63268 = { + .regs = { + [USBH_BRT_CONTROL1] = 0x00, + [USBH_BRT_CONTROL2] = 0x04, + [USBH_BRT_STATUS1] = 0x08, + [USBH_BRT_STATUS2] = 0x0c, + [USBH_UTMI_CONTROL1] = 0x10, + [USBH_TEST_PORT_CONTROL] = 0x14, + [USBH_PLL_CONTROL1] = 0x18, + [USBH_SWAP_CONTROL] = 0x1c, + [USBH_GENERIC_CONTROL] = 0x20, + [USBH_FRAME_ADJUST_VALUE] = 0x24, + [USBH_SETUP] = 0x28, + [USBH_MDIO] = 0x2c, + [USBH_MDIO32] = 0x30, + [USBH_USB_SIM_CONTROL] = 0x34, + }, + .power_pllc_clr = USBH_PLLC_PLL_IDDQ_PWRDN | USBH_PLLC_PLL_PWRDN_DELAY, + .setup_clr = USBH_S_IPP, + .setup_set = USBH_S_IOC, + .swapctl_dev_set = USBH_SC_USB_DEVICE_SEL, + .utmictl1_dev_set = USBH_UC1_DEV_MODE_SEL, +}; + +static inline bool usbh_has_reg(struct bcm63xx_usbh_phy *usbh, int reg) +{ + return (usbh->variant->regs[reg] >= 0); +} + +static inline u32 usbh_readl(struct bcm63xx_usbh_phy *usbh, int reg) +{ + return __raw_readl(usbh->base + usbh->variant->regs[reg]); +} + +static inline void usbh_writel(struct bcm63xx_usbh_phy *usbh, int reg, + u32 value) +{ + __raw_writel(value, usbh->base + usbh->variant->regs[reg]); +} + +static int bcm63xx_usbh_phy_init(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(usbh->usbh_clk); + if (ret) { + dev_err(&phy->dev, "unable to enable usbh clock: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(usbh->usb_ref_clk); + if (ret) { + dev_err(&phy->dev, "unable to enable usb_ref clock: %d\n", ret); + clk_disable_unprepare(usbh->usbh_clk); + return ret; + } + + ret = reset_control_reset(usbh->reset); + if (ret) { + dev_err(&phy->dev, "unable to reset device: %d\n", ret); + clk_disable_unprepare(usbh->usb_ref_clk); + clk_disable_unprepare(usbh->usbh_clk); + return ret; + } + + /* Configure to work in native CPU endian */ + if (usbh_has_reg(usbh, USBH_SWAP_CONTROL)) { + u32 val = usbh_readl(usbh, USBH_SWAP_CONTROL); + + val |= USBH_SC_EHCI_DATA_SWAP; + val &= ~USBH_SC_EHCI_ENDIAN_SWAP; + + val |= USBH_SC_OHCI_DATA_SWAP; + val &= ~USBH_SC_OHCI_ENDIAN_SWAP; + + if (usbh->device_mode && usbh->variant->swapctl_dev_set) + val |= usbh->variant->swapctl_dev_set; + + usbh_writel(usbh, USBH_SWAP_CONTROL, val); + } + + if (usbh_has_reg(usbh, USBH_SETUP)) { + u32 val = usbh_readl(usbh, USBH_SETUP); + + val |= usbh->variant->setup_set; + val &= ~usbh->variant->setup_clr; + + usbh_writel(usbh, USBH_SETUP, val); + } + + if (usbh_has_reg(usbh, USBH_USB_SIM_CONTROL)) { + u32 val = usbh_readl(usbh, USBH_USB_SIM_CONTROL); + + val |= usbh->variant->usc_set; + + usbh_writel(usbh, USBH_USB_SIM_CONTROL, val); + } + + if (usbh->variant->tpc_val && + usbh_has_reg(usbh, USBH_TEST_PORT_CONTROL)) + usbh_writel(usbh, USBH_TEST_PORT_CONTROL, + usbh->variant->tpc_val); + + if (usbh->device_mode && + usbh_has_reg(usbh, USBH_UTMI_CONTROL1) && + usbh->variant->utmictl1_dev_set) { + u32 val = usbh_readl(usbh, USBH_UTMI_CONTROL1); + + val |= usbh->variant->utmictl1_dev_set; + + usbh_writel(usbh, USBH_UTMI_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_power_on(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) { + u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1); + + val |= usbh->variant->power_pllc_set; + val &= ~usbh->variant->power_pllc_clr; + + usbh_writel(usbh, USBH_PLL_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_power_off(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + if (usbh_has_reg(usbh, USBH_PLL_CONTROL1)) { + u32 val = usbh_readl(usbh, USBH_PLL_CONTROL1); + + val &= ~usbh->variant->power_pllc_set; + val |= usbh->variant->power_pllc_clr; + + usbh_writel(usbh, USBH_PLL_CONTROL1, val); + } + + return 0; +} + +static int bcm63xx_usbh_phy_exit(struct phy *phy) +{ + struct bcm63xx_usbh_phy *usbh = phy_get_drvdata(phy); + + clk_disable_unprepare(usbh->usbh_clk); + clk_disable_unprepare(usbh->usb_ref_clk); + + return 0; +} + +static const struct phy_ops bcm63xx_usbh_phy_ops = { + .exit = bcm63xx_usbh_phy_exit, + .init = bcm63xx_usbh_phy_init, + .power_off = bcm63xx_usbh_phy_power_off, + .power_on = bcm63xx_usbh_phy_power_on, + .owner = THIS_MODULE, +}; + +static struct phy *bcm63xx_usbh_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct bcm63xx_usbh_phy *usbh = dev_get_drvdata(dev); + + usbh->device_mode = !!args->args[0]; + + return of_phy_simple_xlate(dev, args); +} + +static int __init bcm63xx_usbh_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm63xx_usbh_phy *usbh; + const struct bcm63xx_usbh_phy_variant *variant; + struct phy *phy; + struct phy_provider *phy_provider; + + usbh = devm_kzalloc(dev, sizeof(*usbh), GFP_KERNEL); + if (!usbh) + return -ENOMEM; + + variant = device_get_match_data(dev); + if (!variant) + return -EINVAL; + usbh->variant = variant; + + usbh->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(usbh->base)) + return PTR_ERR(usbh->base); + + usbh->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(usbh->reset)) { + if (PTR_ERR(usbh->reset) != -EPROBE_DEFER) + dev_err(dev, "failed to get reset\n"); + return PTR_ERR(usbh->reset); + } + + usbh->usbh_clk = devm_clk_get_optional(dev, "usbh"); + if (IS_ERR(usbh->usbh_clk)) + return PTR_ERR(usbh->usbh_clk); + + usbh->usb_ref_clk = devm_clk_get_optional(dev, "usb_ref"); + if (IS_ERR(usbh->usb_ref_clk)) + return PTR_ERR(usbh->usb_ref_clk); + + phy = devm_phy_create(dev, NULL, &bcm63xx_usbh_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + platform_set_drvdata(pdev, usbh); + phy_set_drvdata(phy, usbh); + + phy_provider = devm_of_phy_provider_register(dev, + bcm63xx_usbh_phy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(phy_provider); + } + + dev_dbg(dev, "Registered BCM63xx USB PHY driver\n"); + + return 0; +} + +static const struct of_device_id bcm63xx_usbh_phy_ids[] __initconst = { + { .compatible = "brcm,bcm6318-usbh-phy", .data = &usbh_bcm6318 }, + { .compatible = "brcm,bcm6328-usbh-phy", .data = &usbh_bcm6328 }, + { .compatible = "brcm,bcm6358-usbh-phy", .data = &usbh_bcm6358 }, + { .compatible = "brcm,bcm6362-usbh-phy", .data = &usbh_bcm6368 }, + { .compatible = "brcm,bcm6368-usbh-phy", .data = &usbh_bcm6368 }, + { .compatible = "brcm,bcm63268-usbh-phy", .data = &usbh_bcm63268 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm63xx_usbh_phy_ids); + +static struct platform_driver bcm63xx_usbh_phy_driver __refdata = { + .driver = { + .name = "bcm63xx-usbh-phy", + .of_match_table = bcm63xx_usbh_phy_ids, + }, + .probe = bcm63xx_usbh_phy_probe, +}; +module_platform_driver(bcm63xx_usbh_phy_driver); + +MODULE_DESCRIPTION("BCM63xx USBH PHY driver"); +MODULE_AUTHOR("Álvaro Fernández Rojas "); +MODULE_AUTHOR("Simon Arlott"); +MODULE_LICENSE("GPL"); From 3d7b0ca5300bd01b176f2b4c10e173db802560d8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 21 Jul 2020 16:06:13 +0100 Subject: [PATCH 46/46] phy: qualcomm: fix setting of tx_deamp_3_5db when device property read fails Currently when reading of the device property for "qcom,tx-deamp_3_5db" fails the default is being assigned incorrectly to phy_dwc3->rx_eq. This looks like a copy-n-paste error and in fact should be assigning the default instead to phy_dwc3->tx_deamp_3_5db Addresses-Coverity: ("Copy-paste error") Fixes: ef19b117b834 ("phy: qualcomm: add qcom ipq806x dwc usb phy driver") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200721150613.416876-1-colin.king@canonical.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c index a7241bf110d7..71f257b4a7f5 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c @@ -531,7 +531,7 @@ static int qcom_ipq806x_usb_phy_probe(struct platform_device *pdev) if (device_property_read_u32(&pdev->dev, "qcom,tx-deamp_3_5db", &phy_dwc3->tx_deamp_3_5db)) - phy_dwc3->rx_eq = SSPHY_TX_DEEMPH_3_5DB; + phy_dwc3->tx_deamp_3_5db = SSPHY_TX_DEEMPH_3_5DB; if (device_property_read_u32(&pdev->dev, "qcom,mpll", &phy_dwc3->mpll)) phy_dwc3->mpll = SSPHY_MPLL_VALUE;