From 9b870e8c04ec0c079440d8320d9c728901517052 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 24 Sep 2021 15:26:57 +0200 Subject: [PATCH 01/28] phy: samsung: unify naming and describe driver in KConfig We use everywhere "Samsung" and "Exynos", not the uppercase versions. Describe better which driver applies to which SoC, to make configuring kernel for Samsung SoC easier. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20210924132658.109814-1-krzysztof.kozlowski@canonical.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index e20d2fcc9fe7..3ccaabf2850a 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -30,16 +30,16 @@ config PHY_EXYNOS_PCIE This driver provides PHY interface for Exynos PCIe controller. config PHY_SAMSUNG_UFS - tristate "SAMSUNG SoC series UFS PHY driver" + tristate "Exynos 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. + Enable this to support the Samsung Exynos SoC UFS PHY driver for + Samsung Exynos 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" + tristate "S5P/Exynos SoC series USB 2.0 PHY driver" depends on HAS_IOMEM depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 || COMPILE_TEST select GENERIC_PHY @@ -47,9 +47,9 @@ config PHY_SAMSUNG_USB2 default ARCH_EXYNOS help Enable this to support the Samsung USB 2.0 PHY driver for Samsung - SoCs. This driver provides the interface for USB 2.0 PHY. Support - for particular PHYs will be enabled based on the SoC type in addition - to this driver. + S5Pv210 and Exynos SoCs. This driver provides the interface for USB + 2.0 PHY. Support for particular PHYs will be enabled based on the SoC + type in addition to this driver. config PHY_EXYNOS4210_USB2 bool From 73075011ffff876de8516a1e583dc41869293da9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Sep 2021 09:34:11 +0200 Subject: [PATCH 02/28] phy: HiSilicon: Add driver for Kirin 970 PCIe PHY The Kirin 970 PHY is somewhat similar to the Kirin 960, but it does a lot more. Add the needed bits for PCIe to start working on HiKey 970 boards. Co-developed-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/b7a4ff41b57d861b003f1a00cae81f3d226fbe18.1632814194.git.mchehab+huawei@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/hisilicon/Kconfig | 10 + drivers/phy/hisilicon/Makefile | 1 + drivers/phy/hisilicon/phy-hi3670-pcie.c | 845 ++++++++++++++++++++++++ 3 files changed, 856 insertions(+) create mode 100644 drivers/phy/hisilicon/phy-hi3670-pcie.c diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig index 4d008cfc279c..d3b92c288554 100644 --- a/drivers/phy/hisilicon/Kconfig +++ b/drivers/phy/hisilicon/Kconfig @@ -33,6 +33,16 @@ config PHY_HI3670_USB To compile this driver as a module, choose M here. +config PHY_HI3670_PCIE + tristate "hi3670 PCIe PHY support" + depends on (ARCH_HISI && ARM64) || COMPILE_TEST + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the HiSilicon hi3670 PCIe PHY. + + To compile this driver as a module, choose M here. + config PHY_HISTB_COMBPHY tristate "HiSilicon STB SoCs COMBPHY support" depends on (ARCH_HISI && ARM64) || COMPILE_TEST diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile index 51729868145b..4029d3813b1e 100644 --- a/drivers/phy/hisilicon/Makefile +++ b/drivers/phy/hisilicon/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o obj-$(CONFIG_PHY_HI3660_USB) += phy-hi3660-usb3.o obj-$(CONFIG_PHY_HI3670_USB) += phy-hi3670-usb3.o +obj-$(CONFIG_PHY_HI3670_PCIE) += phy-hi3670-pcie.o obj-$(CONFIG_PHY_HISTB_COMBPHY) += phy-histb-combphy.o obj-$(CONFIG_PHY_HISI_INNO_USB2) += phy-hisi-inno-usb2.o obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o diff --git a/drivers/phy/hisilicon/phy-hi3670-pcie.c b/drivers/phy/hisilicon/phy-hi3670-pcie.c new file mode 100644 index 000000000000..c64c6679b1b9 --- /dev/null +++ b/drivers/phy/hisilicon/phy-hi3670-pcie.c @@ -0,0 +1,845 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe phy driver for Kirin 970 + * + * Copyright (C) 2017 HiSilicon Electronics Co., Ltd. + * https://www.huawei.com + * Copyright (C) 2021 Huawei Technologies Co., Ltd. + * https://www.huawei.com + * + * Authors: + * Mauro Carvalho Chehab + * Manivannan Sadhasivam + * + * Based on: + * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXI_CLK_FREQ 207500000 +#define REF_CLK_FREQ 100000000 + +/* PCIe CTRL registers */ +#define SOC_PCIECTRL_CTRL7_ADDR 0x01c +#define SOC_PCIECTRL_CTRL12_ADDR 0x030 +#define SOC_PCIECTRL_CTRL20_ADDR 0x050 +#define SOC_PCIECTRL_CTRL21_ADDR 0x054 + +#define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0) +#define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550 +#define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070 +#define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10) +#define PCIE_OUTPUT_PULL_DOWN BIT(1) + +/* PCIe PHY registers */ +#define SOC_PCIEPHY_CTRL0_ADDR 0x000 +#define SOC_PCIEPHY_CTRL1_ADDR 0x004 +#define SOC_PCIEPHY_CTRL38_ADDR 0x0098 +#define SOC_PCIEPHY_STATE0_ADDR 0x400 + +#define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004 +#define SUP_DIG_LVL_OVRD_IN 0x003c +#define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008 +#define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c + +#define PCIEPHY_RESET_BIT BIT(17) +#define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19) +#define PCIE_TXDETECT_RX_FAIL BIT(2) +#define PCIE_CLK_SOURCE BIT(8) +#define PCIE_IS_CLOCK_STABLE BIT(19) +#define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22) +#define PCIE_DEASSERT_CONTROLLER_PERST BIT(2) + +#define EYEPARAM_NOCFG 0xffffffff +#define EYE_PARM0_MASK GENMASK(8, 6) +#define EYE_PARM1_MASK GENMASK(11, 8) +#define EYE_PARM2_MASK GENMASK(5, 0) +#define EYE_PARM3_MASK GENMASK(12, 7) +#define EYE_PARM4_MASK GENMASK(14, 9) +#define EYE_PARM0_EN BIT(9) +#define EYE_PARM1_EN BIT(12) +#define EYE_PARM2_EN BIT(6) +#define EYE_PARM3_EN BIT(13) +#define EYE_PARM4_EN BIT(15) + +/* hi3670 pciephy register */ +#define APB_PHY_START_ADDR 0x40000 +#define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04 +#define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40 +#define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44 +#define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50 +#define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54 +#define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00 + +#define CRGPERIPH_PEREN12 0x470 +#define CRGPERIPH_PERDIS12 0x474 +#define CRGPERIPH_PCIECTRL0 0x800 + +#define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16) +#define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0) +#define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8) +#define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12) +#define PCIE_FNPLL_PLL_MODE_MASK BIT(25) + +#define PCIE_FNPLL_DLL_EN BIT(27) +#define PCIE_FNPLL_FBDIV 0xd0 +#define PCIE_FNPLL_FRACDIV 0x555555 +#define PCIE_FNPLL_POSTDIV1 0x5 +#define PCIE_FNPLL_POSTDIV2 0x4 +#define PCIE_FNPLL_PLL_MODE 0x0 + +#define PCIE_PHY_MMC1PLL 0x20 +#define PCIE_PHY_CHOOSE_FNPLL BIT(27) +#define PCIE_PHY_MMC1PLL_DISABLE BIT(0) +#define PCIE_PHY_PCIEPL_BP BIT(16) + +/* define ie,oe cfg */ +#define IO_OE_HARD_GT_MODE BIT(1) +#define IO_IE_EN_HARD_BYPASS BIT(27) +#define IO_OE_EN_HARD_BYPASS BIT(11) +#define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10) +#define IO_OE_GT_MODE BIT(8) +#define DEBOUNCE_WAITCFG_IN GENMASK(23, 20) +#define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13) + +#define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15)) +#define IO_PHYREF_SOFT_GT_MODE BIT(14) +#define IO_REF_SOFT_GT_MODE BIT(13) +#define IO_REF_HARD_GT_MODE BIT(0) + +/* noc power domain */ +#define NOC_POWER_IDLEREQ_1 0x38c +#define NOC_POWER_IDLE_1 0x394 +#define NOC_PW_MASK 0x10000 +#define NOC_PW_SET_BIT 0x1 + +#define NUM_EYEPARAM 5 + +/* info located in sysctrl */ +#define SCTRL_PCIE_CMOS_OFFSET 0x60 +#define SCTRL_PCIE_CMOS_BIT 0x10 +#define SCTRL_PCIE_ISO_OFFSET 0x44 +#define SCTRL_PCIE_ISO_BIT 0x30 +#define SCTRL_PCIE_HPCLK_OFFSET 0x190 +#define SCTRL_PCIE_HPCLK_BIT 0x184000 +#define SCTRL_PCIE_OE_OFFSET 0x14a +#define PCIE_DEBOUNCE_PARAM 0xf0f400 +#define PCIE_OE_BYPASS GENMASK(29, 28) + +/* peri_crg ctrl */ +#define CRGCTRL_PCIE_ASSERT_OFFSET 0x88 +#define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000 + +#define FNPLL_HAS_LOCKED BIT(4) + +/* Time for delay */ +#define TIME_CMOS_MIN 100 +#define TIME_CMOS_MAX 105 +#define PIPE_CLK_STABLE_TIME 100 +#define PLL_CTRL_WAIT_TIME 200 +#define NOC_POWER_TIME 100 + +struct hi3670_pcie_phy { + struct device *dev; + void __iomem *base; + struct regmap *apb; + struct regmap *crgctrl; + struct regmap *sysctrl; + struct regmap *pmctrl; + struct clk *apb_sys_clk; + struct clk *apb_phy_clk; + struct clk *phy_ref_clk; + struct clk *aclk; + struct clk *aux_clk; + u32 eye_param[NUM_EYEPARAM]; +}; + +/* Registers in PCIePHY */ +static inline void hi3670_apb_phy_writel(struct hi3670_pcie_phy *phy, u32 val, + u32 reg) +{ + writel(val, phy->base + APB_PHY_START_ADDR + reg); +} + +static inline u32 hi3670_apb_phy_readl(struct hi3670_pcie_phy *phy, u32 reg) +{ + return readl(phy->base + APB_PHY_START_ADDR + reg); +} + +static inline void hi3670_apb_phy_updatel(struct hi3670_pcie_phy *phy, + u32 val, u32 mask, u32 reg) +{ + u32 regval; + + regval = hi3670_apb_phy_readl(phy, reg); + regval &= ~mask; + regval |= val; + hi3670_apb_phy_writel(phy, regval, reg); +} + +static inline void kirin_apb_natural_phy_writel(struct hi3670_pcie_phy *phy, + u32 val, u32 reg) +{ + writel(val, phy->base + reg); +} + +static inline u32 kirin_apb_natural_phy_readl(struct hi3670_pcie_phy *phy, + u32 reg) +{ + return readl(phy->base + reg); +} + +static void hi3670_pcie_phy_oe_enable(struct hi3670_pcie_phy *phy, bool enable) +{ + u32 val; + + regmap_read(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, &val); + val |= PCIE_DEBOUNCE_PARAM; + if (enable) + val &= ~PCIE_OE_BYPASS; + else + val |= PCIE_OE_BYPASS; + regmap_write(phy->sysctrl, SCTRL_PCIE_OE_OFFSET, val); +} + +static void hi3670_pcie_get_eyeparam(struct hi3670_pcie_phy *phy) +{ + struct device *dev = phy->dev; + struct device_node *np; + int ret, i; + + np = dev->of_node; + + ret = of_property_read_u32_array(np, "hisilicon,eye-diagram-param", + phy->eye_param, NUM_EYEPARAM); + if (!ret) + return; + + /* There's no optional eye_param property. Set array to default */ + for (i = 0; i < NUM_EYEPARAM; i++) + phy->eye_param[i] = EYEPARAM_NOCFG; +} + +static void hi3670_pcie_set_eyeparam(struct hi3670_pcie_phy *phy) +{ + u32 val; + + val = kirin_apb_natural_phy_readl(phy, RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1); + + if (phy->eye_param[1] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM1_MASK; + val |= FIELD_PREP(EYE_PARM1_MASK, phy->eye_param[1]); + val |= EYE_PARM1_EN; + } + kirin_apb_natural_phy_writel(phy, val, + RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1); + + val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_2); + val &= ~(EYE_PARM2_MASK | EYE_PARM3_MASK); + if (phy->eye_param[2] != EYEPARAM_NOCFG) { + val |= FIELD_PREP(EYE_PARM2_MASK, phy->eye_param[2]); + val |= EYE_PARM2_EN; + } + + if (phy->eye_param[3] != EYEPARAM_NOCFG) { + val |= FIELD_PREP(EYE_PARM3_MASK, phy->eye_param[3]); + val |= EYE_PARM3_EN; + } + + kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_2); + + val = kirin_apb_natural_phy_readl(phy, SUP_DIG_LVL_OVRD_IN); + if (phy->eye_param[0] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM0_MASK; + val |= FIELD_PREP(EYE_PARM0_MASK, phy->eye_param[0]); + val |= EYE_PARM0_EN; + } + kirin_apb_natural_phy_writel(phy, val, SUP_DIG_LVL_OVRD_IN); + + val = kirin_apb_natural_phy_readl(phy, LANEN_DIG_ASIC_TX_OVRD_IN_1); + if (phy->eye_param[4] != EYEPARAM_NOCFG) { + val &= ~EYE_PARM4_MASK; + val |= FIELD_PREP(EYE_PARM4_MASK, phy->eye_param[4]); + val |= EYE_PARM4_EN; + } + kirin_apb_natural_phy_writel(phy, val, LANEN_DIG_ASIC_TX_OVRD_IN_1); +} + +static void hi3670_pcie_natural_cfg(struct hi3670_pcie_phy *phy) +{ + u32 val; + + /* change 2p mem_ctrl */ + regmap_write(phy->apb, SOC_PCIECTRL_CTRL20_ADDR, + SOC_PCIECTRL_CTRL20_2P_MEM_CTRL); + + regmap_read(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, &val); + val |= PCIE_PULL_UP_SYS_AUX_PWR_DET; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL7_ADDR, val); + + /* output, pull down */ + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val); + val &= ~PCIE_OUTPUT_PULL_BITS; + val |= PCIE_OUTPUT_PULL_DOWN; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val); + + /* Handle phy_reset and lane0_reset to HW */ + hi3670_apb_phy_updatel(phy, PCIEPHY_RESET_BIT, + PCIEPHY_PIPE_LINE0_RESET_BIT | PCIEPHY_RESET_BIT, + SOC_PCIEPHY_CTRL1_ADDR); + + /* fix chip bug: TxDetectRx fail */ + hi3670_apb_phy_updatel(phy, PCIE_TXDETECT_RX_FAIL, PCIE_TXDETECT_RX_FAIL, + SOC_PCIEPHY_CTRL38_ADDR); +} + +static void hi3670_pcie_pll_init(struct hi3670_pcie_phy *phy) +{ + hi3670_apb_phy_updatel(phy, PCIE_PHY_CHOOSE_FNPLL, PCIE_PHY_CHOOSE_FNPLL, + SOC_PCIEPHY_MMC1PLL_CTRL1); + + hi3670_apb_phy_updatel(phy, + FIELD_PREP(PCIE_FNPLL_FBDIV_MASK, PCIE_FNPLL_FBDIV), + PCIE_FNPLL_FBDIV_MASK, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + hi3670_apb_phy_updatel(phy, + FIELD_PREP(PCIE_FNPLL_FRACDIV_MASK, PCIE_FNPLL_FRACDIV), + PCIE_FNPLL_FRACDIV_MASK, SOC_PCIEPHY_MMC1PLL_CTRL17); + + hi3670_apb_phy_updatel(phy, + PCIE_FNPLL_DLL_EN | + FIELD_PREP(PCIE_FNPLL_POSTDIV1_MASK, PCIE_FNPLL_POSTDIV1) | + FIELD_PREP(PCIE_FNPLL_POSTDIV2_MASK, PCIE_FNPLL_POSTDIV2) | + FIELD_PREP(PCIE_FNPLL_PLL_MODE_MASK, PCIE_FNPLL_PLL_MODE), + PCIE_FNPLL_POSTDIV1_MASK | + PCIE_FNPLL_POSTDIV2_MASK | + PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN, + SOC_PCIEPHY_MMC1PLL_CTRL20); + + hi3670_apb_phy_writel(phy, PCIE_PHY_MMC1PLL, + SOC_PCIEPHY_MMC1PLL_CTRL21); +} + +static int hi3670_pcie_pll_ctrl(struct hi3670_pcie_phy *phy, bool enable) +{ + struct device *dev = phy->dev; + u32 val; + int time = PLL_CTRL_WAIT_TIME; + + if (enable) { + /* pd = 0 */ + hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_MMC1PLL_DISABLE, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + /* choose FNPLL */ + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0); + while (!(val & FNPLL_HAS_LOCKED)) { + if (!time) { + dev_err(dev, "wait for pll_lock timeout\n"); + return -EINVAL; + } + time--; + udelay(1); + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_MMC1PLL_STAT0); + } + + hi3670_apb_phy_updatel(phy, 0, PCIE_PHY_PCIEPL_BP, + SOC_PCIEPHY_MMC1PLL_CTRL20); + + } else { + hi3670_apb_phy_updatel(phy, + PCIE_PHY_MMC1PLL_DISABLE, + PCIE_PHY_MMC1PLL_DISABLE, + SOC_PCIEPHY_MMC1PLL_CTRL16); + + hi3670_apb_phy_updatel(phy, PCIE_PHY_PCIEPL_BP, + PCIE_PHY_PCIEPL_BP, + SOC_PCIEPHY_MMC1PLL_CTRL20); + } + + return 0; +} + +static void hi3670_pcie_hp_debounce_gt(struct hi3670_pcie_phy *phy, bool open) +{ + if (open) + /* gt_clk_pcie_hp/gt_clk_pcie_debounce open */ + regmap_write(phy->crgctrl, CRGPERIPH_PEREN12, + IO_HP_DEBOUNCE_GT); + else + /* gt_clk_pcie_hp/gt_clk_pcie_debounce close */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_HP_DEBOUNCE_GT); +} + +static void hi3670_pcie_phyref_gt(struct hi3670_pcie_phy *phy, bool open) +{ + unsigned int val; + + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + + if (open) + val &= ~IO_OE_HARD_GT_MODE; /* enable hard gt mode */ + else + val |= IO_OE_HARD_GT_MODE; /* disable hard gt mode */ + + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, IO_PHYREF_SOFT_GT_MODE); +} + +static void hi3670_pcie_oe_ctrl(struct hi3670_pcie_phy *phy, bool en_flag) +{ + unsigned int val; + + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + + /* set ie cfg */ + val |= IO_IE_EN_HARD_BYPASS; + + /* set oe cfg */ + val &= ~IO_HARD_CTRL_DEBOUNCE_BYPASS; + + /* set phy_debounce in&out time */ + val |= (DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT); + + /* select oe_gt_mode */ + val |= IO_OE_GT_MODE; + + if (en_flag) + val &= ~IO_OE_EN_HARD_BYPASS; + else + val |= IO_OE_EN_HARD_BYPASS; + + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); +} + +static void hi3670_pcie_ioref_gt(struct hi3670_pcie_phy *phy, bool open) +{ + unsigned int val; + + if (open) { + regmap_write(phy->apb, SOC_PCIECTRL_CTRL21_ADDR, + SOC_PCIECTRL_CTRL21_DEFAULT); + + hi3670_pcie_oe_ctrl(phy, true); + + /* en hard gt mode */ + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + val &= ~IO_REF_HARD_GT_MODE; + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_REF_SOFT_GT_MODE); + + } else { + /* disable hard gt mode */ + regmap_read(phy->crgctrl, CRGPERIPH_PCIECTRL0, &val); + val |= IO_REF_HARD_GT_MODE; + regmap_write(phy->crgctrl, CRGPERIPH_PCIECTRL0, val); + + /* disable soft gt mode */ + regmap_write(phy->crgctrl, CRGPERIPH_PERDIS12, + IO_REF_SOFT_GT_MODE); + + hi3670_pcie_oe_ctrl(phy, false); + } +} + +static int hi3670_pcie_allclk_ctrl(struct hi3670_pcie_phy *phy, bool clk_on) +{ + struct device *dev = phy->dev; + int ret = 0; + + if (!clk_on) + goto close_clocks; + + /* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */ + hi3670_apb_phy_updatel(phy, 0, PCIE_CLK_SOURCE, + SOC_PCIEPHY_CTRL1_ADDR); + + hi3670_pcie_pll_init(phy); + + ret = hi3670_pcie_pll_ctrl(phy, true); + if (ret) { + dev_err(dev, "Failed to enable pll\n"); + return -EINVAL; + } + hi3670_pcie_hp_debounce_gt(phy, true); + hi3670_pcie_phyref_gt(phy, true); + hi3670_pcie_ioref_gt(phy, true); + + ret = clk_set_rate(phy->aclk, AXI_CLK_FREQ); + if (ret) { + dev_err(dev, "Failed to set rate\n"); + goto close_clocks; + } + + return 0; + +close_clocks: + hi3670_pcie_ioref_gt(phy, false); + hi3670_pcie_phyref_gt(phy, false); + hi3670_pcie_hp_debounce_gt(phy, false); + + hi3670_pcie_pll_ctrl(phy, false); + + return ret; +} + +static bool is_pipe_clk_stable(struct hi3670_pcie_phy *phy) +{ + struct device *dev = phy->dev; + u32 val; + u32 time = PIPE_CLK_STABLE_TIME; + u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE; + + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR); + while (val & pipe_clk_stable) { + mdelay(1); + if (!time) { + dev_err(dev, "PIPE clk is not stable\n"); + return false; + } + time--; + val = hi3670_apb_phy_readl(phy, SOC_PCIEPHY_STATE0_ADDR); + } + + return true; +} + +static int hi3670_pcie_noc_power(struct hi3670_pcie_phy *phy, bool enable) +{ + struct device *dev = phy->dev; + u32 time = NOC_POWER_TIME; + unsigned int val = NOC_PW_MASK; + int rst; + + if (enable) + val = NOC_PW_MASK | NOC_PW_SET_BIT; + else + val = NOC_PW_MASK; + rst = enable ? 1 : 0; + + regmap_write(phy->pmctrl, NOC_POWER_IDLEREQ_1, val); + + time = NOC_POWER_TIME; + regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val); + while ((val & NOC_PW_SET_BIT) != rst) { + udelay(10); + if (!time) { + dev_err(dev, "Failed to reverse noc power-status\n"); + return -EINVAL; + } + time--; + regmap_read(phy->pmctrl, NOC_POWER_IDLE_1, &val); + } + + return 0; +} + +static int hi3670_pcie_get_resources_from_pcie(struct hi3670_pcie_phy *phy) +{ + struct device_node *pcie_port; + struct device *dev = phy->dev; + struct device *pcie_dev; + + pcie_port = of_get_child_by_name(dev->parent->of_node, "pcie"); + if (!pcie_port) { + dev_err(dev, "no pcie node found in %s\n", + dev->parent->of_node->full_name); + return -ENODEV; + } + + pcie_dev = bus_find_device_by_of_node(&platform_bus_type, pcie_port); + if (!pcie_dev) { + dev_err(dev, "Didn't find pcie device\n"); + return -ENODEV; + } + + /* + * We might just use NULL instead of the APB name, as the + * pcie-kirin currently registers directly just one regmap (although + * the DWC driver register other regmaps). + * + * Yet, it sounds safer to warrant that it will be accessing the + * right regmap. So, let's use the named version. + */ + phy->apb = dev_get_regmap(pcie_dev, "kirin_pcie_apb"); + if (!phy->apb) { + dev_err(dev, "Failed to get APB regmap\n"); + return -ENODEV; + } + + return 0; +} + +static int kirin_pcie_clk_ctrl(struct hi3670_pcie_phy *phy, bool enable) +{ + int ret = 0; + + if (!enable) + goto close_clk; + + ret = clk_set_rate(phy->phy_ref_clk, REF_CLK_FREQ); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->phy_ref_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->apb_sys_clk); + if (ret) + goto apb_sys_fail; + + ret = clk_prepare_enable(phy->apb_phy_clk); + if (ret) + goto apb_phy_fail; + + ret = clk_prepare_enable(phy->aclk); + if (ret) + goto aclk_fail; + + ret = clk_prepare_enable(phy->aux_clk); + if (ret) + goto aux_clk_fail; + + return 0; + +close_clk: + clk_disable_unprepare(phy->aux_clk); +aux_clk_fail: + clk_disable_unprepare(phy->aclk); +aclk_fail: + clk_disable_unprepare(phy->apb_phy_clk); +apb_phy_fail: + clk_disable_unprepare(phy->apb_sys_clk); +apb_sys_fail: + clk_disable_unprepare(phy->phy_ref_clk); + + return ret; +} + +static int hi3670_pcie_phy_init(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + int ret; + + /* + * The code under hi3670_pcie_get_resources_from_pcie() need to + * access the reset-gpios and the APB registers, both from the + * pcie-kirin driver. + * + * The APB is obtained via the pcie driver's regmap + * Such kind of resource can only be obtained during the PCIe + * power_on sequence, as the code inside pcie-kirin needs to + * be already probed, as it needs to register the APB regmap. + */ + + ret = hi3670_pcie_get_resources_from_pcie(phy); + if (ret) + return ret; + + return 0; +} + +static int hi3670_pcie_phy_power_on(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + int val, ret; + + /* Power supply for Host */ + regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, SCTRL_PCIE_CMOS_BIT); + usleep_range(TIME_CMOS_MIN, TIME_CMOS_MAX); + + hi3670_pcie_phy_oe_enable(phy, true); + + ret = kirin_pcie_clk_ctrl(phy, true); + if (ret) + return ret; + + /* ISO disable, PCIeCtrl, PHY assert and clk gate clear */ + regmap_write(phy->sysctrl, SCTRL_PCIE_ISO_OFFSET, SCTRL_PCIE_ISO_BIT); + regmap_write(phy->crgctrl, CRGCTRL_PCIE_ASSERT_OFFSET, + CRGCTRL_PCIE_ASSERT_BIT); + regmap_write(phy->sysctrl, SCTRL_PCIE_HPCLK_OFFSET, + SCTRL_PCIE_HPCLK_BIT); + + hi3670_pcie_natural_cfg(phy); + + ret = hi3670_pcie_allclk_ctrl(phy, true); + if (ret) + goto disable_clks; + + /* pull down phy_test_powerdown signal */ + hi3670_apb_phy_updatel(phy, 0, PCIE_PULL_DOWN_PHY_TEST_POWERDOWN, + SOC_PCIEPHY_CTRL0_ADDR); + + /* deassert controller perst_n */ + regmap_read(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, &val); + val |= PCIE_DEASSERT_CONTROLLER_PERST; + regmap_write(phy->apb, SOC_PCIECTRL_CTRL12_ADDR, val); + udelay(10); + + ret = is_pipe_clk_stable(phy); + if (!ret) + goto disable_clks; + + hi3670_pcie_set_eyeparam(phy); + + ret = hi3670_pcie_noc_power(phy, false); + if (ret) + goto disable_clks; + + return 0; + +disable_clks: + kirin_pcie_clk_ctrl(phy, false); + return ret; +} + +static int hi3670_pcie_phy_power_off(struct phy *generic_phy) +{ + struct hi3670_pcie_phy *phy = phy_get_drvdata(generic_phy); + + hi3670_pcie_phy_oe_enable(phy, false); + + hi3670_pcie_allclk_ctrl(phy, false); + + /* Drop power supply for Host */ + regmap_write(phy->sysctrl, SCTRL_PCIE_CMOS_OFFSET, 0); + + /* + * FIXME: The enabled clocks should be disabled here by calling + * kirin_pcie_clk_ctrl(phy, false); + * However, some clocks used at Kirin 970 should be marked as + * CLK_IS_CRITICAL at clk-hi3670 driver, as powering such clocks off + * cause an Asynchronous SError interrupt, which produces panic(). + * While clk-hi3670 is not fixed, we cannot risk disabling clocks here. + */ + + return 0; +} + +static const struct phy_ops hi3670_phy_ops = { + .init = hi3670_pcie_phy_init, + .power_on = hi3670_pcie_phy_power_on, + .power_off = hi3670_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static int hi3670_pcie_phy_get_resources(struct hi3670_pcie_phy *phy, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + /* syscon */ + phy->crgctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-crgctrl"); + if (IS_ERR(phy->crgctrl)) + return PTR_ERR(phy->crgctrl); + + phy->sysctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-sctrl"); + if (IS_ERR(phy->sysctrl)) + return PTR_ERR(phy->sysctrl); + + phy->pmctrl = syscon_regmap_lookup_by_compatible("hisilicon,hi3670-pmctrl"); + if (IS_ERR(phy->sysctrl)) + return PTR_ERR(phy->sysctrl); + + /* clocks */ + phy->phy_ref_clk = devm_clk_get(dev, "phy_ref"); + if (IS_ERR(phy->phy_ref_clk)) + return PTR_ERR(phy->phy_ref_clk); + + phy->aux_clk = devm_clk_get(dev, "aux"); + if (IS_ERR(phy->aux_clk)) + return PTR_ERR(phy->aux_clk); + + phy->apb_phy_clk = devm_clk_get(dev, "apb_phy"); + if (IS_ERR(phy->apb_phy_clk)) + return PTR_ERR(phy->apb_phy_clk); + + phy->apb_sys_clk = devm_clk_get(dev, "apb_sys"); + if (IS_ERR(phy->apb_sys_clk)) + return PTR_ERR(phy->apb_sys_clk); + + phy->aclk = devm_clk_get(dev, "aclk"); + if (IS_ERR(phy->aclk)) + return PTR_ERR(phy->aclk); + + /* registers */ + phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->base)) + return PTR_ERR(phy->base); + + hi3670_pcie_get_eyeparam(phy); + + return 0; +} + +static int hi3670_pcie_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct hi3670_pcie_phy *phy; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->dev = dev; + + ret = hi3670_pcie_phy_get_resources(phy, pdev); + if (ret) + return ret; + + generic_phy = devm_phy_create(dev, dev->of_node, &hi3670_phy_ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id hi3670_pcie_phy_match[] = { + { + .compatible = "hisilicon,hi970-pcie-phy", + }, + {}, +}; + +static struct platform_driver hi3670_pcie_phy_driver = { + .probe = hi3670_pcie_phy_probe, + .driver = { + .of_match_table = hi3670_pcie_phy_match, + .name = "hi3670_pcie_phy", + .suppress_bind_attrs = true, + } +}; +builtin_platform_driver(hi3670_pcie_phy_driver); + +MODULE_DEVICE_TABLE(of, hi3670_pcie_phy_match); +MODULE_DESCRIPTION("PCIe phy driver for Kirin 970"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_LICENSE("GPL v2"); From c2aff14ea0d909a0132471ce2f79ffeb259cce08 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 27 Sep 2021 15:16:39 +0800 Subject: [PATCH 03/28] dt-bindings: phy: qcom,qmp: Update maintainer email Manu's codeaurora.org email address bounces. Before he comes back with his new email, fill Vinod's address in there. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20210927071639.6225-1-shawnguo@kernel.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index 75be5650a198..a167b5c1ae17 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -8,7 +8,7 @@ $schema: "http://devicetree.org/meta-schemas/core.yaml#" title: Qualcomm QMP PHY controller maintainers: - - Manu Gautam + - Vinod Koul description: QMP phy controller supports physical layer functionality for a number of From 34356d113bdc62ff68cf7b760ef98580aafdc60c Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 23 Sep 2021 10:50:13 +0800 Subject: [PATCH 04/28] phy: broadcom: Kconfig: Add configuration menu for Broadcom phy drivers Adding a configuration menu to hold many Broadcom phy drivers helps to make the menu display more concise. Signed-off-by: Cai Huoqing Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20210923025013.189-1-caihuoqing@baidu.com Signed-off-by: Vinod Koul --- drivers/phy/broadcom/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index fd92b73b7109..f81e23742079 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -2,6 +2,8 @@ # # Phy drivers for Broadcom platforms # +menu "PHY drivers for Broadcom platforms" + config PHY_BCM63XX_USBH tristate "BCM63xx USBH PHY driver" depends on BMIPS_GENERIC || COMPILE_TEST @@ -112,3 +114,5 @@ config PHY_BCM_SR_PCIE help Enable this to support the Broadcom Stingray PCIe PHY If unsure, say N. + +endmenu From 40683ee5ff040b979f02ce2adf02ea32b83cd006 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 19 Sep 2021 11:11:09 +0800 Subject: [PATCH 05/28] dt-bindings: phy: qcom,qusb2: Add compatible for QCM2290 Add compatible for QUSB2 PHY on QCM2290 platform. Signed-off-by: Shawn Guo Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210919031110.25064-2-shawn.guo@linaro.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 ec9ccaaba098..48ae604b2194 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -21,6 +21,7 @@ properties: - qcom,ipq8074-qusb2-phy - qcom,msm8996-qusb2-phy - qcom,msm8998-qusb2-phy + - qcom,qcm2290-qusb2-phy - qcom,sdm660-qusb2-phy - qcom,ipq6018-qusb2-phy - qcom,sm4250-qusb2-phy From 0fd732f8246786164db7375f7ae7b8d1f4cdbc14 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 19 Sep 2021 11:11:10 +0800 Subject: [PATCH 06/28] phy: qcom-qusb2: Add compatible for QCM2290 Add compatible for QCM2290 QUSB2 device which reuses SM6115 configuration. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20210919031110.25064-3-shawn.guo@linaro.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 3c1d3b71c825..ae063a8b3e28 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -913,6 +913,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = { }, { .compatible = "qcom,msm8998-qusb2-phy", .data = &msm8998_phy_cfg, + }, { + .compatible = "qcom,qcm2290-qusb2-phy", + .data = &sm6115_phy_cfg, }, { .compatible = "qcom,sdm660-qusb2-phy", .data = &sdm660_phy_cfg, From 55b9b741712de84d144cfed6cb3987314002ea21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 13 Sep 2021 10:00:22 +0200 Subject: [PATCH 07/28] dt-bindings: phy: brcm,ns-usb2-phy: bind just a PHY block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old binding was covering the whole DMU block space (DMU block contains CRU block which contains USB PHY). It was a bad design, overkill and a non-generic solution. Northstar's USB 2.0 PHY is a small block (part of the CRU MFD) and binding should be designed to represent that properly. Rework the binding to map just PHY with the "reg" property and use syscon to reference shared register that controls block access. The old binding is deprecated now. Signed-off-by: Rafał Miłecki Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210913080024.6951-2-zajec5@gmail.com Signed-off-by: Vinod Koul --- .../bindings/phy/bcm-ns-usb2-phy.yaml | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml index 05b4dcd80019..426101530a21 100644 --- a/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/bcm-ns-usb2-phy.yaml @@ -18,13 +18,21 @@ properties: const: brcm,ns-usb2-phy reg: - items: - - description: iomem address range of DMU (Device Management Unit) + anyOf: + - maxItems: 1 + description: PHY control register + - maxItems: 1 + description: iomem address range of DMU (Device Management Unit) + deprecated: true reg-names: items: - const: dmu + brcm,syscon-clkset: + description: phandle to syscon for clkset register + $ref: /schemas/types.yaml#/definitions/phandle + clocks: items: - description: USB PHY reference clock @@ -39,20 +47,25 @@ properties: required: - compatible - reg - - reg-names - clocks - clock-names - "#phy-cells" +oneOf: + - required: + - brcm,syscon-clkset + - required: + - reg-names + additionalProperties: false examples: - | #include - phy@1800c000 { + phy@1800c164 { compatible = "brcm,ns-usb2-phy"; - reg = <0x1800c000 0x1000>; - reg-names = "dmu"; + reg = <0x1800c164 0x4>; + brcm,syscon-clkset = <&clkset>; clocks = <&genpll BCM_NSP_GENPLL_USB_PHY_REF_CLK>; clock-names = "phy-ref-clk"; #phy-cells = <0>; From 6ae6942fe996a32f0ff471c6481b3a545e11e14b Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 22 Sep 2021 21:00:16 +0800 Subject: [PATCH 08/28] phy: qcom-qmp: Make use of the helper function devm_add_action_or_reset() The helper function devm_add_action_or_reset() will internally call devm_add_action(), and gif devm_add_action() fails then it will execute the action mentioned and return the error code. So use devm_add_action_or_reset() instead of devm_add_action() to simplify the error handling, reduce the code. Signed-off-by: Cai Huoqing Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210922130017.692-1-caihuoqing@baidu.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index f14032170b1c..084e3d96264e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -5154,11 +5154,7 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) * Roll a devm action because the clock provider is the child node, but * the child node is not actually a device. */ - ret = devm_add_action(qmp->dev, phy_clk_release_provider, np); - if (ret) - phy_clk_release_provider(np); - - return ret; + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); } /* @@ -5350,11 +5346,7 @@ static int phy_dp_clks_register(struct qcom_qmp *qmp, struct qmp_phy *qphy, * Roll a devm action because the clock provider is the child node, but * the child node is not actually a device. */ - ret = devm_add_action(qmp->dev, phy_clk_release_provider, np); - if (ret) - phy_clk_release_provider(np); - - return ret; + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); } static const struct phy_ops qcom_qmp_phy_gen_ops = { From 717e04fba4fa0e2f8e5a1fa810c5eff4d4d5e7db Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 22 Sep 2021 21:00:23 +0800 Subject: [PATCH 09/28] phy: rockchip-inno-usb2: Make use of the helper function devm_add_action_or_reset() The helper function devm_add_action_or_reset() will internally call devm_add_action(), and gif devm_add_action() fails then it will execute the action mentioned and return the error code. So use devm_add_action_or_reset() instead of devm_add_action() to simplify the error handling, reduce the code. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20210922130024.745-1-caihuoqing@baidu.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 4f569d9307b9..1938365abbb3 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -321,7 +321,7 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) struct device_node *node = rphy->dev->of_node; struct clk_init_data init; const char *clk_name; - int ret; + int ret = 0; init.flags = 0; init.name = "clk_usbphy_480m"; @@ -352,15 +352,8 @@ rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) if (ret < 0) goto err_clk_provider; - ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, - rphy); - if (ret < 0) - goto err_unreg_action; + return devm_add_action_or_reset(rphy->dev, rockchip_usb2phy_clk480m_unregister, rphy); - return 0; - -err_unreg_action: - of_clk_del_provider(node); err_clk_provider: clk_unregister(rphy->clk480m); err_ret: From c6ae0bce6bf39042796df9fb1e857b596047f0a5 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 28 Sep 2021 10:20:00 +0800 Subject: [PATCH 10/28] dt-bindings: phy: qcom,qusb2: Add missing vdd-supply Besides vdda-pll and vdda-phy-dpdm, vdd-supply is a required supply for PHY digital circuit operation. Add it for correctness and completeness. Signed-off-by: Shawn Guo Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210928022002.26286-2-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index 48ae604b2194..aa2e409a1a09 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -51,6 +51,10 @@ properties: - const: ref - const: iface + vdd-supply: + description: + Phandle to 0.9V regulator supply to PHY digital circuit. + vdda-pll-supply: description: Phandle to 1.8V regulator supply to PHY refclk pll block. @@ -157,6 +161,7 @@ required: - "#phy-cells" - clocks - clock-names + - vdd-supply - vdda-pll-supply - vdda-phy-dpdm-supply - resets @@ -175,6 +180,7 @@ examples: <&gcc GCC_RX1_USB2_CLKREF_CLK>; clock-names = "cfg_ahb", "ref"; + vdd-supply = <&pm8994_l28>; vdda-pll-supply = <&pm8994_l12>; vdda-phy-dpdm-supply = <&pm8994_l24>; From 19b6348e472c34e360157d68c6ee10b19dd23154 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 28 Sep 2021 10:20:01 +0800 Subject: [PATCH 11/28] phy: qcom-qusb2: Add missing vdd supply Per downstream kernel, beside vdda-pll and vdda-phy-dpdm, vdd is also a required supply for QUSB2 PHY digital circuit operation. The driver works right now likely because firmware already sets it up. Add it for correctness and completeness. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20210928022002.26286-3-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qusb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index ae063a8b3e28..7a8e4333babe 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -371,7 +371,7 @@ static const struct qusb2_phy_cfg sm6115_phy_cfg = { }; static const char * const qusb2_phy_vreg_names[] = { - "vdda-pll", "vdda-phy-dpdm", + "vdd", "vdda-pll", "vdda-phy-dpdm", }; #define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) From 0b7c7ebe0f603dc4740685c23339e73519490ad8 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 27 Sep 2021 14:48:28 +0800 Subject: [PATCH 12/28] dt-bindings: phy: qcom,qmp: Add QCM2290 USB3 PHY Add support for USB3 PHY found on Qualcomm QCM2290 SoC. Signed-off-by: Shawn Guo Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210927064829.5752-2-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,qmp-phy.yaml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index a167b5c1ae17..647b45cfec84 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -27,6 +27,7 @@ properties: - qcom,msm8998-qmp-pcie-phy - qcom,msm8998-qmp-ufs-phy - qcom,msm8998-qmp-usb3-phy + - qcom,qcm2290-qmp-usb3-phy - qcom,sc7180-qmp-usb3-phy - qcom,sc8180x-qmp-pcie-phy - qcom,sc8180x-qmp-ufs-phy @@ -414,6 +415,32 @@ allOf: items: - const: phy - const: common + - if: + properties: + compatible: + contains: + enum: + - qcom,qcm2290-qmp-usb3-phy + then: + properties: + clocks: + items: + - description: Phy config clock. + - description: 19.2 MHz ref clk. + - description: Phy common block aux clock. + clock-names: + items: + - const: cfg_ahb + - const: ref + - const: com_aux + resets: + items: + - description: phy_phy reset. + - description: reset of phy block. + reset-names: + items: + - const: phy_phy + - const: phy examples: - | From 8abe5e778b2c236c053d643435a847cf151231e9 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 27 Sep 2021 14:48:29 +0800 Subject: [PATCH 13/28] phy: qcom-qmp: Add QCM2290 USB3 PHY support Enable QCM2290 USB3 PHY support by adding the qmp_phy_cfg data which are taken from downstream kernel. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20210927064829.5752-3-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 143 ++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 2 + 2 files changed, 145 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 084e3d96264e..32123a60be97 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -135,6 +135,8 @@ enum qphy_reg_layout { QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, QPHY_PCS_LFPS_RXTERM_IRQ_STATUS, QPHY_PCS_POWER_DOWN_CONTROL, + /* PCS_MISC registers */ + QPHY_PCS_MISC_TYPEC_CTRL, /* Keep last to ensure regs_layout arrays are properly initialized */ QPHY_LAYOUT_SIZE }; @@ -229,6 +231,16 @@ static const unsigned int sm8350_usb3_uniphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x1014, }; +static const unsigned int qcm2290_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x04, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0xd8, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0xdc, + [QPHY_PCS_STATUS] = 0x174, + [QPHY_PCS_MISC_TYPEC_CTRL] = 0x00, +}; + static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = 0x00, [QPHY_PCS_READY_STATUS] = 0x160, @@ -2761,6 +2773,99 @@ static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), }; +static const struct qmp_phy_init_tbl qcm2290_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + 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_SYS_CLK_CTRL, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + 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_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_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), + 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_LOCK_CMP_EN, 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), + 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), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_INITVAL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CTRL_BY_PSM, 0x01), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0xc6), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x00), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_VGA_CAL_CNTRL2, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x00), +}; + +static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f), + 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_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), +}; + struct qmp_phy; /* struct qmp_phy_cfg - per-PHY initialization config */ @@ -2995,6 +3100,10 @@ static const char * const qmp_v4_sdx55_usbphy_clk_l[] = { "aux", "cfg_ahb", "ref" }; +static const char * const qcm2290_usb3phy_clk_l[] = { + "cfg_ahb", "ref", "com_aux", +}; + /* list of resets */ static const char * const msm8996_pciephy_reset_l[] = { "phy", "common", "cfg", @@ -3008,6 +3117,10 @@ static const char * const sc7180_usb3phy_reset_l[] = { "phy", }; +static const char * const qcm2290_usb3phy_reset_l[] = { + "phy_phy", "phy", +}; + static const char * const sdm845_pciephy_reset_l[] = { "phy", }; @@ -3974,6 +4087,33 @@ static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, }; +static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = qcm2290_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl), + .tx_tbl = qcm2290_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qcm2290_usb3_tx_tbl), + .rx_tbl = qcm2290_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qcm2290_usb3_rx_tbl), + .pcs_tbl = qcm2290_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qcm2290_usb3_pcs_tbl), + .clk_list = qcm2290_usb3phy_clk_l, + .num_clks = ARRAY_SIZE(qcm2290_usb3phy_clk_l), + .reset_list = qcm2290_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qcm2290_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .is_dual_lane_phy = true, +}; + static void qcom_qmp_phy_configure_lane(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -5605,6 +5745,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8350-qmp-usb3-uni-phy", .data = &sm8350_usb3_uniphy_cfg, + }, { + .compatible = "qcom,qcm2290-qmp-usb3-phy", + .data = &qcm2290_usb3phy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index bebeac2c091c..e15f461065bb 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -169,6 +169,7 @@ #define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0a8 #define QSERDES_COM_SYSCLK_EN_SEL 0x0ac #define QSERDES_COM_RESETSM_CNTRL 0x0b4 +#define QSERDES_COM_RESETSM_CNTRL2 0x0b8 #define QSERDES_COM_RESTRIM_CTRL 0x0bc #define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 #define QSERDES_COM_LOCK_CMP_EN 0x0c8 @@ -181,6 +182,7 @@ #define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 #define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec #define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 +#define QSERDES_COM_INTEGLOOP_INITVAL 0x100 #define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 #define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c #define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 From bf7ffcd0069d30e2e7ba2b827f08c89f471cd1f3 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 23 Sep 2021 02:35:48 +0300 Subject: [PATCH 14/28] phy: qcom-qusb2: Fix a memory leak on probe On success nvmem_cell_read() returns a pointer to a dynamically allocated buffer, and therefore it shall be freed after usage. The issue is reported by kmemleak: # cat /sys/kernel/debug/kmemleak unreferenced object 0xffff3b3803e4b280 (size 128): comm "kworker/u16:1", pid 107, jiffies 4294892861 (age 94.120s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [<000000007739afdc>] __kmalloc+0x27c/0x41c [<0000000071c0fbf8>] nvmem_cell_read+0x40/0xe0 [<00000000e803ef1f>] qusb2_phy_init+0x258/0x5bc [<00000000fc81fcfa>] phy_init+0x70/0x110 [<00000000e3d48a57>] dwc3_core_soft_reset+0x4c/0x234 [<0000000027d1dbd4>] dwc3_core_init+0x68/0x990 [<000000001965faf9>] dwc3_probe+0x4f4/0x730 [<000000002f7617ca>] platform_probe+0x74/0xf0 [<00000000a2576cac>] really_probe+0xc4/0x470 [<00000000bc77f2c5>] __driver_probe_device+0x11c/0x190 [<00000000130db71f>] driver_probe_device+0x48/0x110 [<0000000019f36c2b>] __device_attach_driver+0xa4/0x140 [<00000000e5812ff7>] bus_for_each_drv+0x84/0xe0 [<00000000f4bac574>] __device_attach+0xe4/0x1c0 [<00000000d3beb631>] device_initial_probe+0x20/0x30 [<000000008019b9db>] bus_probe_device+0xa4/0xb0 Fixes: ca04d9d3e1b1 ("phy: qcom-qusb2: New driver for QUSB2 PHY on Qcom chips") Signed-off-by: Vladimir Zapolskiy Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20210922233548.2150244-1-vladimir.zapolskiy@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qusb2.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 7a8e4333babe..032d02bf50c5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -561,7 +561,7 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) { struct device *dev = &qphy->phy->dev; const struct qusb2_phy_cfg *cfg = qphy->cfg; - u8 *val; + u8 *val, hstx_trim; /* efuse register is optional */ if (!qphy->cell) @@ -575,7 +575,13 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) * set while configuring the phy. */ val = nvmem_cell_read(qphy->cell, NULL); - if (IS_ERR(val) || !val[0]) { + if (IS_ERR(val)) { + dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); + return; + } + hstx_trim = val[0]; + kfree(val); + if (!hstx_trim) { dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); return; } @@ -583,12 +589,10 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) /* Fused TUNE1/2 value is the higher nibble only */ if (cfg->update_tune1_with_efuse) qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1], - val[0] << HSTX_TRIM_SHIFT, - HSTX_TRIM_MASK); + hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); else qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2], - val[0] << HSTX_TRIM_SHIFT, - HSTX_TRIM_MASK); + hstx_trim << HSTX_TRIM_SHIFT, HSTX_TRIM_MASK); } static int qusb2_phy_set_mode(struct phy *phy, From 4ff6b676ba53c02a4ab82181265678f64272641c Mon Sep 17 00:00:00 2001 From: Johan Jonker Date: Sat, 28 Aug 2021 13:12:17 +0200 Subject: [PATCH 15/28] dt-bindings: phy: rockchip: remove usb-phy fallback string for rk3066a/rk3188 With the conversion of rockchip-usb-phy.yaml a long time used fallback string for rk3066a/rk3188 was added. The linux driver doesn't do much with the GRF phy address range, however the u-boot driver rockchip_usb2_phy.c does. The bits in GRF_UOC0_CON2 for rk3066a/rk3188 and rk3288 for example don't match. Remove the usb-phy fallback string for rk3066a/rk3188 to prevent possible strange side effects. Signed-off-by: Johan Jonker Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210828111218.10026-1-jbx6244@gmail.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/rockchip-usb-phy.yaml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml index f0fc8275dcd0..499d55131aa8 100644 --- a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.yaml @@ -11,13 +11,10 @@ maintainers: properties: compatible: - oneOf: - - const: rockchip,rk3288-usb-phy - - items: - - enum: - - rockchip,rk3066a-usb-phy - - rockchip,rk3188-usb-phy - - const: rockchip,rk3288-usb-phy + enum: + - rockchip,rk3066a-usb-phy + - rockchip,rk3188-usb-phy + - rockchip,rk3288-usb-phy "#address-cells": const: 1 From 95e38c17d9971cd6639ce899a24e581800e7113a Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Fri, 15 Oct 2021 18:14:25 +0200 Subject: [PATCH 16/28] phy: stm32: restore utmi switch on resume UTMI switch value can be lost during suspend/resume, depending on the power state reached. This patch adds resume function to usbphyc, to reconfigure utmi switch after suspend. Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20211015161427.220784-2-amelie.delaunay@foss.st.com Signed-off-by: Vinod Koul --- drivers/phy/st/phy-stm32-usbphyc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 937a14fa7448..083593aea53a 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -598,6 +598,18 @@ static int stm32_usbphyc_remove(struct platform_device *pdev) return 0; } +static int __maybe_unused stm32_usbphyc_resume(struct device *dev) +{ + struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev); + + if (usbphyc->switch_setup >= 0) + stm32_usbphyc_switch_setup(usbphyc, usbphyc->switch_setup); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_usbphyc_pm_ops, NULL, stm32_usbphyc_resume); + static const struct of_device_id stm32_usbphyc_of_match[] = { { .compatible = "st,stm32mp1-usbphyc", }, { }, @@ -610,6 +622,7 @@ static struct platform_driver stm32_usbphyc_driver = { .driver = { .of_match_table = stm32_usbphyc_of_match, .name = "stm32-usbphyc", + .pm = &stm32_usbphyc_pm_ops, } }; module_platform_driver(stm32_usbphyc_driver); From 6e59b5aea2f8b55e1c629ac35c147dfc14c418da Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Fri, 15 Oct 2021 18:14:26 +0200 Subject: [PATCH 17/28] dt-bindings: phy: phy-stm32-usbphyc: add optional phy tuning properties This patch adds the description of new optional phy tuning properties for usbphyc phy sub nodes. Signed-off-by: Amelie Delaunay Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211015161427.220784-3-amelie.delaunay@foss.st.com Signed-off-by: Vinod Koul --- .../bindings/phy/phy-stm32-usbphyc.yaml | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml b/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml index 3329f1d33a4f..225128364a63 100644 --- a/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml +++ b/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.yaml @@ -81,6 +81,119 @@ patternProperties: properties: vbus-supply: true + # It can be necessary to adjust the PHY settings to compensate parasitics, which can be due + # to USB connector/receptacle, routing, ESD protection component,... Here is the list of + # all optional parameters to tune the interface of the PHY (HS for High-Speed, FS for Full- + # Speed, LS for Low-Speed) + + st,current-boost-microamp: + description: Current boosting in uA + enum: [ 1000, 2000 ] + + st,no-lsfs-fb-cap: + description: Disables the LS/FS feedback capacitor + type: boolean + + st,decrease-hs-slew-rate: + description: Decreases the HS driver slew rate by 10% + type: boolean + + st,tune-hs-dc-level: + description: | + Tunes the HS driver DC level + - <0> normal level + - <1> increases the level by 5 to 7 mV + - <2> increases the level by 10 to 14 mV + - <3> decreases the level by 5 to 7 mV + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + st,enable-fs-rftime-tuning: + description: Enables the FS rise/fall tuning option + type: boolean + + st,enable-hs-rftime-reduction: + description: Enables the HS rise/fall reduction feature + type: boolean + + st,trim-hs-current: + description: | + Controls HS driver current trimming for choke compensation + - <0> = 18.87 mA target current / nominal + 0% + - <1> = 19.165 mA target current / nominal + 1.56% + - <2> = 19.46 mA target current / nominal + 3.12% + - <3> = 19.755 mA target current / nominal + 4.68% + - <4> = 20.05 mA target current / nominal + 6.24% + - <5> = 20.345 mA target current / nominal + 7.8% + - <6> = 20.64 mA target current / nominal + 9.36% + - <7> = 20.935 mA target current / nominal + 10.92% + - <8> = 21.23 mA target current / nominal + 12.48% + - <9> = 21.525 mA target current / nominal + 14.04% + - <10> = 21.82 mA target current / nominal + 15.6% + - <11> = 22.115 mA target current / nominal + 17.16% + - <12> = 22.458 mA target current / nominal + 19.01% + - <13> = 22.755 mA target current / nominal + 20.58% + - <14> = 23.052 mA target current / nominal + 22.16% + - <15> = 23.348 mA target current / nominal + 23.73% + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + default: 0 + + st,trim-hs-impedance: + description: | + Controls HS driver impedance tuning for choke compensation + - <0> = no impedance offset + - <1> = reduce the impedance by 2 ohms + - <2> = reduce the impedance by 4 ohms + - <3> = reduce the impedance by 6 ohms + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + st,tune-squelch-level: + description: | + Tunes the squelch DC threshold value + - <0> = no shift in threshold + - <1> = threshold shift by +7 mV + - <2> = threshold shift by -5 mV + - <3> = threshold shift by +14 mV + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + st,enable-hs-rx-gain-eq: + description: Enables the HS Rx gain equalizer + type: boolean + + st,tune-hs-rx-offset: + description: | + Adjusts the HS Rx offset + - <0> = no offset + - <1> = offset of +5 mV + - <2> = offset of +10 mV + - <3> = offset of -5 mV + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + default: 0 + + st,no-hs-ftime-ctrl: + description: Disables the HS fall time control of single ended signals during pre-emphasis + type: boolean + + st,no-lsfs-sc: + description: Disables the short circuit protection in LS/FS driver + type: boolean + + st,enable-hs-tx-staggering: + description: Enables the basic staggering in HS Tx mode + type: boolean + allOf: - if: properties: @@ -137,6 +250,14 @@ examples: reg = <0>; phy-supply = <&vdd_usb>; #phy-cells = <0>; + st,tune-hs-dc-level = <2>; + st,enable-fs-rftime-tuning; + st,enable-hs-rftime-reduction; + st,trim-hs-current = <15>; + st,trim-hs-impedance = <1>; + st,tune-squelch-level = <3>; + st,tune-hs-rx-offset = <2>; + st,no-lsfs-sc; connector { compatible = "usb-a-connector"; vbus-supply = <&vbus_sw>; @@ -147,6 +268,14 @@ examples: reg = <1>; phy-supply = <&vdd_usb>; #phy-cells = <1>; + st,tune-hs-dc-level = <2>; + st,enable-fs-rftime-tuning; + st,enable-hs-rftime-reduction; + st,trim-hs-current = <15>; + st,trim-hs-impedance = <1>; + st,tune-squelch-level = <3>; + st,tune-hs-rx-offset = <2>; + st,no-lsfs-sc; }; }; ... From 2f5e9f815a2f9d581b0998710949400fc6e7a51f Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Fri, 15 Oct 2021 18:14:27 +0200 Subject: [PATCH 18/28] phy: stm32: add phy tuning support It can be necessary to adjust the phys settings to compensate parasitics. This patch adds support of new optional properties to configure the tune interface of the phys of stm32-usbphyc. Properties are optional, that's why each property is skipped if not found (-EINVAL). Phy tuning is restored on resume because if deep low power state is achieved, phy tuning configuration is reset. Signed-off-by: Amelie Delaunay Link: https://lore.kernel.org/r/20211015161427.220784-4-amelie.delaunay@foss.st.com Signed-off-by: Vinod Koul --- drivers/phy/st/phy-stm32-usbphyc.c | 190 +++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 083593aea53a..7df6a63ad37b 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -20,6 +20,7 @@ #define STM32_USBPHYC_PLL 0x0 #define STM32_USBPHYC_MISC 0x8 #define STM32_USBPHYC_MONITOR(X) (0x108 + ((X) * 0x100)) +#define STM32_USBPHYC_TUNE(X) (0x10C + ((X) * 0x100)) #define STM32_USBPHYC_VERSION 0x3F4 /* STM32_USBPHYC_PLL bit fields */ @@ -41,6 +42,83 @@ #define STM32_USBPHYC_MON_SEL_LOCKP 0x1F #define STM32_USBPHYC_MON_OUT_LOCKP BIT(3) +/* STM32_USBPHYC_TUNE bit fields */ +#define INCURREN BIT(0) +#define INCURRINT BIT(1) +#define LFSCAPEN BIT(2) +#define HSDRVSLEW BIT(3) +#define HSDRVDCCUR BIT(4) +#define HSDRVDCLEV BIT(5) +#define HSDRVCURINCR BIT(6) +#define FSDRVRFADJ BIT(7) +#define HSDRVRFRED BIT(8) +#define HSDRVCHKITRM GENMASK(12, 9) +#define HSDRVCHKZTRM GENMASK(14, 13) +#define OTPCOMP GENMASK(19, 15) +#define SQLCHCTL GENMASK(21, 20) +#define HDRXGNEQEN BIT(22) +#define HSRXOFF GENMASK(24, 23) +#define HSFALLPREEM BIT(25) +#define SHTCCTCTLPROT BIT(26) +#define STAGSEL BIT(27) + +enum boosting_vals { + BOOST_1000_UA = 1000, + BOOST_2000_UA = 2000, +}; + +enum dc_level_vals { + DC_NOMINAL, + DC_PLUS_5_TO_7_MV, + DC_PLUS_10_TO_14_MV, + DC_MINUS_5_TO_7_MV, + DC_MAX, +}; + +enum current_trim { + CUR_NOMINAL, + CUR_PLUS_1_56_PCT, + CUR_PLUS_3_12_PCT, + CUR_PLUS_4_68_PCT, + CUR_PLUS_6_24_PCT, + CUR_PLUS_7_8_PCT, + CUR_PLUS_9_36_PCT, + CUR_PLUS_10_92_PCT, + CUR_PLUS_12_48_PCT, + CUR_PLUS_14_04_PCT, + CUR_PLUS_15_6_PCT, + CUR_PLUS_17_16_PCT, + CUR_PLUS_19_01_PCT, + CUR_PLUS_20_58_PCT, + CUR_PLUS_22_16_PCT, + CUR_PLUS_23_73_PCT, + CUR_MAX, +}; + +enum impedance_trim { + IMP_NOMINAL, + IMP_MINUS_2_OHMS, + IMP_MINUS_4_OMHS, + IMP_MINUS_6_OHMS, + IMP_MAX, +}; + +enum squelch_level { + SQLCH_NOMINAL, + SQLCH_PLUS_7_MV, + SQLCH_MINUS_5_MV, + SQLCH_PLUS_14_MV, + SQLCH_MAX, +}; + +enum rx_offset { + NO_RX_OFFSET, + RX_OFFSET_PLUS_5_MV, + RX_OFFSET_PLUS_10_MV, + RX_OFFSET_MINUS_5_MV, + RX_OFFSET_MAX, +}; + /* STM32_USBPHYC_VERSION bit fields */ #define MINREV GENMASK(3, 0) #define MAJREV GENMASK(7, 4) @@ -60,6 +138,7 @@ struct stm32_usbphyc_phy { struct regulator *vbus; u32 index; bool active; + u32 tune; }; struct stm32_usbphyc { @@ -375,6 +454,107 @@ static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc) return ret; } +static void stm32_usbphyc_phy_tuning(struct stm32_usbphyc *usbphyc, + struct device_node *np, u32 index) +{ + struct stm32_usbphyc_phy *usbphyc_phy = usbphyc->phys[index]; + u32 reg = STM32_USBPHYC_TUNE(index); + u32 otpcomp, val; + int ret; + + /* Backup OTP compensation code */ + otpcomp = FIELD_GET(OTPCOMP, readl_relaxed(usbphyc->base + reg)); + + ret = of_property_read_u32(np, "st,current-boost-microamp", &val); + if (ret != -EINVAL) { + if (!ret && (val == BOOST_1000_UA || val == BOOST_2000_UA)) { + val = (val == BOOST_2000_UA) ? 1 : 0; + usbphyc_phy->tune |= INCURREN | FIELD_PREP(INCURRINT, val); + } else { + dev_warn(usbphyc->dev, "phy%d: invalid st,current-boost-microamp\n", index); + } + } + + if (!of_property_read_bool(np, "st,no-lsfs-fb-cap")) + usbphyc_phy->tune |= LFSCAPEN; + + if (of_property_read_bool(np, "st,slow-hs-slew-rate")) + usbphyc_phy->tune |= HSDRVSLEW; + + ret = of_property_read_u32(np, "st,tune-hs-dc-level", &val); + if (ret != -EINVAL) { + if (!ret && val < DC_MAX) { + if (val == DC_MINUS_5_TO_7_MV) {/* Decreases HS driver DC level */ + usbphyc_phy->tune |= HSDRVDCCUR; + } else if (val > 0) { /* Increases HS driver DC level */ + val = (val == DC_PLUS_10_TO_14_MV) ? 1 : 0; + usbphyc_phy->tune |= HSDRVCURINCR | FIELD_PREP(HSDRVDCLEV, val); + } + } else { + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-dc-level\n", index); + } + } + + if (of_property_read_bool(np, "st,enable-fs-rftime-tuning")) + usbphyc_phy->tune |= FSDRVRFADJ; + + if (of_property_read_bool(np, "st,enable-hs-rftime-reduction")) + usbphyc_phy->tune |= HSDRVRFRED; + + ret = of_property_read_u32(np, "st,trim-hs-current", &val); + if (ret != -EINVAL) { + if (!ret && val < CUR_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKITRM, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-current\n", index); + } + + ret = of_property_read_u32(np, "st,trim-hs-impedance", &val); + if (ret != -EINVAL) { + if (!ret && val < IMP_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSDRVCHKZTRM, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,trim-hs-impedance\n", index); + } + + ret = of_property_read_u32(np, "st,tune-squelch-level", &val); + if (ret != -EINVAL) { + if (!ret && val < SQLCH_MAX) + usbphyc_phy->tune |= FIELD_PREP(SQLCHCTL, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-squelch\n", index); + } + + if (of_property_read_bool(np, "st,enable-hs-rx-gain-eq")) + usbphyc_phy->tune |= HDRXGNEQEN; + + ret = of_property_read_u32(np, "st,tune-hs-rx-offset", &val); + if (ret != -EINVAL) { + if (!ret && val < RX_OFFSET_MAX) + usbphyc_phy->tune |= FIELD_PREP(HSRXOFF, val); + else + dev_warn(usbphyc->dev, "phy%d: invalid st,tune-hs-rx-offset\n", index); + } + + if (of_property_read_bool(np, "st,no-hs-ftime-ctrl")) + usbphyc_phy->tune |= HSFALLPREEM; + + if (!of_property_read_bool(np, "st,no-lsfs-sc")) + usbphyc_phy->tune |= SHTCCTCTLPROT; + + if (of_property_read_bool(np, "st,enable-hs-tx-staggering")) + usbphyc_phy->tune |= STAGSEL; + + /* Restore OTP compensation code */ + usbphyc_phy->tune |= FIELD_PREP(OTPCOMP, otpcomp); + + /* + * By default, if no st,xxx tuning property is used, usbphyc_phy->tune is equal to + * STM32_USBPHYC_TUNE reset value (LFSCAPEN | SHTCCTCTLPROT | OTPCOMP). + */ + writel_relaxed(usbphyc_phy->tune, usbphyc->base + reg); +} + static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc, u32 utmi_switch) { @@ -550,6 +730,9 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) usbphyc->phys[port]->vbus = NULL; } + /* Configure phy tuning */ + stm32_usbphyc_phy_tuning(usbphyc, child, index); + port++; } @@ -601,10 +784,17 @@ static int stm32_usbphyc_remove(struct platform_device *pdev) static int __maybe_unused stm32_usbphyc_resume(struct device *dev) { struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev); + struct stm32_usbphyc_phy *usbphyc_phy; + int port; if (usbphyc->switch_setup >= 0) stm32_usbphyc_switch_setup(usbphyc, usbphyc->switch_setup); + for (port = 0; port < usbphyc->nphys; port++) { + usbphyc_phy = usbphyc->phys[port]; + writel_relaxed(usbphyc_phy->tune, usbphyc->base + STM32_USBPHYC_TUNE(port)); + } + return 0; } From d8b951abd7ff7b126398c51d33671a6909541c68 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 20 Oct 2021 21:06:34 +0800 Subject: [PATCH 19/28] dt-bindings: phy: qcom,qmp: IPQ6018 and IPQ8074 PCIe PHY require no supply The qmp-phy driver suggests that 'vdda-phy-supply' and 'vdda-pll-supply' are not required for IPQ6018 and IPQ8074 QMP PCIe PHY. Update the bindings to reflect it. While at it, also correct the clock properties for IPQ8074 QMP PCIe PHY. And as the result, 'qcom,ipq8074-qmp-pcie-phy' and 'qcom,ipq6018-qmp-pcie-phy' share the same clock, reset and supply bindings. Signed-off-by: Shawn Guo Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211020130634.26194-1-shawn.guo@linaro.org Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,qmp-phy.yaml | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index 647b45cfec84..630ceaf915e2 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -117,8 +117,6 @@ required: - clock-names - resets - reset-names - - vdda-phy-supply - - vdda-pll-supply additionalProperties: false @@ -151,6 +149,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -177,6 +178,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -205,6 +209,9 @@ allOf: - const: phy - const: common - const: cfg + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -234,6 +241,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -254,6 +264,9 @@ allOf: reset-names: items: - const: ufsphy + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -279,34 +292,16 @@ allOf: reset-names: items: - const: ufsphy - - if: - properties: - compatible: - contains: - enum: - - qcom,ipq8074-qmp-pcie-phy - then: - properties: - clocks: - items: - - description: pipe clk. - clock-names: - items: - - const: pipe_clk - resets: - items: - - description: reset of phy block. - - description: phy common block reset. - reset-names: - items: - - const: phy - - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: contains: enum: - qcom,ipq6018-qmp-pcie-phy + - qcom,ipq8074-qmp-pcie-phy then: properties: clocks: @@ -357,6 +352,9 @@ allOf: reset-names: items: - const: phy + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -388,6 +386,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -415,6 +416,9 @@ allOf: items: - const: phy - const: common + required: + - vdda-phy-supply + - vdda-pll-supply - if: properties: compatible: @@ -441,6 +445,9 @@ allOf: items: - const: phy_phy - const: phy + required: + - vdda-phy-supply + - vdda-pll-supply examples: - | From 8d55027f4e2c04146a75fb63371ab96ccc887f2c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 Sep 2021 14:00:38 +0300 Subject: [PATCH 20/28] phy: ti: gmii-sel: check of_get_address() for failure Smatch complains that if of_get_address() returns NULL, then "size" isn't initialized. Also it would lead to an Oops. Fixes: 7f78322cdd67 ("phy: ti: gmii-sel: retrieve ports number and base offset from dt") Signed-off-by: Dan Carpenter Reviewed-by: Grygorii Strashko Link: https://lore.kernel.org/r/20210914110038.GB11657@kili Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-gmii-sel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 5fd2e8a08bfc..d0ab69750c6b 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -320,6 +320,8 @@ static int phy_gmii_sel_init_ports(struct phy_gmii_sel_priv *priv) u64 size; offset = of_get_address(dev->of_node, 0, &size, NULL); + if (!offset) + return -EINVAL; priv->num_ports = size / sizeof(u32); if (!priv->num_ports) return -EINVAL; From aef096dbf514a9f70a1d81824d3b21d5266e0d6e Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Wed, 22 Sep 2021 14:37:32 +0200 Subject: [PATCH 21/28] phy: cadence-torrent: Migrate to clk_hw based registration and OF APIs Use clk_hw based provider APIs to register clks to remove the usage of deprecated APIs. Signed-off-by: Swapnil Jakhade Link: https://lore.kernel.org/r/20210922123735.21927-2-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-torrent.c | 30 ++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 415ace64adc5..ecb1aa883c05 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -235,6 +235,8 @@ #define PHY_PMA_CMN_CTRL2 0x0001U #define PHY_PMA_PLL_RAW_CTRL 0x0003U +#define CDNS_TORRENT_OUTPUT_CLOCKS 1 + static const char * const clk_names[] = { [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver", }; @@ -333,8 +335,7 @@ struct cdns_torrent_phy { struct regmap_field *phy_pma_pll_raw_ctrl; struct regmap_field *phy_reset_ctrl; struct regmap_field *phy_pcs_iso_link_ctrl_1[MAX_NUM_LANES]; - struct clk *clks[CDNS_TORRENT_REFCLK_DRIVER + 1]; - struct clk_onecell_data clk_data; + struct clk_hw_onecell_data *clk_hw_data; }; enum phy_powerstate { @@ -1659,8 +1660,9 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph const char *parent_name; struct regmap *regmap; char clk_name[100]; + struct clk_hw *hw; struct clk *clk; - int i; + int i, ret; derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL); if (!derived_refclk) @@ -1706,11 +1708,12 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph derived_refclk->hw.init = init; - clk = devm_clk_register(dev, &derived_refclk->hw); - if (IS_ERR(clk)) - return PTR_ERR(clk); + hw = &derived_refclk->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; - cdns_phy->clks[CDNS_TORRENT_REFCLK_DRIVER] = clk; + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_REFCLK_DRIVER] = hw; return 0; } @@ -2188,18 +2191,23 @@ static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy) { struct device *dev = cdns_phy->dev; struct device_node *node = dev->of_node; + struct clk_hw_onecell_data *data; int ret; + data = devm_kzalloc(dev, struct_size(data, hws, CDNS_TORRENT_OUTPUT_CLOCKS), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->num = CDNS_TORRENT_OUTPUT_CLOCKS; + cdns_phy->clk_hw_data = data; + ret = cdns_torrent_derived_refclk_register(cdns_phy); if (ret) { dev_err(dev, "failed to register derived refclk\n"); return ret; } - cdns_phy->clk_data.clks = cdns_phy->clks; - cdns_phy->clk_data.clk_num = CDNS_TORRENT_REFCLK_DRIVER + 1; - - ret = of_clk_add_provider(node, of_clk_src_onecell_get, &cdns_phy->clk_data); + ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data); if (ret) { dev_err(dev, "Failed to add clock provider: %s\n", node->name); return ret; From f9aec1648df09d55436a0e3a94acff1df507751f Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Wed, 22 Sep 2021 14:37:33 +0200 Subject: [PATCH 22/28] dt-bindings: phy: cadence-torrent: Add clock IDs for derived and received refclk Add clock IDs for derived and received reference clock output. Signed-off-by: Swapnil Jakhade Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210922123735.21927-3-sjakhade@cadence.com Signed-off-by: Vinod Koul --- include/dt-bindings/phy/phy-cadence.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/dt-bindings/phy/phy-cadence.h b/include/dt-bindings/phy/phy-cadence.h index 4652bcb86265..24fdc9e11bd6 100644 --- a/include/dt-bindings/phy/phy-cadence.h +++ b/include/dt-bindings/phy/phy-cadence.h @@ -12,6 +12,8 @@ #define TORRENT_SERDES_INTERNAL_SSC 2 #define CDNS_TORRENT_REFCLK_DRIVER 0 +#define CDNS_TORRENT_DERIVED_REFCLK 1 +#define CDNS_TORRENT_RECEIVED_REFCLK 2 /* Sierra */ #define CDNS_SIERRA_PLL_CMNLC 0 From 235bde4f440a32a528bcedc48dbfa0ec4a039060 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Wed, 22 Sep 2021 14:37:34 +0200 Subject: [PATCH 23/28] phy: cadence-torrent: Model reference clock driver as a clock to enable derived refclk When reference clock driver is enabled, either derived or received refclk is output on cmn_refclk_p/m. Update the reference clock driver implementation by modelling reference clock driver as a "clock" with derived reference clock set as its default parent. The support for received reference clock will be added in a separate patch. Signed-off-by: Swapnil Jakhade Link: https://lore.kernel.org/r/20210922123735.21927-4-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-torrent.c | 171 ++++++++++++++++++---- 1 file changed, 139 insertions(+), 32 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index ecb1aa883c05..615aca6bd52b 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -235,10 +235,11 @@ #define PHY_PMA_CMN_CTRL2 0x0001U #define PHY_PMA_PLL_RAW_CTRL 0x0003U -#define CDNS_TORRENT_OUTPUT_CLOCKS 1 +#define CDNS_TORRENT_OUTPUT_CLOCKS 2 static const char * const clk_names[] = { [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver", + [CDNS_TORRENT_DERIVED_REFCLK] = "refclk-der", }; static const struct reg_field phy_pll_cfg = @@ -261,10 +262,12 @@ static const struct reg_field phy_pcs_iso_link_ctrl_1 = static const struct reg_field phy_pipe_cmn_ctrl1_0 = REG_FIELD(PHY_PIPE_CMN_CTRL1, 0, 0); -#define REFCLK_OUT_NUM_CMN_CONFIG 5 +static const struct reg_field cmn_cdiag_refclk_ovrd_4 = + REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4); + +#define REFCLK_OUT_NUM_CMN_CONFIG 4 enum cdns_torrent_refclk_out_cmn { - CMN_CDIAG_REFCLK_OVRD_4, CMN_CDIAG_REFCLK_DRV0_CTRL_1, CMN_CDIAG_REFCLK_DRV0_CTRL_4, CMN_CDIAG_REFCLK_DRV0_CTRL_5, @@ -272,7 +275,6 @@ enum cdns_torrent_refclk_out_cmn { }; static const struct reg_field refclk_out_cmn_cfg[] = { - [CMN_CDIAG_REFCLK_OVRD_4] = REG_FIELD(CMN_CDIAG_REFCLK_OVRD, 4, 4), [CMN_CDIAG_REFCLK_DRV0_CTRL_1] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 1, 1), [CMN_CDIAG_REFCLK_DRV0_CTRL_4] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 4, 4), [CMN_CDIAG_REFCLK_DRV0_CTRL_5] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 5, 5), @@ -330,6 +332,8 @@ struct cdns_torrent_phy { struct regmap *regmap_phy_pcs_lane_cdb[MAX_NUM_LANES]; struct regmap *regmap_dptx_phy_reg; struct regmap_field *phy_pll_cfg; + struct regmap_field *phy_pipe_cmn_ctrl1_0; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; struct regmap_field *phy_pma_cmn_ctrl_1; struct regmap_field *phy_pma_cmn_ctrl_2; struct regmap_field *phy_pma_pll_raw_ctrl; @@ -345,10 +349,19 @@ enum phy_powerstate { POWERSTATE_A3 = 3, }; +struct cdns_torrent_refclk_driver { + struct clk_hw hw; + struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG]; + struct clk_init_data clk_data; +}; + +#define to_cdns_torrent_refclk_driver(_hw) \ + container_of(_hw, struct cdns_torrent_refclk_driver, hw) + struct cdns_torrent_derived_refclk { struct clk_hw hw; struct regmap_field *phy_pipe_cmn_ctrl1_0; - struct regmap_field *cmn_fields[REFCLK_OUT_NUM_CMN_CONFIG]; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; struct clk_init_data clk_data; }; @@ -1618,11 +1631,7 @@ static int cdns_torrent_derived_refclk_enable(struct clk_hw *hw) { struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0); - regmap_field_write(derived_refclk->cmn_fields[CMN_CDIAG_REFCLK_OVRD_4], 1); + regmap_field_write(derived_refclk->cmn_cdiag_refclk_ovrd_4, 1); regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 1); return 0; @@ -1633,6 +1642,7 @@ static void cdns_torrent_derived_refclk_disable(struct clk_hw *hw) struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); regmap_field_write(derived_refclk->phy_pipe_cmn_ctrl1_0, 0); + regmap_field_write(derived_refclk->cmn_cdiag_refclk_ovrd_4, 0); } static int cdns_torrent_derived_refclk_is_enabled(struct clk_hw *hw) @@ -1640,7 +1650,7 @@ static int cdns_torrent_derived_refclk_is_enabled(struct clk_hw *hw) struct cdns_torrent_derived_refclk *derived_refclk = to_cdns_torrent_derived_refclk(hw); int val; - regmap_field_read(derived_refclk->phy_pipe_cmn_ctrl1_0, &val); + regmap_field_read(derived_refclk->cmn_cdiag_refclk_ovrd_4, &val); return !!val; } @@ -1655,21 +1665,19 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph { struct cdns_torrent_derived_refclk *derived_refclk; struct device *dev = cdns_phy->dev; - struct regmap_field *field; struct clk_init_data *init; const char *parent_name; - struct regmap *regmap; char clk_name[100]; struct clk_hw *hw; struct clk *clk; - int i, ret; + int ret; derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL); if (!derived_refclk) return -ENOMEM; snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), - clk_names[CDNS_TORRENT_REFCLK_DRIVER]); + clk_names[CDNS_TORRENT_DERIVED_REFCLK]); clk = devm_clk_get_optional(dev, "phy_en_refclk"); if (IS_ERR(clk)) { @@ -1688,23 +1696,8 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph init->flags = 0; init->name = clk_name; - regmap = cdns_phy->regmap_phy_pcs_common_cdb; - field = devm_regmap_field_alloc(dev, regmap, phy_pipe_cmn_ctrl1_0); - if (IS_ERR(field)) { - dev_err(dev, "phy_pipe_cmn_ctrl1_0 reg field init failed\n"); - return PTR_ERR(field); - } - derived_refclk->phy_pipe_cmn_ctrl1_0 = field; - - regmap = cdns_phy->regmap_common_cdb; - for (i = 0; i < REFCLK_OUT_NUM_CMN_CONFIG; i++) { - field = devm_regmap_field_alloc(dev, regmap, refclk_out_cmn_cfg[i]); - if (IS_ERR(field)) { - dev_err(dev, "CMN reg field init failed\n"); - return PTR_ERR(field); - } - derived_refclk->cmn_fields[i] = field; - } + derived_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0; + derived_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4; derived_refclk->hw.init = init; @@ -1713,6 +1706,98 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph if (ret) return ret; + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK] = hw; + + return 0; +} + +static int cdns_torrent_refclk_driver_enable(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0); + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1); + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0); + + return 0; +} + +static void cdns_torrent_refclk_driver_disable(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 1); +} + +static int cdns_torrent_refclk_driver_is_enabled(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + int val; + + regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], &val); + + return !val; +} + +static const struct clk_ops cdns_torrent_refclk_driver_ops = { + .enable = cdns_torrent_refclk_driver_enable, + .disable = cdns_torrent_refclk_driver_disable, + .is_enabled = cdns_torrent_refclk_driver_is_enabled, +}; + +static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy) +{ + struct cdns_torrent_refclk_driver *refclk_driver; + struct device *dev = cdns_phy->dev; + struct regmap_field *field; + struct clk_init_data *init; + const char *parent_name; + struct regmap *regmap; + char clk_name[100]; + struct clk_hw *hw; + int i, ret; + + refclk_driver = devm_kzalloc(dev, sizeof(*refclk_driver), GFP_KERNEL); + if (!refclk_driver) + return -ENOMEM; + + hw = cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK]; + if (IS_ERR_OR_NULL(hw)) { + dev_err(dev, "No parent clock for refclk driver clock\n"); + return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT; + } + parent_name = clk_hw_get_name(hw); + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_TORRENT_REFCLK_DRIVER]); + + init = &refclk_driver->clk_data; + + init->ops = &cdns_torrent_refclk_driver_ops; + init->flags = 0; + init->parent_names = &parent_name; + init->num_parents = 1; + init->name = clk_name; + + regmap = cdns_phy->regmap_common_cdb; + + for (i = 0; i < REFCLK_OUT_NUM_CMN_CONFIG; i++) { + field = devm_regmap_field_alloc(dev, regmap, refclk_out_cmn_cfg[i]); + if (IS_ERR(field)) { + dev_err(dev, "Refclk driver CMN reg field init failed\n"); + return PTR_ERR(field); + } + refclk_driver->cmn_fields[i] = field; + } + + refclk_driver->hw.init = init; + + hw = &refclk_driver->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_REFCLK_DRIVER] = hw; return 0; @@ -1768,6 +1853,22 @@ static int cdns_torrent_regfield_init(struct cdns_torrent_phy *cdns_phy) } cdns_phy->phy_pll_cfg = field; + regmap = cdns_phy->regmap_phy_pcs_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, phy_pipe_cmn_ctrl1_0); + if (IS_ERR(field)) { + dev_err(dev, "phy_pipe_cmn_ctrl1_0 reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->phy_pipe_cmn_ctrl1_0 = field; + + regmap = cdns_phy->regmap_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, cmn_cdiag_refclk_ovrd_4); + if (IS_ERR(field)) { + dev_err(dev, "cmn_cdiag_refclk_ovrd_4 reg field init failed\n"); + return PTR_ERR(field); + } + cdns_phy->cmn_cdiag_refclk_ovrd_4 = field; + regmap = cdns_phy->regmap_phy_pma_common_cdb; field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_1); if (IS_ERR(field)) { @@ -2207,6 +2308,12 @@ static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy) return ret; } + ret = cdns_torrent_refclk_driver_register(cdns_phy); + if (ret) { + dev_err(dev, "failed to register refclk driver\n"); + return ret; + } + ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data); if (ret) { dev_err(dev, "Failed to add clock provider: %s\n", node->name); From 785a4e688cd29adfe9a5f7a7d20f877f5b8ac4fa Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Wed, 22 Sep 2021 14:37:35 +0200 Subject: [PATCH 24/28] phy: cadence-torrent: Add support to output received reference clock Add support to output received reference clock. Model the received reference clock as an alternate parent of reference clock driver clock. When received refclk is selected to output on cmn_refclk_p/m, this is the internal reference clock driven on the pma_cmn_refclk_int. Signed-off-by: Swapnil Jakhade Link: https://lore.kernel.org/r/20210922123735.21927-5-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-torrent.c | 159 ++++++++++++++++++++-- 1 file changed, 148 insertions(+), 11 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 615aca6bd52b..5786166133d3 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -235,11 +235,12 @@ #define PHY_PMA_CMN_CTRL2 0x0001U #define PHY_PMA_PLL_RAW_CTRL 0x0003U -#define CDNS_TORRENT_OUTPUT_CLOCKS 2 +#define CDNS_TORRENT_OUTPUT_CLOCKS 3 static const char * const clk_names[] = { [CDNS_TORRENT_REFCLK_DRIVER] = "refclk-driver", [CDNS_TORRENT_DERIVED_REFCLK] = "refclk-der", + [CDNS_TORRENT_RECEIVED_REFCLK] = "refclk-rec", }; static const struct reg_field phy_pll_cfg = @@ -281,6 +282,13 @@ static const struct reg_field refclk_out_cmn_cfg[] = { [CMN_CDIAG_REFCLK_DRV0_CTRL_6] = REG_FIELD(CMN_CDIAG_REFCLK_DRV0_CTRL, 6, 6), }; +static const int refclk_driver_parent_index[] = { + CDNS_TORRENT_DERIVED_REFCLK, + CDNS_TORRENT_RECEIVED_REFCLK +}; + +static u32 cdns_torrent_refclk_driver_mux_table[] = { 1, 0 }; + enum cdns_torrent_phy_type { TYPE_NONE, TYPE_DP, @@ -368,6 +376,16 @@ struct cdns_torrent_derived_refclk { #define to_cdns_torrent_derived_refclk(_hw) \ container_of(_hw, struct cdns_torrent_derived_refclk, hw) +struct cdns_torrent_received_refclk { + struct clk_hw hw; + struct regmap_field *phy_pipe_cmn_ctrl1_0; + struct regmap_field *cmn_cdiag_refclk_ovrd_4; + struct clk_init_data clk_data; +}; + +#define to_cdns_torrent_received_refclk(_hw) \ + container_of(_hw, struct cdns_torrent_received_refclk, hw) + struct cdns_reg_pairs { u32 val; u32 off; @@ -1711,12 +1729,94 @@ static int cdns_torrent_derived_refclk_register(struct cdns_torrent_phy *cdns_ph return 0; } +static int cdns_torrent_received_refclk_enable(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + + regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 1); + + return 0; +} + +static void cdns_torrent_received_refclk_disable(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + + regmap_field_write(received_refclk->phy_pipe_cmn_ctrl1_0, 0); +} + +static int cdns_torrent_received_refclk_is_enabled(struct clk_hw *hw) +{ + struct cdns_torrent_received_refclk *received_refclk = to_cdns_torrent_received_refclk(hw); + int val, cmn_val; + + regmap_field_read(received_refclk->phy_pipe_cmn_ctrl1_0, &val); + regmap_field_read(received_refclk->cmn_cdiag_refclk_ovrd_4, &cmn_val); + + return val && !cmn_val; +} + +static const struct clk_ops cdns_torrent_received_refclk_ops = { + .enable = cdns_torrent_received_refclk_enable, + .disable = cdns_torrent_received_refclk_disable, + .is_enabled = cdns_torrent_received_refclk_is_enabled, +}; + +static int cdns_torrent_received_refclk_register(struct cdns_torrent_phy *cdns_phy) +{ + struct cdns_torrent_received_refclk *received_refclk; + struct device *dev = cdns_phy->dev; + struct clk_init_data *init; + const char *parent_name; + char clk_name[100]; + struct clk_hw *hw; + struct clk *clk; + int ret; + + received_refclk = devm_kzalloc(dev, sizeof(*received_refclk), GFP_KERNEL); + if (!received_refclk) + return -ENOMEM; + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_TORRENT_RECEIVED_REFCLK]); + + clk = devm_clk_get_optional(dev, "phy_en_refclk"); + if (IS_ERR(clk)) { + dev_err(dev, "No parent clock for received_refclk\n"); + return PTR_ERR(clk); + } + + init = &received_refclk->clk_data; + + if (clk) { + parent_name = __clk_get_name(clk); + init->parent_names = &parent_name; + init->num_parents = 1; + } + init->ops = &cdns_torrent_received_refclk_ops; + init->flags = 0; + init->name = clk_name; + + received_refclk->phy_pipe_cmn_ctrl1_0 = cdns_phy->phy_pipe_cmn_ctrl1_0; + received_refclk->cmn_cdiag_refclk_ovrd_4 = cdns_phy->cmn_cdiag_refclk_ovrd_4; + + received_refclk->hw.init = init; + + hw = &received_refclk->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + cdns_phy->clk_hw_data->hws[CDNS_TORRENT_RECEIVED_REFCLK] = hw; + + return 0; +} + static int cdns_torrent_refclk_driver_enable(struct clk_hw *hw) { struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_6], 0); - regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_5], 1); regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_1], 0); @@ -1740,10 +1840,30 @@ static int cdns_torrent_refclk_driver_is_enabled(struct clk_hw *hw) return !val; } +static u8 cdns_torrent_refclk_driver_get_parent(struct clk_hw *hw) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + unsigned int val; + + regmap_field_read(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], &val); + return clk_mux_val_to_index(hw, cdns_torrent_refclk_driver_mux_table, 0, val); +} + +static int cdns_torrent_refclk_driver_set_parent(struct clk_hw *hw, u8 index) +{ + struct cdns_torrent_refclk_driver *refclk_driver = to_cdns_torrent_refclk_driver(hw); + unsigned int val; + + val = cdns_torrent_refclk_driver_mux_table[index]; + return regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], val); +} + static const struct clk_ops cdns_torrent_refclk_driver_ops = { .enable = cdns_torrent_refclk_driver_enable, .disable = cdns_torrent_refclk_driver_disable, .is_enabled = cdns_torrent_refclk_driver_is_enabled, + .set_parent = cdns_torrent_refclk_driver_set_parent, + .get_parent = cdns_torrent_refclk_driver_get_parent, }; static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy) @@ -1752,7 +1872,8 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy struct device *dev = cdns_phy->dev; struct regmap_field *field; struct clk_init_data *init; - const char *parent_name; + const char **parent_names; + unsigned int num_parents; struct regmap *regmap; char clk_name[100]; struct clk_hw *hw; @@ -1762,12 +1883,19 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy if (!refclk_driver) return -ENOMEM; - hw = cdns_phy->clk_hw_data->hws[CDNS_TORRENT_DERIVED_REFCLK]; - if (IS_ERR_OR_NULL(hw)) { - dev_err(dev, "No parent clock for refclk driver clock\n"); - return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT; + num_parents = ARRAY_SIZE(refclk_driver_parent_index); + parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents), GFP_KERNEL); + if (!parent_names) + return -ENOMEM; + + for (i = 0; i < num_parents; i++) { + hw = cdns_phy->clk_hw_data->hws[refclk_driver_parent_index[i]]; + if (IS_ERR_OR_NULL(hw)) { + dev_err(dev, "No parent clock for refclk driver clock\n"); + return IS_ERR(hw) ? PTR_ERR(hw) : -ENOENT; + } + parent_names[i] = clk_hw_get_name(hw); } - parent_name = clk_hw_get_name(hw); snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), clk_names[CDNS_TORRENT_REFCLK_DRIVER]); @@ -1775,9 +1903,9 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy init = &refclk_driver->clk_data; init->ops = &cdns_torrent_refclk_driver_ops; - init->flags = 0; - init->parent_names = &parent_name; - init->num_parents = 1; + init->flags = CLK_SET_RATE_NO_REPARENT; + init->parent_names = parent_names; + init->num_parents = num_parents; init->name = clk_name; regmap = cdns_phy->regmap_common_cdb; @@ -1791,6 +1919,9 @@ static int cdns_torrent_refclk_driver_register(struct cdns_torrent_phy *cdns_phy refclk_driver->cmn_fields[i] = field; } + /* Enable Derived reference clock as default */ + regmap_field_write(refclk_driver->cmn_fields[CMN_CDIAG_REFCLK_DRV0_CTRL_4], 1); + refclk_driver->hw.init = init; hw = &refclk_driver->hw; @@ -2308,6 +2439,12 @@ static int cdns_torrent_clk_register(struct cdns_torrent_phy *cdns_phy) return ret; } + ret = cdns_torrent_received_refclk_register(cdns_phy); + if (ret) { + dev_err(dev, "failed to register received refclk\n"); + return ret; + } + ret = cdns_torrent_refclk_driver_register(cdns_phy); if (ret) { dev_err(dev, "failed to register refclk driver\n"); From 26f71abef580537d978f6299330689f029ee1e6c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 Oct 2021 18:56:04 +0300 Subject: [PATCH 25/28] phy: qcom-qmp: another fix for the sc8180x PCIe definition Commit f839f14e24f2 ("phy: qcom-qmp: Add sc8180x PCIe support") added SC8180X PCIe tables, but used sm8250_qmp_pcie_serdes_tbl as a serdes table because of the copy paste error. Commit bfccd9a71a08 ("phy: qcom-qmp: Fix sc8180x PCIe definition") corrected part of this mistake by pointing serdes_tbl to sc8180x_qmp_pcie_serdes_tbl, however the serdes_tbl_num field was not updated to use sc8180x table. So let's now fix the serdes_tbl_num field too. Fixes: bfccd9a71a08 ("phy: qcom-qmp: Fix sc8180x PCIe definition") Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211020155604.1374530-1-dmitry.baryshkov@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 32123a60be97..456a59d8c7d0 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -3745,7 +3745,7 @@ static const struct qmp_phy_cfg sc8180x_pciephy_cfg = { .nlanes = 1, .serdes_tbl = sc8180x_qmp_pcie_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl), + .serdes_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_serdes_tbl), .tx_tbl = sc8180x_qmp_pcie_tx_tbl, .tx_tbl_num = ARRAY_SIZE(sc8180x_qmp_pcie_tx_tbl), .rx_tbl = sc8180x_qmp_pcie_rx_tbl, From 21b89120be87b3fd2454e354bf466e5c4fea9624 Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Fri, 15 Oct 2021 04:01:51 -0400 Subject: [PATCH 26/28] phy: hisilicon: Add of_node_put() in phy-hisi-inno-usb2 Fix following coccicheck warning: ./drivers/phy/hisilicon/phy-hisi-inno-usb2.c:138:1-23: WARNING: Function for_each_child_of_node should have of_node_put() before break Early exits from for_each_child_of_node should decrement the node reference counter. Signed-off-by: Wan Jiabing Link: https://lore.kernel.org/r/20211015080154.16016-1-wanjiabing@vivo.com Signed-off-by: Vinod Koul --- drivers/phy/hisilicon/phy-hisi-inno-usb2.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c index 34a6a9a1ceb2..b133ae06757a 100644 --- a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c +++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c @@ -140,14 +140,19 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) struct phy *phy; rst = of_reset_control_get_exclusive(child, NULL); - if (IS_ERR(rst)) + if (IS_ERR(rst)) { + of_node_put(child); return PTR_ERR(rst); + } + priv->ports[i].utmi_rst = rst; priv->ports[i].priv = priv; phy = devm_phy_create(dev, child, &hisi_inno_phy_ops); - if (IS_ERR(phy)) + if (IS_ERR(phy)) { + of_node_put(child); return PTR_ERR(phy); + } phy_set_bus_width(phy, 8); phy_set_drvdata(phy, &priv->ports[i]); @@ -155,6 +160,7 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) if (i > INNO_PHY_PORT_NUM) { dev_warn(dev, "Support %d ports in maximum\n", i); + of_node_put(child); break; } } From b475bf0ec40a2b13fb32ef62f5706576d5858460 Mon Sep 17 00:00:00 2001 From: Sandeep Maheswaram Date: Mon, 25 Oct 2021 09:49:35 +0530 Subject: [PATCH 27/28] phy: qcom-snps: Correct the FSEL_MASK The FSEL_MASK which selects the refclock is defined incorrectly. It should be [4:6] not [5:7]. Due to this incorrect definition, the BIT(7) in USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 is reset which keeps PHY analog blocks ON during suspend. Fix this issue by correctly defining the FSEL_MASK. Fixes: 51e8114f80d0 ("phy: qcom-snps: Add SNPS USB PHY driver for QCOM based SOCs") Signed-off-by: Sandeep Maheswaram Link: https://lore.kernel.org/r/1635135575-5668-1-git-send-email-quic_c_sanm@quicinc.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index ae4bac024c7b..7e61202aa234 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -33,7 +33,7 @@ #define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54) #define RETENABLEN BIT(3) -#define FSEL_MASK GENMASK(7, 5) +#define FSEL_MASK GENMASK(6, 4) #define FSEL_DEFAULT (0x3 << 4) #define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58) From b4dc97ab0a629eda8bda20d96ef47dac08a505d9 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Thu, 9 Sep 2021 15:21:49 +0800 Subject: [PATCH 28/28] phy: Sparx5 Eth SerDes: Fix return value check in sparx5_serdes_probe() In case of error, the function devm_ioremap() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Fixes: 2ff8a1eeb5aa ("phy: Add Sparx5 ethernet serdes PHY driver") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Link: https://lore.kernel.org/r/20210909072149.2934047-1-yangyingliang@huawei.com Signed-off-by: Vinod Koul --- drivers/phy/microchip/sparx5_serdes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/microchip/sparx5_serdes.c b/drivers/phy/microchip/sparx5_serdes.c index 4076580fc2cd..ab1b0986aa67 100644 --- a/drivers/phy/microchip/sparx5_serdes.c +++ b/drivers/phy/microchip/sparx5_serdes.c @@ -2475,10 +2475,10 @@ static int sparx5_serdes_probe(struct platform_device *pdev) return -EINVAL; } iomem = devm_ioremap(priv->dev, iores->start, resource_size(iores)); - if (IS_ERR(iomem)) { + if (!iomem) { dev_err(priv->dev, "Unable to get serdes registers: %s\n", iores->name); - return PTR_ERR(iomem); + return -ENOMEM; } for (idx = 0; idx < ARRAY_SIZE(sparx5_serdes_iomap); idx++) { struct sparx5_serdes_io_resource *iomap = &sparx5_serdes_iomap[idx];