diff options
Diffstat (limited to 'drivers/phy')
25 files changed, 2233 insertions, 481 deletions
diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig index 71801e30d601..617cf073e9aa 100644 --- a/drivers/phy/amlogic/Kconfig +++ b/drivers/phy/amlogic/Kconfig @@ -3,12 +3,13 @@ # Phy drivers for Amlogic platforms # config PHY_MESON8B_USB2 - tristate "Meson8, Meson8b and GXBB USB2 PHY driver" + tristate "Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver" default ARCH_MESON depends on OF && (ARCH_MESON || COMPILE_TEST) depends on USB_SUPPORT select USB_COMMON select GENERIC_PHY + select REGMAP_MMIO help Enable this to support the Meson USB2 PHYs found in Meson8, Meson8b and GXBB SoCs. @@ -26,18 +27,6 @@ config PHY_MESON_GXL_USB2 GXL and GXM SoCs. If unsure, say N. -config PHY_MESON_GXL_USB3 - tristate "Meson GXL and GXM USB3 PHY drivers" - default ARCH_MESON - depends on OF && (ARCH_MESON || COMPILE_TEST) - depends on USB_SUPPORT - select GENERIC_PHY - select REGMAP_MMIO - help - Enable this to support the Meson USB3 PHY and OTG detection - IP block found in Meson GXL and GXM SoCs. - If unsure, say N. - config PHY_MESON_G12A_USB2 tristate "Meson G12A USB2 PHY driver" default ARCH_MESON diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile index e2baa133f7af..99702a45e9be 100644 --- a/drivers/phy/amlogic/Makefile +++ b/drivers/phy/amlogic/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o -obj-$(CONFIG_PHY_MESON_GXL_USB3) += phy-meson-gxl-usb3.o obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE) += phy-meson-g12a-usb3-pcie.o obj-$(CONFIG_PHY_MESON_AXG_PCIE) += phy-meson-axg-pcie.o obj-$(CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG) += phy-meson-axg-mipi-pcie-analog.o diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb3.c b/drivers/phy/amlogic/phy-meson-gxl-usb3.c deleted file mode 100644 index c0e9e4c16149..000000000000 --- a/drivers/phy/amlogic/phy-meson-gxl-usb3.c +++ /dev/null @@ -1,283 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Meson GXL USB3 PHY and OTG mode detection driver - * - * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com> - */ - -#include <linux/bitfield.h> -#include <linux/bitops.h> -#include <linux/clk.h> -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/phy/phy.h> -#include <linux/regmap.h> -#include <linux/reset.h> -#include <linux/platform_device.h> - -#define USB_R0 0x00 - #define USB_R0_P30_FSEL_MASK GENMASK(5, 0) - #define USB_R0_P30_PHY_RESET BIT(6) - #define USB_R0_P30_TEST_POWERDOWN_HSP BIT(7) - #define USB_R0_P30_TEST_POWERDOWN_SSP BIT(8) - #define USB_R0_P30_ACJT_LEVEL_MASK GENMASK(13, 9) - #define USB_R0_P30_TX_BOOST_LEVEL_MASK GENMASK(16, 14) - #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) - #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) - #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) - #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) - #define USB_R0_U2D_ACT BIT(31) - -#define USB_R1 0x04 - #define USB_R1_U3H_BIGENDIAN_GS BIT(0) - #define USB_R1_U3H_PME_ENABLE BIT(1) - #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(6, 2) - #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(11, 7) - #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(15, 12) - #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) - #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) - #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) - #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) - #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) - -#define USB_R2 0x08 - #define USB_R2_P30_CR_DATA_IN_MASK GENMASK(15, 0) - #define USB_R2_P30_CR_READ BIT(16) - #define USB_R2_P30_CR_WRITE BIT(17) - #define USB_R2_P30_CR_CAP_ADDR BIT(18) - #define USB_R2_P30_CR_CAP_DATA BIT(19) - #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) - #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) - -#define USB_R3 0x0c - #define USB_R3_P30_SSC_ENABLE BIT(0) - #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) - #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) - #define USB_R3_P30_REF_SSP_EN BIT(13) - #define USB_R3_P30_LOS_BIAS_MASK GENMASK(18, 16) - #define USB_R3_P30_LOS_LEVEL_MASK GENMASK(23, 19) - #define USB_R3_P30_MPLL_MULTIPLIER_MASK GENMASK(30, 24) - -#define USB_R4 0x10 - #define USB_R4_P21_PORT_RESET_0 BIT(0) - #define USB_R4_P21_SLEEP_M0 BIT(1) - #define USB_R4_MEM_PD_MASK GENMASK(3, 2) - #define USB_R4_P21_ONLY BIT(4) - -#define USB_R5 0x14 - #define USB_R5_ID_DIG_SYNC BIT(0) - #define USB_R5_ID_DIG_REG BIT(1) - #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) - #define USB_R5_ID_DIG_EN_0 BIT(4) - #define USB_R5_ID_DIG_EN_1 BIT(5) - #define USB_R5_ID_DIG_CURR BIT(6) - #define USB_R5_ID_DIG_IRQ BIT(7) - #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) - #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) - -/* read-only register */ -#define USB_R6 0x18 - #define USB_R6_P30_CR_DATA_OUT_MASK GENMASK(15, 0) - #define USB_R6_P30_CR_ACK BIT(16) - -struct phy_meson_gxl_usb3_priv { - struct regmap *regmap; - enum phy_mode mode; - struct clk *clk_phy; - struct clk *clk_peripheral; - struct reset_control *reset; -}; - -static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = { - .reg_bits = 8, - .val_bits = 32, - .reg_stride = 4, - .max_register = USB_R6, -}; - -static int phy_meson_gxl_usb3_power_on(struct phy *phy) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, - USB_R5_ID_DIG_EN_0); - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, - USB_R5_ID_DIG_EN_1); - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK, - FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); - - return 0; -} - -static int phy_meson_gxl_usb3_power_off(struct phy *phy) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0); - regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0); - - return 0; -} - -static int phy_meson_gxl_usb3_set_mode(struct phy *phy, - enum phy_mode mode, int submode) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - - switch (mode) { - case PHY_MODE_USB_HOST: - regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0); - regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0, - 0); - break; - - case PHY_MODE_USB_DEVICE: - regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, - USB_R0_U2D_ACT); - regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0, - USB_R4_P21_SLEEP_M0); - break; - - default: - dev_err(&phy->dev, "unsupported PHY mode %d\n", mode); - return -EINVAL; - } - - priv->mode = mode; - - return 0; -} - -static int phy_meson_gxl_usb3_init(struct phy *phy) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - int ret; - - ret = reset_control_reset(priv->reset); - if (ret) - goto err; - - ret = clk_prepare_enable(priv->clk_phy); - if (ret) - goto err; - - ret = clk_prepare_enable(priv->clk_peripheral); - if (ret) - goto err_disable_clk_phy; - - ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0); - if (ret) - goto err_disable_clk_peripheral; - - regmap_update_bits(priv->regmap, USB_R1, - USB_R1_U3H_FLADJ_30MHZ_REG_MASK, - FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); - - return 0; - -err_disable_clk_peripheral: - clk_disable_unprepare(priv->clk_peripheral); -err_disable_clk_phy: - clk_disable_unprepare(priv->clk_phy); -err: - return ret; -} - -static int phy_meson_gxl_usb3_exit(struct phy *phy) -{ - struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy); - - clk_disable_unprepare(priv->clk_peripheral); - clk_disable_unprepare(priv->clk_phy); - - return 0; -} - -static const struct phy_ops phy_meson_gxl_usb3_ops = { - .power_on = phy_meson_gxl_usb3_power_on, - .power_off = phy_meson_gxl_usb3_power_off, - .set_mode = phy_meson_gxl_usb3_set_mode, - .init = phy_meson_gxl_usb3_init, - .exit = phy_meson_gxl_usb3_exit, - .owner = THIS_MODULE, -}; - -static int phy_meson_gxl_usb3_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct phy_meson_gxl_usb3_priv *priv; - struct resource *res; - struct phy *phy; - struct phy_provider *phy_provider; - void __iomem *base; - int ret; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - priv->regmap = devm_regmap_init_mmio(dev, base, - &phy_meson_gxl_usb3_regmap_conf); - if (IS_ERR(priv->regmap)) - return PTR_ERR(priv->regmap); - - priv->clk_phy = devm_clk_get(dev, "phy"); - if (IS_ERR(priv->clk_phy)) - return PTR_ERR(priv->clk_phy); - - priv->clk_peripheral = devm_clk_get(dev, "peripheral"); - if (IS_ERR(priv->clk_peripheral)) - return PTR_ERR(priv->clk_peripheral); - - priv->reset = devm_reset_control_array_get_shared(dev); - if (IS_ERR(priv->reset)) - return PTR_ERR(priv->reset); - - /* - * default to host mode as hardware defaults and/or boot-loader - * behavior can result in this PHY starting up in device mode. this - * default and the initialization in phy_meson_gxl_usb3_init ensure - * that we reproducibly start in a known mode on all devices. - */ - priv->mode = PHY_MODE_USB_HOST; - - phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops); - if (IS_ERR(phy)) { - ret = PTR_ERR(phy); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to create PHY\n"); - - return ret; - } - - phy_set_drvdata(phy, priv); - - 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 phy_meson_gxl_usb3_of_match[] = { - { .compatible = "amlogic,meson-gxl-usb3-phy", }, - { }, -}; -MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match); - -static struct platform_driver phy_meson_gxl_usb3_driver = { - .probe = phy_meson_gxl_usb3_probe, - .driver = { - .name = "phy-meson-gxl-usb3", - .of_match_table = phy_meson_gxl_usb3_of_match, - }, -}; -module_platform_driver(phy_meson_gxl_usb3_driver); - -MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); -MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/amlogic/phy-meson8b-usb2.c b/drivers/phy/amlogic/phy-meson8b-usb2.c index bd66bd723e4a..03c061dd5f0d 100644 --- a/drivers/phy/amlogic/phy-meson8b-usb2.c +++ b/drivers/phy/amlogic/phy-meson8b-usb2.c @@ -10,6 +10,8 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/property.h> +#include <linux/regmap.h> #include <linux/reset.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> @@ -76,6 +78,17 @@ #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) #define REG_DBG_UART 0x10 + #define REG_DBG_UART_BYPASS_SEL BIT(0) + #define REG_DBG_UART_BYPASS_DM_EN BIT(1) + #define REG_DBG_UART_BYPASS_DP_EN BIT(2) + #define REG_DBG_UART_BYPASS_DM_DATA BIT(3) + #define REG_DBG_UART_BYPASS_DP_DATA BIT(4) + #define REG_DBG_UART_FSV_MINUS BIT(5) + #define REG_DBG_UART_FSV_PLUS BIT(6) + #define REG_DBG_UART_FSV_BURN_IN_TEST BIT(7) + #define REG_DBG_UART_LOOPBACK_EN_B BIT(8) + #define REG_DBG_UART_SET_IDDQ BIT(9) + #define REG_DBG_UART_ATE_RESET BIT(10) #define REG_TEST 0x14 #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) @@ -104,35 +117,30 @@ #define RESET_COMPLETE_TIME 500 #define ACA_ENABLE_COMPLETE_TIME 50 -struct phy_meson8b_usb2_priv { - void __iomem *regs; - enum usb_dr_mode dr_mode; - struct clk *clk_usb_general; - struct clk *clk_usb; - struct reset_control *reset; +struct phy_meson8b_usb2_match_data { + bool host_enable_aca; }; -static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, - u32 reg) -{ - return readl(phy_priv->regs + reg); -} - -static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, - u32 reg, u32 mask, u32 value) -{ - u32 data; - - data = phy_meson8b_usb2_read(phy_priv, reg); - data &= ~mask; - data |= (value & mask); +struct phy_meson8b_usb2_priv { + struct regmap *regmap; + enum usb_dr_mode dr_mode; + struct clk *clk_usb_general; + struct clk *clk_usb; + struct reset_control *reset; + const struct phy_meson8b_usb2_match_data *match; +}; - writel(data, phy_priv->regs + reg); -} +static const struct regmap_config phy_meson8b_usb2_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_TUNE, +}; static int phy_meson8b_usb2_power_on(struct phy *phy) { struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + u32 reg; int ret; if (!IS_ERR_OR_NULL(priv->reset)) { @@ -156,38 +164,43 @@ static int phy_meson8b_usb2_power_on(struct phy *phy) return ret; } - phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, - REG_CONFIG_CLK_32k_ALTSEL); + regmap_update_bits(priv->regmap, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, + REG_CONFIG_CLK_32k_ALTSEL); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, - 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, + 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, - 0x5 << REG_CTRL_FSEL_SHIFT); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_FSEL_MASK, + 0x5 << REG_CTRL_FSEL_SHIFT); /* reset the PHY */ - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, - REG_CTRL_POWER_ON_RESET); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, + REG_CTRL_POWER_ON_RESET); udelay(RESET_COMPLETE_TIME); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); udelay(RESET_COMPLETE_TIME); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, - REG_CTRL_SOF_TOGGLE_OUT); + regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, + REG_CTRL_SOF_TOGGLE_OUT); if (priv->dr_mode == USB_DR_MODE_HOST) { - phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, + regmap_update_bits(priv->regmap, REG_DBG_UART, + REG_DBG_UART_SET_IDDQ, 0); + + if (priv->match->host_enable_aca) { + regmap_update_bits(priv->regmap, REG_ADP_BC, REG_ADP_BC_ACA_ENABLE, REG_ADP_BC_ACA_ENABLE); - udelay(ACA_ENABLE_COMPLETE_TIME); + udelay(ACA_ENABLE_COMPLETE_TIME); - if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & - REG_ADP_BC_ACA_PIN_FLOAT) { - dev_warn(&phy->dev, "USB ID detect failed!\n"); - clk_disable_unprepare(priv->clk_usb); - clk_disable_unprepare(priv->clk_usb_general); - return -EINVAL; + regmap_read(priv->regmap, REG_ADP_BC, ®); + if (reg & REG_ADP_BC_ACA_PIN_FLOAT) { + dev_warn(&phy->dev, "USB ID detect failed!\n"); + clk_disable_unprepare(priv->clk_usb); + clk_disable_unprepare(priv->clk_usb_general); + return -EINVAL; + } } } @@ -198,6 +211,11 @@ static int phy_meson8b_usb2_power_off(struct phy *phy) { struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + if (priv->dr_mode == USB_DR_MODE_HOST) + regmap_update_bits(priv->regmap, REG_DBG_UART, + REG_DBG_UART_SET_IDDQ, + REG_DBG_UART_SET_IDDQ); + clk_disable_unprepare(priv->clk_usb); clk_disable_unprepare(priv->clk_usb_general); @@ -213,18 +231,26 @@ static const struct phy_ops phy_meson8b_usb2_ops = { static int phy_meson8b_usb2_probe(struct platform_device *pdev) { struct phy_meson8b_usb2_priv *priv; - struct resource *res; struct phy *phy; struct phy_provider *phy_provider; + void __iomem *base; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->regs)) - return PTR_ERR(priv->regs); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->match = device_get_match_data(&pdev->dev); + if (!priv->match) + return -ENODEV; + + priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &phy_meson8b_usb2_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); if (IS_ERR(priv->clk_usb_general)) @@ -259,11 +285,32 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } +static const struct phy_meson8b_usb2_match_data phy_meson8_usb2_match_data = { + .host_enable_aca = false, +}; + +static const struct phy_meson8b_usb2_match_data phy_meson8b_usb2_match_data = { + .host_enable_aca = true, +}; + static const struct of_device_id phy_meson8b_usb2_of_match[] = { - { .compatible = "amlogic,meson8-usb2-phy", }, - { .compatible = "amlogic,meson8b-usb2-phy", }, - { .compatible = "amlogic,meson-gxbb-usb2-phy", }, - { }, + { + .compatible = "amlogic,meson8-usb2-phy", + .data = &phy_meson8_usb2_match_data + }, + { + .compatible = "amlogic,meson8b-usb2-phy", + .data = &phy_meson8b_usb2_match_data + }, + { + .compatible = "amlogic,meson8m2-usb2-phy", + .data = &phy_meson8b_usb2_match_data + }, + { + .compatible = "amlogic,meson-gxbb-usb2-phy", + .data = &phy_meson8b_usb2_match_data + }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); @@ -277,5 +324,5 @@ static struct platform_driver phy_meson8b_usb2_driver = { module_platform_driver(phy_meson8b_usb2_driver); MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); -MODULE_DESCRIPTION("Meson8, Meson8b and GXBB USB2 PHY driver"); +MODULE_DESCRIPTION("Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c index 7ceea5ae2704..527625912b78 100644 --- a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c +++ b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c @@ -279,7 +279,7 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static struct phy_ops ops = { +static const struct phy_ops ops = { .init = ns2_drd_phy_init, .power_on = ns2_drd_phy_poweron, .power_off = ns2_drd_phy_poweroff, diff --git a/drivers/phy/broadcom/phy-bcm-sr-usb.c b/drivers/phy/broadcom/phy-bcm-sr-usb.c index fe6c58910e4c..77c025a0720c 100644 --- a/drivers/phy/broadcom/phy-bcm-sr-usb.c +++ b/drivers/phy/broadcom/phy-bcm-sr-usb.c @@ -16,8 +16,6 @@ enum bcm_usb_phy_version { }; enum bcm_usb_phy_reg { - PLL_NDIV_FRAC, - PLL_NDIV_INT, PLL_CTRL, PHY_CTRL, PHY_PLL_CTRL, @@ -31,18 +29,11 @@ static const u8 bcm_usb_combo_phy_ss[] = { }; static const u8 bcm_usb_combo_phy_hs[] = { - [PLL_NDIV_FRAC] = 0x04, - [PLL_NDIV_INT] = 0x08, [PLL_CTRL] = 0x0c, [PHY_CTRL] = 0x10, }; -#define HSPLL_NDIV_INT_VAL 0x13 -#define HSPLL_NDIV_FRAC_VAL 0x1005 - static const u8 bcm_usb_hs_phy[] = { - [PLL_NDIV_FRAC] = 0x0, - [PLL_NDIV_INT] = 0x4, [PLL_CTRL] = 0x8, [PHY_CTRL] = 0xc, }; @@ -52,7 +43,6 @@ enum pll_ctrl_bits { SSPLL_SUSPEND_EN, PLL_SEQ_START, PLL_LOCK, - PLL_PDIV, }; static const u8 u3pll_ctrl[] = { @@ -66,29 +56,17 @@ static const u8 u3pll_ctrl[] = { #define HSPLL_PDIV_VAL 0x1 static const u8 u2pll_ctrl[] = { - [PLL_PDIV] = 1, [PLL_RESETB] = 5, [PLL_LOCK] = 6, }; enum bcm_usb_phy_ctrl_bits { CORERDY, - AFE_LDO_PWRDWNB, - AFE_PLL_PWRDWNB, - AFE_BG_PWRDWNB, - PHY_ISO, PHY_RESETB, PHY_PCTL, }; #define PHY_PCTL_MASK 0xffff -/* - * 0x0806 of PCTL_VAL has below bits set - * BIT-8 : refclk divider 1 - * BIT-3:2: device mode; mode is not effect - * BIT-1: soft reset active low - */ -#define HSPHY_PCTL_VAL 0x0806 #define SSPHY_PCTL_VAL 0x0006 static const u8 u3phy_ctrl[] = { @@ -98,10 +76,6 @@ static const u8 u3phy_ctrl[] = { static const u8 u2phy_ctrl[] = { [CORERDY] = 0, - [AFE_LDO_PWRDWNB] = 1, - [AFE_PLL_PWRDWNB] = 2, - [AFE_BG_PWRDWNB] = 3, - [PHY_ISO] = 4, [PHY_RESETB] = 5, [PHY_PCTL] = 6, }; @@ -186,38 +160,13 @@ static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg) int ret = 0; void __iomem *regs = phy_cfg->regs; const u8 *offset; - u32 rd_data; offset = phy_cfg->offset; - writel(HSPLL_NDIV_INT_VAL, regs + offset[PLL_NDIV_INT]); - writel(HSPLL_NDIV_FRAC_VAL, regs + offset[PLL_NDIV_FRAC]); - - rd_data = readl(regs + offset[PLL_CTRL]); - rd_data &= ~(HSPLL_PDIV_MASK << u2pll_ctrl[PLL_PDIV]); - rd_data |= (HSPLL_PDIV_VAL << u2pll_ctrl[PLL_PDIV]); - writel(rd_data, regs + offset[PLL_CTRL]); - - /* Set Core Ready high */ - bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], - BIT(u2phy_ctrl[CORERDY])); - - /* Maximum timeout for Core Ready done */ - msleep(30); - + bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL], + BIT(u2pll_ctrl[PLL_RESETB])); bcm_usb_reg32_setbits(regs + offset[PLL_CTRL], BIT(u2pll_ctrl[PLL_RESETB])); - bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], - BIT(u2phy_ctrl[PHY_RESETB])); - - - rd_data = readl(regs + offset[PHY_CTRL]); - rd_data &= ~(PHY_PCTL_MASK << u2phy_ctrl[PHY_PCTL]); - rd_data |= (HSPHY_PCTL_VAL << u2phy_ctrl[PHY_PCTL]); - writel(rd_data, regs + offset[PHY_CTRL]); - - /* Maximum timeout for PLL reset done */ - msleep(30); ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL], BIT(u2pll_ctrl[PLL_LOCK])); @@ -256,7 +205,7 @@ static int bcm_usb_phy_init(struct phy *phy) return ret; } -static struct phy_ops sr_phy_ops = { +static const struct phy_ops sr_phy_ops = { .init = bcm_usb_phy_init, .reset = bcm_usb_phy_reset, .owner = THIS_MODULE, diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 491bbd46c5b3..99fbc7e4138b 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -39,14 +39,14 @@ struct match_chip_info { u8 optional_reg; }; -static struct value_to_name_map brcm_dr_mode_to_name[] = { +static const struct value_to_name_map brcm_dr_mode_to_name[] = { { USB_CTLR_MODE_HOST, "host" }, { USB_CTLR_MODE_DEVICE, "peripheral" }, { USB_CTLR_MODE_DRD, "drd" }, { USB_CTLR_MODE_TYPEC_PD, "typec-pd" } }; -static struct value_to_name_map brcm_dual_mode_to_name[] = { +static const struct value_to_name_map brcm_dual_mode_to_name[] = { { 0, "host" }, { 1, "device" }, { 2, "auto" }, @@ -138,7 +138,7 @@ static int brcm_usb_phy_exit(struct phy *gphy) return 0; } -static struct phy_ops brcm_usb_phy_ops = { +static const struct phy_ops brcm_usb_phy_ops = { .init = brcm_usb_phy_init, .exit = brcm_usb_phy_exit, .owner = THIS_MODULE, @@ -170,7 +170,7 @@ static struct phy *brcm_usb_phy_xlate(struct device *dev, return ERR_PTR(-ENODEV); } -static int name_to_value(struct value_to_name_map *table, int count, +static int name_to_value(const struct value_to_name_map *table, int count, const char *name, int *value) { int x; @@ -185,7 +185,7 @@ static int name_to_value(struct value_to_name_map *table, int count, return -EINVAL; } -static const char *value_to_name(struct value_to_name_map *table, int count, +static const char *value_to_name(const struct value_to_name_map *table, int count, int value) { if (value >= count) @@ -252,7 +252,7 @@ static const struct attribute_group brcm_usb_phy_group = { .attrs = brcm_usb_phy_attrs, }; -static struct match_chip_info chip_info_7216 = { +static const struct match_chip_info chip_info_7216 = { .init_func = &brcm_usb_dvr_init_7216, .required_regs = { BRCM_REGS_CTRL, @@ -262,7 +262,7 @@ static struct match_chip_info chip_info_7216 = { }, }; -static struct match_chip_info chip_info_7211b0 = { +static const struct match_chip_info chip_info_7211b0 = { .init_func = &brcm_usb_dvr_init_7211b0, .required_regs = { BRCM_REGS_CTRL, @@ -275,7 +275,7 @@ static struct match_chip_info chip_info_7211b0 = { .optional_reg = BRCM_REGS_BDC_EC, }; -static struct match_chip_info chip_info_7445 = { +static const struct match_chip_info chip_info_7445 = { .init_func = &brcm_usb_dvr_init_7445, .required_regs = { BRCM_REGS_CTRL, diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig index 459545871608..432832bdbd16 100644 --- a/drivers/phy/cadence/Kconfig +++ b/drivers/phy/cadence/Kconfig @@ -27,3 +27,12 @@ config PHY_CADENCE_SIERRA select GENERIC_PHY help Enable this to support the Cadence Sierra PHY driver + +config PHY_CADENCE_SALVO + tristate "Cadence Salvo PHY Driver" + depends on OF && HAS_IOMEM + select GENERIC_PHY + help + Enable this to support the Cadence SALVO PHY driver, + this PHY is a legacy PHY, and only are used for USB3 + and USB2. diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile index 6a7ffc6ea599..26e16bd34efe 100644 --- a/drivers/phy/cadence/Makefile +++ b/drivers/phy/cadence/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_PHY_CADENCE_TORRENT) += phy-cadence-torrent.o obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o obj-$(CONFIG_PHY_CADENCE_SIERRA) += phy-cadence-sierra.o +obj-$(CONFIG_PHY_CADENCE_SALVO) += phy-cadence-salvo.o diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c new file mode 100644 index 000000000000..1ecbb964cd21 --- /dev/null +++ b/drivers/phy/cadence/phy-cadence-salvo.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Salvo PHY is a 28nm PHY, it is a legacy PHY, and only + * for USB3 and USB2. + * + * Copyright (c) 2019-2020 NXP + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_platform.h> + +/* PHY register definition */ +#define PHY_PMA_CMN_CTRL1 0xC800 +#define TB_ADDR_CMN_DIAG_HSCLK_SEL 0x01e0 +#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR 0x0084 +#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR 0x0085 +#define TB_ADDR_CMN_PLL0_INTDIV 0x0094 +#define TB_ADDR_CMN_PLL0_FRACDIV 0x0095 +#define TB_ADDR_CMN_PLL0_HIGH_THR 0x0096 +#define TB_ADDR_CMN_PLL0_SS_CTRL1 0x0098 +#define TB_ADDR_CMN_PLL0_SS_CTRL2 0x0099 +#define TB_ADDR_CMN_PLL0_DSM_DIAG 0x0097 +#define TB_ADDR_CMN_DIAG_PLL0_OVRD 0x01c2 +#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD 0x01c0 +#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD 0x01c1 +#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE 0x01C5 +#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE 0x01C6 +#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG 0x01C7 +#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE 0x01c4 +#define TB_ADDR_CMN_PSM_CLK_CTRL 0x0061 +#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR 0x40ea +#define TB_ADDR_XCVR_PSM_RCTRL 0x4001 +#define TB_ADDR_TX_PSC_A0 0x4100 +#define TB_ADDR_TX_PSC_A1 0x4101 +#define TB_ADDR_TX_PSC_A2 0x4102 +#define TB_ADDR_TX_PSC_A3 0x4103 +#define TB_ADDR_TX_DIAG_ECTRL_OVRD 0x41f5 +#define TB_ADDR_TX_PSC_CAL 0x4106 +#define TB_ADDR_TX_PSC_RDY 0x4107 +#define TB_ADDR_RX_PSC_A0 0x8000 +#define TB_ADDR_RX_PSC_A1 0x8001 +#define TB_ADDR_RX_PSC_A2 0x8002 +#define TB_ADDR_RX_PSC_A3 0x8003 +#define TB_ADDR_RX_PSC_CAL 0x8006 +#define TB_ADDR_RX_PSC_RDY 0x8007 +#define TB_ADDR_TX_TXCC_MGNLS_MULT_000 0x4058 +#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY 0x41e7 +#define TB_ADDR_RX_SLC_CU_ITER_TMR 0x80e3 +#define TB_ADDR_RX_SIGDET_HL_FILT_TMR 0x8090 +#define TB_ADDR_RX_SAMP_DAC_CTRL 0x8058 +#define TB_ADDR_RX_DIAG_SIGDET_TUNE 0x81dc +#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 0x81df +#define TB_ADDR_RX_DIAG_BS_TM 0x81f5 +#define TB_ADDR_RX_DIAG_DFE_CTRL1 0x81d3 +#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 0x81c7 +#define TB_ADDR_RX_DIAG_ILL_E_TRIM0 0x81c2 +#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 0x81c1 +#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 0x81c9 +#define TB_ADDR_RX_DIAG_RXFE_TM3 0x81f8 +#define TB_ADDR_RX_DIAG_RXFE_TM4 0x81f9 +#define TB_ADDR_RX_DIAG_LFPSDET_TUNE 0x81dd +#define TB_ADDR_RX_DIAG_DFE_CTRL3 0x81d5 +#define TB_ADDR_RX_DIAG_SC2C_DELAY 0x81e1 +#define TB_ADDR_RX_REE_VGA_GAIN_NODFE 0x81bf +#define TB_ADDR_XCVR_PSM_CAL_TMR 0x4002 +#define TB_ADDR_XCVR_PSM_A0BYP_TMR 0x4004 +#define TB_ADDR_XCVR_PSM_A0IN_TMR 0x4003 +#define TB_ADDR_XCVR_PSM_A1IN_TMR 0x4005 +#define TB_ADDR_XCVR_PSM_A2IN_TMR 0x4006 +#define TB_ADDR_XCVR_PSM_A3IN_TMR 0x4007 +#define TB_ADDR_XCVR_PSM_A4IN_TMR 0x4008 +#define TB_ADDR_XCVR_PSM_A5IN_TMR 0x4009 +#define TB_ADDR_XCVR_PSM_A0OUT_TMR 0x400a +#define TB_ADDR_XCVR_PSM_A1OUT_TMR 0x400b +#define TB_ADDR_XCVR_PSM_A2OUT_TMR 0x400c +#define TB_ADDR_XCVR_PSM_A3OUT_TMR 0x400d +#define TB_ADDR_XCVR_PSM_A4OUT_TMR 0x400e +#define TB_ADDR_XCVR_PSM_A5OUT_TMR 0x400f +#define TB_ADDR_TX_RCVDET_EN_TMR 0x4122 +#define TB_ADDR_TX_RCVDET_ST_TMR 0x4123 +#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2 +#define TB_ADDR_TX_RCVDETSC_CTRL 0x4124 + +/* TB_ADDR_TX_RCVDETSC_CTRL */ +#define RXDET_IN_P3_32KHZ BIT(1) + +struct cdns_reg_pairs { + u16 val; + u32 off; +}; + +struct cdns_salvo_data { + u8 reg_offset_shift; + struct cdns_reg_pairs *init_sequence_val; + u8 init_sequence_length; +}; + +struct cdns_salvo_phy { + struct phy *phy; + struct clk *clk; + void __iomem *base; + struct cdns_salvo_data *data; +}; + +static const struct of_device_id cdns_salvo_phy_of_match[]; +static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 reg) +{ + return (u16)readl(salvo_phy->base + + reg * (1 << salvo_phy->data->reg_offset_shift)); +} + +static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, + u32 reg, u16 val) +{ + writel(val, salvo_phy->base + + reg * (1 << salvo_phy->data->reg_offset_shift)); +} + +/* + * Below bringup sequence pair are from Cadence PHY's User Guide + * and NXP platform tuning results. + */ +static struct cdns_reg_pairs cdns_nxp_sequence_pair[] = { + {0x0830, PHY_PMA_CMN_CTRL1}, + {0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL}, + {0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR}, + {0x0018, TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR}, + {0x00d0, TB_ADDR_CMN_PLL0_INTDIV}, + {0x4aaa, TB_ADDR_CMN_PLL0_FRACDIV}, + {0x0034, TB_ADDR_CMN_PLL0_HIGH_THR}, + {0x01ee, TB_ADDR_CMN_PLL0_SS_CTRL1}, + {0x7f03, TB_ADDR_CMN_PLL0_SS_CTRL2}, + {0x0020, TB_ADDR_CMN_PLL0_DSM_DIAG}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_OVRD}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD}, + {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD}, + {0x0007, TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE}, + {0x0027, TB_ADDR_CMN_DIAG_PLL0_CP_TUNE}, + {0x0008, TB_ADDR_CMN_DIAG_PLL0_LF_PROG}, + {0x0022, TB_ADDR_CMN_DIAG_PLL0_TEST_MODE}, + {0x000a, TB_ADDR_CMN_PSM_CLK_CTRL}, + {0x0139, TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR}, + {0xbefc, TB_ADDR_XCVR_PSM_RCTRL}, + + {0x7799, TB_ADDR_TX_PSC_A0}, + {0x7798, TB_ADDR_TX_PSC_A1}, + {0x509b, TB_ADDR_TX_PSC_A2}, + {0x0003, TB_ADDR_TX_DIAG_ECTRL_OVRD}, + {0x509b, TB_ADDR_TX_PSC_A3}, + {0x2090, TB_ADDR_TX_PSC_CAL}, + {0x2090, TB_ADDR_TX_PSC_RDY}, + + {0xA6FD, TB_ADDR_RX_PSC_A0}, + {0xA6FD, TB_ADDR_RX_PSC_A1}, + {0xA410, TB_ADDR_RX_PSC_A2}, + {0x2410, TB_ADDR_RX_PSC_A3}, + + {0x23FF, TB_ADDR_RX_PSC_CAL}, + {0x2010, TB_ADDR_RX_PSC_RDY}, + + {0x0020, TB_ADDR_TX_TXCC_MGNLS_MULT_000}, + {0x00ff, TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY}, + {0x0002, TB_ADDR_RX_SLC_CU_ITER_TMR}, + {0x0013, TB_ADDR_RX_SIGDET_HL_FILT_TMR}, + {0x0000, TB_ADDR_RX_SAMP_DAC_CTRL}, + {0x1004, TB_ADDR_RX_DIAG_SIGDET_TUNE}, + {0x4041, TB_ADDR_RX_DIAG_LFPSDET_TUNE2}, + {0x0480, TB_ADDR_RX_DIAG_BS_TM}, + {0x8006, TB_ADDR_RX_DIAG_DFE_CTRL1}, + {0x003f, TB_ADDR_RX_DIAG_ILL_IQE_TRIM4}, + {0x543f, TB_ADDR_RX_DIAG_ILL_E_TRIM0}, + {0x543f, TB_ADDR_RX_DIAG_ILL_IQ_TRIM0}, + {0x0000, TB_ADDR_RX_DIAG_ILL_IQE_TRIM6}, + {0x8000, TB_ADDR_RX_DIAG_RXFE_TM3}, + {0x0003, TB_ADDR_RX_DIAG_RXFE_TM4}, + {0x2408, TB_ADDR_RX_DIAG_LFPSDET_TUNE}, + {0x05ca, TB_ADDR_RX_DIAG_DFE_CTRL3}, + {0x0258, TB_ADDR_RX_DIAG_SC2C_DELAY}, + {0x1fff, TB_ADDR_RX_REE_VGA_GAIN_NODFE}, + + {0x02c6, TB_ADDR_XCVR_PSM_CAL_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A0BYP_TMR}, + {0x02c6, TB_ADDR_XCVR_PSM_A0IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A1IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A2IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A3IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A4IN_TMR}, + {0x0010, TB_ADDR_XCVR_PSM_A5IN_TMR}, + + {0x0002, TB_ADDR_XCVR_PSM_A0OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A1OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A2OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A3OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A4OUT_TMR}, + {0x0002, TB_ADDR_XCVR_PSM_A5OUT_TMR}, + /* Change rx detect parameter */ + {0x0960, TB_ADDR_TX_RCVDET_EN_TMR}, + {0x01e0, TB_ADDR_TX_RCVDET_ST_TMR}, + {0x0090, TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR}, +}; + +static int cdns_salvo_phy_init(struct phy *phy) +{ + struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy); + struct cdns_salvo_data *data = salvo_phy->data; + int ret, i; + u16 value; + + ret = clk_prepare_enable(salvo_phy->clk); + if (ret) + return ret; + + for (i = 0; i < data->init_sequence_length; i++) { + struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i; + + cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val); + } + + /* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */ + value = cdns_salvo_read(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL); + value |= RXDET_IN_P3_32KHZ; + cdns_salvo_write(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL, + RXDET_IN_P3_32KHZ); + + udelay(10); + + clk_disable_unprepare(salvo_phy->clk); + + return ret; +} + +static int cdns_salvo_phy_power_on(struct phy *phy) +{ + struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy); + + return clk_prepare_enable(salvo_phy->clk); +} + +static int cdns_salvo_phy_power_off(struct phy *phy) +{ + struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy); + + clk_disable_unprepare(salvo_phy->clk); + + return 0; +} + +static struct phy_ops cdns_salvo_phy_ops = { + .init = cdns_salvo_phy_init, + .power_on = cdns_salvo_phy_power_on, + .power_off = cdns_salvo_phy_power_off, + .owner = THIS_MODULE, +}; + +static int cdns_salvo_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct cdns_salvo_phy *salvo_phy; + struct resource *res; + const struct of_device_id *match; + struct cdns_salvo_data *data; + + match = of_match_device(cdns_salvo_phy_of_match, dev); + if (!match) + return -EINVAL; + + data = (struct cdns_salvo_data *)match->data; + salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL); + if (!salvo_phy) + return -ENOMEM; + + salvo_phy->data = data; + salvo_phy->clk = devm_clk_get_optional(dev, "salvo_phy_clk"); + if (IS_ERR(salvo_phy->clk)) + return PTR_ERR(salvo_phy->clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + salvo_phy->base = devm_ioremap_resource(dev, res); + if (IS_ERR(salvo_phy->base)) + return PTR_ERR(salvo_phy->base); + + salvo_phy->phy = devm_phy_create(dev, NULL, &cdns_salvo_phy_ops); + if (IS_ERR(salvo_phy->phy)) + return PTR_ERR(salvo_phy->phy); + + phy_set_drvdata(salvo_phy->phy, salvo_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct cdns_salvo_data cdns_nxp_salvo_data = { + 2, + cdns_nxp_sequence_pair, + ARRAY_SIZE(cdns_nxp_sequence_pair), +}; + +static const struct of_device_id cdns_salvo_phy_of_match[] = { + { + .compatible = "nxp,salvo-phy", + .data = &cdns_nxp_salvo_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cdns_salvo_phy_of_match); + +static struct platform_driver cdns_salvo_phy_driver = { + .probe = cdns_salvo_phy_probe, + .driver = { + .name = "cdns-salvo-phy", + .of_match_table = cdns_salvo_phy_of_match, + } +}; +module_platform_driver(cdns_salvo_phy_driver); + +MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence SALVO PHY Driver"); diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index a5c08e5bd2bf..faed652b73f7 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -685,10 +685,10 @@ static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = { static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0xFE0A, SIERRA_DET_STANDEC_A_PREG}, {0x000F, SIERRA_DET_STANDEC_B_PREG}, - {0x00A5, SIERRA_DET_STANDEC_C_PREG}, + {0x55A5, SIERRA_DET_STANDEC_C_PREG}, {0x69ad, SIERRA_DET_STANDEC_D_PREG}, {0x0241, SIERRA_DET_STANDEC_E_PREG}, - {0x0010, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG}, + {0x0110, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG}, {0x0014, SIERRA_PSM_A0IN_TMR_PREG}, {0xCF00, SIERRA_PSM_DIAG_PREG}, {0x001F, SIERRA_PSC_TX_A0_PREG}, @@ -696,7 +696,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x0003, SIERRA_PSC_TX_A2_PREG}, {0x0003, SIERRA_PSC_TX_A3_PREG}, {0x0FFF, SIERRA_PSC_RX_A0_PREG}, - {0x0619, SIERRA_PSC_RX_A1_PREG}, + {0x0003, SIERRA_PSC_RX_A1_PREG}, {0x0003, SIERRA_PSC_RX_A2_PREG}, {0x0001, SIERRA_PSC_RX_A3_PREG}, {0x0001, SIERRA_PLLCTRL_SUBRATE_PREG}, @@ -705,19 +705,19 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x00CA, SIERRA_CLKPATH_BIASTRIM_PREG}, {0x2512, SIERRA_DFE_BIASTRIM_PREG}, {0x0000, SIERRA_DRVCTRL_ATTEN_PREG}, - {0x873E, SIERRA_CLKPATHCTRL_TMR_PREG}, - {0x03CF, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, - {0x01CE, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x823E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG}, - {0x033F, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x023C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, {0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG}, {0x0000, SIERRA_CREQ_EQ_CTRL_PREG}, - {0x8000, SIERRA_CREQ_SPARE_PREG}, + {0x0000, SIERRA_CREQ_SPARE_PREG}, {0xCC44, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, - {0x8453, SIERRA_CTLELUT_CTRL_PREG}, - {0x4110, SIERRA_DFE_ECMP_RATESEL_PREG}, - {0x4110, SIERRA_DFE_SMP_RATESEL_PREG}, - {0x0002, SIERRA_DEQ_PHALIGN_CTRL}, + {0x8452, SIERRA_CTLELUT_CTRL_PREG}, + {0x4121, SIERRA_DFE_ECMP_RATESEL_PREG}, + {0x4121, SIERRA_DFE_SMP_RATESEL_PREG}, + {0x0003, SIERRA_DEQ_PHALIGN_CTRL}, {0x3200, SIERRA_DEQ_CONCUR_CTRL1_PREG}, {0x5064, SIERRA_DEQ_CONCUR_CTRL2_PREG}, {0x0030, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, @@ -725,7 +725,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x5A5A, SIERRA_DEQ_ERRCMP_CTRL_PREG}, {0x02F5, SIERRA_DEQ_OFFSET_CTRL_PREG}, {0x02F5, SIERRA_DEQ_GAIN_CTRL_PREG}, - {0x9A8A, SIERRA_DEQ_VGATUNE_CTRL_PREG}, + {0x9999, SIERRA_DEQ_VGATUNE_CTRL_PREG}, {0x0014, SIERRA_DEQ_GLUT0}, {0x0014, SIERRA_DEQ_GLUT1}, {0x0014, SIERRA_DEQ_GLUT2}, @@ -772,6 +772,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x000F, SIERRA_LFPSFILT_NS_PREG}, {0x0009, SIERRA_LFPSFILT_RD_PREG}, {0x0001, SIERRA_LFPSFILT_MP_PREG}, + {0x6013, SIERRA_SIGDET_SUPPORT_PREG}, {0x8013, SIERRA_SDFILT_H2L_A_PREG}, {0x8009, SIERRA_SDFILT_L2H_PREG}, {0x0024, SIERRA_RXBUFFER_CTLECTRL_PREG}, diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig index 4ea6a8897cd7..7b47682a4e0e 100644 --- a/drivers/phy/intel/Kconfig +++ b/drivers/phy/intel/Kconfig @@ -2,8 +2,23 @@ # # Phy drivers for Intel Lightning Mountain(LGM) platform # +config PHY_INTEL_COMBO + bool "Intel ComboPHY driver" + depends on X86 || COMPILE_TEST + depends on OF && HAS_IOMEM + select MFD_SYSCON + select GENERIC_PHY + select REGMAP + help + Enable this to support Intel ComboPhy. + + This driver configures ComboPhy subsystem on Intel gateway + chipsets which provides PHYs for various controllers, EMAC, + SATA and PCIe. + config PHY_INTEL_EMMC tristate "Intel EMMC PHY driver" + depends on X86 || COMPILE_TEST select GENERIC_PHY help Enable this to support the Intel EMMC PHY diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile index 6b876a75599d..233d530dadde 100644 --- a/drivers/phy/intel/Makefile +++ b/drivers/phy/intel/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_INTEL_COMBO) += phy-intel-combo.o obj-$(CONFIG_PHY_INTEL_EMMC) += phy-intel-emmc.o diff --git a/drivers/phy/intel/phy-intel-combo.c b/drivers/phy/intel/phy-intel-combo.c new file mode 100644 index 000000000000..c2a35be4cdfb --- /dev/null +++ b/drivers/phy/intel/phy-intel-combo.c @@ -0,0 +1,632 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Combo-PHY driver + * + * Copyright (C) 2019-2020 Intel Corporation. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <dt-bindings/phy/phy.h> + +#define PCIE_PHY_GEN_CTRL 0x00 +#define PCIE_PHY_CLK_PAD BIT(17) + +#define PAD_DIS_CFG 0x174 + +#define PCS_XF_ATE_OVRD_IN_2 0x3008 +#define ADAPT_REQ_MSK GENMASK(5, 4) + +#define PCS_XF_RX_ADAPT_ACK 0x3010 +#define RX_ADAPT_ACK_BIT BIT(0) + +#define CR_ADDR(addr, lane) (((addr) + (lane) * 0x100) << 2) +#define REG_COMBO_MODE(x) ((x) * 0x200) +#define REG_CLK_DISABLE(x) ((x) * 0x200 + 0x124) + +#define COMBO_PHY_ID(x) ((x)->parent->id) +#define PHY_ID(x) ((x)->id) + +#define CLK_100MHZ 100000000 +#define CLK_156_25MHZ 156250000 + +static const unsigned long intel_iphy_clk_rates[] = { + CLK_100MHZ, CLK_156_25MHZ, CLK_100MHZ, +}; + +enum { + PHY_0, + PHY_1, + PHY_MAX_NUM +}; + +/* + * Clock Register bit fields to enable clocks + * for ComboPhy according to the mode. + */ +enum intel_phy_mode { + PHY_PCIE_MODE = 0, + PHY_XPCS_MODE, + PHY_SATA_MODE, +}; + +/* ComboPhy mode Register values */ +enum intel_combo_mode { + PCIE0_PCIE1_MODE = 0, + PCIE_DL_MODE, + RXAUI_MODE, + XPCS0_XPCS1_MODE, + SATA0_SATA1_MODE, +}; + +enum aggregated_mode { + PHY_SL_MODE, + PHY_DL_MODE, +}; + +struct intel_combo_phy; + +struct intel_cbphy_iphy { + struct phy *phy; + struct intel_combo_phy *parent; + struct reset_control *app_rst; + u32 id; +}; + +struct intel_combo_phy { + struct device *dev; + struct clk *core_clk; + unsigned long clk_rate; + void __iomem *app_base; + void __iomem *cr_base; + struct regmap *syscfg; + struct regmap *hsiocfg; + u32 id; + u32 bid; + struct reset_control *phy_rst; + struct reset_control *core_rst; + struct intel_cbphy_iphy iphy[PHY_MAX_NUM]; + enum intel_phy_mode phy_mode; + enum aggregated_mode aggr_mode; + u32 init_cnt; + struct mutex lock; +}; + +static int intel_cbphy_iphy_enable(struct intel_cbphy_iphy *iphy, bool set) +{ + struct intel_combo_phy *cbphy = iphy->parent; + u32 mask = BIT(cbphy->phy_mode * 2 + iphy->id); + u32 val; + + /* Register: 0 is enable, 1 is disable */ + val = set ? 0 : mask; + + return regmap_update_bits(cbphy->hsiocfg, REG_CLK_DISABLE(cbphy->bid), + mask, val); +} + +static int intel_cbphy_pcie_refclk_cfg(struct intel_cbphy_iphy *iphy, bool set) +{ + struct intel_combo_phy *cbphy = iphy->parent; + u32 mask = BIT(cbphy->id * 2 + iphy->id); + u32 val; + + /* Register: 0 is enable, 1 is disable */ + val = set ? 0 : mask; + + return regmap_update_bits(cbphy->syscfg, PAD_DIS_CFG, mask, val); +} + +static inline void combo_phy_w32_off_mask(void __iomem *base, unsigned int reg, + u32 mask, u32 val) +{ + u32 reg_val; + + reg_val = readl(base + reg); + reg_val &= ~mask; + reg_val |= FIELD_PREP(mask, val); + writel(reg_val, base + reg); +} + +static int intel_cbphy_iphy_cfg(struct intel_cbphy_iphy *iphy, + int (*phy_cfg)(struct intel_cbphy_iphy *)) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + ret = phy_cfg(iphy); + if (ret) + return ret; + + if (cbphy->aggr_mode != PHY_DL_MODE) + return 0; + + return phy_cfg(&cbphy->iphy[PHY_1]); +} + +static int intel_cbphy_pcie_en_pad_refclk(struct intel_cbphy_iphy *iphy) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + ret = intel_cbphy_pcie_refclk_cfg(iphy, true); + if (ret) { + dev_err(cbphy->dev, "Failed to enable PCIe pad refclk\n"); + return ret; + } + + if (cbphy->init_cnt) + return 0; + + combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL, + PCIE_PHY_CLK_PAD, 0); + + /* Delay for stable clock PLL */ + usleep_range(50, 100); + + return 0; +} + +static int intel_cbphy_pcie_dis_pad_refclk(struct intel_cbphy_iphy *iphy) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + ret = intel_cbphy_pcie_refclk_cfg(iphy, false); + if (ret) { + dev_err(cbphy->dev, "Failed to disable PCIe pad refclk\n"); + return ret; + } + + if (cbphy->init_cnt) + return 0; + + combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL, + PCIE_PHY_CLK_PAD, 1); + + return 0; +} + +static int intel_cbphy_set_mode(struct intel_combo_phy *cbphy) +{ + enum intel_combo_mode cb_mode = PHY_PCIE_MODE; + enum aggregated_mode aggr = cbphy->aggr_mode; + struct device *dev = cbphy->dev; + enum intel_phy_mode mode; + int ret; + + mode = cbphy->phy_mode; + + switch (mode) { + case PHY_PCIE_MODE: + cb_mode = (aggr == PHY_DL_MODE) ? PCIE_DL_MODE : PCIE0_PCIE1_MODE; + break; + + case PHY_XPCS_MODE: + cb_mode = (aggr == PHY_DL_MODE) ? RXAUI_MODE : XPCS0_XPCS1_MODE; + break; + + case PHY_SATA_MODE: + if (aggr == PHY_DL_MODE) { + dev_err(dev, "Mode:%u not support dual lane!\n", mode); + return -EINVAL; + } + + cb_mode = SATA0_SATA1_MODE; + break; + } + + ret = regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode); + if (ret) + dev_err(dev, "Failed to set ComboPhy mode: %d\n", ret); + + return ret; +} + +static void intel_cbphy_rst_assert(struct intel_combo_phy *cbphy) +{ + reset_control_assert(cbphy->core_rst); + reset_control_assert(cbphy->phy_rst); +} + +static void intel_cbphy_rst_deassert(struct intel_combo_phy *cbphy) +{ + reset_control_deassert(cbphy->core_rst); + reset_control_deassert(cbphy->phy_rst); + /* Delay to ensure reset process is done */ + usleep_range(10, 20); +} + +static int intel_cbphy_iphy_power_on(struct intel_cbphy_iphy *iphy) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + if (!cbphy->init_cnt) { + ret = clk_prepare_enable(cbphy->core_clk); + if (ret) { + dev_err(cbphy->dev, "Clock enable failed!\n"); + return ret; + } + + ret = clk_set_rate(cbphy->core_clk, cbphy->clk_rate); + if (ret) { + dev_err(cbphy->dev, "Clock freq set to %lu failed!\n", + cbphy->clk_rate); + goto clk_err; + } + + intel_cbphy_rst_assert(cbphy); + intel_cbphy_rst_deassert(cbphy); + ret = intel_cbphy_set_mode(cbphy); + if (ret) + goto clk_err; + } + + ret = intel_cbphy_iphy_enable(iphy, true); + if (ret) { + dev_err(cbphy->dev, "Failed enabling PHY core\n"); + goto clk_err; + } + + ret = reset_control_deassert(iphy->app_rst); + if (ret) { + dev_err(cbphy->dev, "PHY(%u:%u) reset deassert failed!\n", + COMBO_PHY_ID(iphy), PHY_ID(iphy)); + goto clk_err; + } + + /* Delay to ensure reset process is done */ + udelay(1); + + return 0; + +clk_err: + clk_disable_unprepare(cbphy->core_clk); + + return ret; +} + +static int intel_cbphy_iphy_power_off(struct intel_cbphy_iphy *iphy) +{ + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + ret = reset_control_assert(iphy->app_rst); + if (ret) { + dev_err(cbphy->dev, "PHY(%u:%u) reset assert failed!\n", + COMBO_PHY_ID(iphy), PHY_ID(iphy)); + return ret; + } + + ret = intel_cbphy_iphy_enable(iphy, false); + if (ret) { + dev_err(cbphy->dev, "Failed disabling PHY core\n"); + return ret; + } + + if (cbphy->init_cnt) + return 0; + + clk_disable_unprepare(cbphy->core_clk); + intel_cbphy_rst_assert(cbphy); + + return 0; +} + +static int intel_cbphy_init(struct phy *phy) +{ + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + mutex_lock(&cbphy->lock); + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on); + if (ret) + goto err; + + if (cbphy->phy_mode == PHY_PCIE_MODE) { + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_en_pad_refclk); + if (ret) + goto err; + } + + cbphy->init_cnt++; + +err: + mutex_unlock(&cbphy->lock); + + return ret; +} + +static int intel_cbphy_exit(struct phy *phy) +{ + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); + struct intel_combo_phy *cbphy = iphy->parent; + int ret; + + mutex_lock(&cbphy->lock); + cbphy->init_cnt--; + if (cbphy->phy_mode == PHY_PCIE_MODE) { + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_dis_pad_refclk); + if (ret) + goto err; + } + + ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off); + +err: + mutex_unlock(&cbphy->lock); + + return ret; +} + +static int intel_cbphy_calibrate(struct phy *phy) +{ + struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy); + struct intel_combo_phy *cbphy = iphy->parent; + void __iomem *cr_base = cbphy->cr_base; + int val, ret, id; + + if (cbphy->phy_mode != PHY_XPCS_MODE) + return 0; + + id = PHY_ID(iphy); + + /* trigger auto RX adaptation */ + combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id), + ADAPT_REQ_MSK, 3); + /* Wait RX adaptation to finish */ + ret = readl_poll_timeout(cr_base + CR_ADDR(PCS_XF_RX_ADAPT_ACK, id), + val, val & RX_ADAPT_ACK_BIT, 10, 5000); + if (ret) + dev_err(cbphy->dev, "RX Adaptation failed!\n"); + else + dev_dbg(cbphy->dev, "RX Adaptation success!\n"); + + /* Stop RX adaptation */ + combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id), + ADAPT_REQ_MSK, 0); + + return ret; +} + +static int intel_cbphy_fwnode_parse(struct intel_combo_phy *cbphy) +{ + struct device *dev = cbphy->dev; + struct platform_device *pdev = to_platform_device(dev); + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct fwnode_reference_args ref; + int ret; + u32 val; + + cbphy->core_clk = devm_clk_get(dev, NULL); + if (IS_ERR(cbphy->core_clk)) { + ret = PTR_ERR(cbphy->core_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Get clk failed:%d!\n", ret); + return ret; + } + + cbphy->core_rst = devm_reset_control_get_optional(dev, "core"); + if (IS_ERR(cbphy->core_rst)) { + ret = PTR_ERR(cbphy->core_rst); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Get core reset control err: %d!\n", ret); + return ret; + } + + cbphy->phy_rst = devm_reset_control_get_optional(dev, "phy"); + if (IS_ERR(cbphy->phy_rst)) { + ret = PTR_ERR(cbphy->phy_rst); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Get PHY reset control err: %d!\n", ret); + return ret; + } + + cbphy->iphy[0].app_rst = devm_reset_control_get_optional(dev, "iphy0"); + if (IS_ERR(cbphy->iphy[0].app_rst)) { + ret = PTR_ERR(cbphy->iphy[0].app_rst); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Get phy0 reset control err: %d!\n", ret); + return ret; + } + + cbphy->iphy[1].app_rst = devm_reset_control_get_optional(dev, "iphy1"); + if (IS_ERR(cbphy->iphy[1].app_rst)) { + ret = PTR_ERR(cbphy->iphy[1].app_rst); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Get phy1 reset control err: %d!\n", ret); + return ret; + } + + cbphy->app_base = devm_platform_ioremap_resource_byname(pdev, "app"); + if (IS_ERR(cbphy->app_base)) + return PTR_ERR(cbphy->app_base); + + cbphy->cr_base = devm_platform_ioremap_resource_byname(pdev, "core"); + if (IS_ERR(cbphy->cr_base)) + return PTR_ERR(cbphy->cr_base); + + /* + * syscfg and hsiocfg variables stores the handle of the registers set + * in which ComboPhy subsytem specific registers are subset. Using + * Register map framework to access the registers set. + */ + ret = fwnode_property_get_reference_args(fwnode, "intel,syscfg", NULL, + 1, 0, &ref); + if (ret < 0) + return ret; + + cbphy->id = ref.args[0]; + cbphy->syscfg = device_node_to_regmap(to_of_node(ref.fwnode)); + fwnode_handle_put(ref.fwnode); + + ret = fwnode_property_get_reference_args(fwnode, "intel,hsio", NULL, 1, + 0, &ref); + if (ret < 0) + return ret; + + cbphy->bid = ref.args[0]; + cbphy->hsiocfg = device_node_to_regmap(to_of_node(ref.fwnode)); + fwnode_handle_put(ref.fwnode); + + ret = fwnode_property_read_u32_array(fwnode, "intel,phy-mode", &val, 1); + if (ret) + return ret; + + switch (val) { + case PHY_TYPE_PCIE: + cbphy->phy_mode = PHY_PCIE_MODE; + break; + + case PHY_TYPE_SATA: + cbphy->phy_mode = PHY_SATA_MODE; + break; + + case PHY_TYPE_XPCS: + cbphy->phy_mode = PHY_XPCS_MODE; + break; + + default: + dev_err(dev, "Invalid PHY mode: %u\n", val); + return -EINVAL; + } + + cbphy->clk_rate = intel_iphy_clk_rates[cbphy->phy_mode]; + + if (fwnode_property_present(fwnode, "intel,aggregation")) + cbphy->aggr_mode = PHY_DL_MODE; + else + cbphy->aggr_mode = PHY_SL_MODE; + + return 0; +} + +static const struct phy_ops intel_cbphy_ops = { + .init = intel_cbphy_init, + .exit = intel_cbphy_exit, + .calibrate = intel_cbphy_calibrate, + .owner = THIS_MODULE, +}; + +static struct phy *intel_cbphy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct intel_combo_phy *cbphy = dev_get_drvdata(dev); + u32 iphy_id; + + if (args->args_count < 1) { + dev_err(dev, "Invalid number of arguments\n"); + return ERR_PTR(-EINVAL); + } + + iphy_id = args->args[0]; + if (iphy_id >= PHY_MAX_NUM) { + dev_err(dev, "Invalid phy instance %d\n", iphy_id); + return ERR_PTR(-EINVAL); + } + + if (cbphy->aggr_mode == PHY_DL_MODE && iphy_id == PHY_1) { + dev_err(dev, "Invalid. ComboPhy is in Dual lane mode %d\n", iphy_id); + return ERR_PTR(-EINVAL); + } + + return cbphy->iphy[iphy_id].phy; +} + +static int intel_cbphy_create(struct intel_combo_phy *cbphy) +{ + struct phy_provider *phy_provider; + struct device *dev = cbphy->dev; + struct intel_cbphy_iphy *iphy; + int i; + + for (i = 0; i < PHY_MAX_NUM; i++) { + iphy = &cbphy->iphy[i]; + iphy->parent = cbphy; + iphy->id = i; + + /* In dual lane mode skip phy creation for the second phy */ + if (cbphy->aggr_mode == PHY_DL_MODE && iphy->id == PHY_1) + continue; + + iphy->phy = devm_phy_create(dev, NULL, &intel_cbphy_ops); + if (IS_ERR(iphy->phy)) { + dev_err(dev, "PHY[%u:%u]: create PHY instance failed!\n", + COMBO_PHY_ID(iphy), PHY_ID(iphy)); + + return PTR_ERR(iphy->phy); + } + + phy_set_drvdata(iphy->phy, iphy); + } + + dev_set_drvdata(dev, cbphy); + phy_provider = devm_of_phy_provider_register(dev, intel_cbphy_xlate); + if (IS_ERR(phy_provider)) + dev_err(dev, "Register PHY provider failed!\n"); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int intel_cbphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_combo_phy *cbphy; + int ret; + + cbphy = devm_kzalloc(dev, sizeof(*cbphy), GFP_KERNEL); + if (!cbphy) + return -ENOMEM; + + cbphy->dev = dev; + cbphy->init_cnt = 0; + mutex_init(&cbphy->lock); + ret = intel_cbphy_fwnode_parse(cbphy); + if (ret) + return ret; + + platform_set_drvdata(pdev, cbphy); + + return intel_cbphy_create(cbphy); +} + +static int intel_cbphy_remove(struct platform_device *pdev) +{ + struct intel_combo_phy *cbphy = platform_get_drvdata(pdev); + + intel_cbphy_rst_assert(cbphy); + clk_disable_unprepare(cbphy->core_clk); + return 0; +} + +static const struct of_device_id of_intel_cbphy_match[] = { + { .compatible = "intel,combo-phy" }, + { .compatible = "intel,combophy-lgm" }, + {} +}; + +static struct platform_driver intel_cbphy_driver = { + .probe = intel_cbphy_probe, + .remove = intel_cbphy_remove, + .driver = { + .name = "intel-combo-phy", + .of_match_table = of_intel_cbphy_match, + } +}; + +module_platform_driver(intel_cbphy_driver); + +MODULE_DESCRIPTION("Intel Combo-phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 12e71a315a2c..089db0dea703 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -122,7 +122,6 @@ enum cpcap_gpio_mode { struct cpcap_phy_ddata { struct regmap *reg; struct device *dev; - struct clk *refclk; struct usb_phy phy; struct delayed_work detect_work; struct pinctrl *pins; @@ -707,7 +706,6 @@ static int cpcap_usb_phy_remove(struct platform_device *pdev) usb_remove_phy(&ddata->phy); cancel_delayed_work_sync(&ddata->detect_work); - clk_unprepare(ddata->refclk); regulator_disable(ddata->vusb); return 0; diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 98674ed094d9..ca9ce7e84a5c 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -18,6 +18,13 @@ config PHY_QCOM_APQ8064_SATA depends on OF select GENERIC_PHY +config PHY_QCOM_IPQ4019_USB + tristate "Qualcomm IPQ4019 USB PHY driver" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s. + config PHY_QCOM_IPQ806X_SATA tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" depends on ARCH_QCOM @@ -85,6 +92,16 @@ config PHY_QCOM_USB_HS Support for the USB high-speed ULPI compliant phy on Qualcomm chipsets. +config PHY_QCOM_USB_SNPS_FEMTO_V2 + tristate "Qualcomm SNPS FEMTO USB HS PHY V2 module" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + Enable support for the USB high-speed SNPS Femto phy on Qualcomm + chipsets. This PHY has differences in the register map compared + to the V1 variants. The PHY is paired with a Synopsys DWC3 USB + controller on Qualcomm SOCs. + config PHY_QCOM_USB_HSIC tristate "Qualcomm USB HSIC ULPI PHY module" depends on USB_ULPI_BUS diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index 1f14aeacbd70..86fb32efab79 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_ATH79_USB) += phy-ath79-usb.o obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o +obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o @@ -12,3 +13,4 @@ obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o obj-$(CONFIG_PHY_QCOM_USB_SS) += phy-qcom-usb-ss.o +obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2)+= phy-qcom-snps-femto-v2.o diff --git a/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c new file mode 100644 index 000000000000..b8ef331e1545 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018 John Crispin <john@phrozen.org> + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +struct ipq4019_usb_phy { + struct device *dev; + struct phy *phy; + void __iomem *base; + struct reset_control *por_rst; + struct reset_control *srif_rst; +}; + +static int ipq4019_ss_phy_power_off(struct phy *_phy) +{ + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); + + reset_control_assert(phy->por_rst); + msleep(10); + + return 0; +} + +static int ipq4019_ss_phy_power_on(struct phy *_phy) +{ + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); + + ipq4019_ss_phy_power_off(_phy); + + reset_control_deassert(phy->por_rst); + + return 0; +} + +static struct phy_ops ipq4019_usb_ss_phy_ops = { + .power_on = ipq4019_ss_phy_power_on, + .power_off = ipq4019_ss_phy_power_off, +}; + +static int ipq4019_hs_phy_power_off(struct phy *_phy) +{ + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); + + reset_control_assert(phy->por_rst); + msleep(10); + + reset_control_assert(phy->srif_rst); + msleep(10); + + return 0; +} + +static int ipq4019_hs_phy_power_on(struct phy *_phy) +{ + struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy); + + ipq4019_hs_phy_power_off(_phy); + + reset_control_deassert(phy->srif_rst); + msleep(10); + + reset_control_deassert(phy->por_rst); + + return 0; +} + +static struct phy_ops ipq4019_usb_hs_phy_ops = { + .power_on = ipq4019_hs_phy_power_on, + .power_off = ipq4019_hs_phy_power_off, +}; + +static const struct of_device_id ipq4019_usb_phy_of_match[] = { + { .compatible = "qcom,usb-hs-ipq4019-phy", .data = &ipq4019_usb_hs_phy_ops}, + { .compatible = "qcom,usb-ss-ipq4019-phy", .data = &ipq4019_usb_ss_phy_ops}, + { }, +}; +MODULE_DEVICE_TABLE(of, ipq4019_usb_phy_of_match); + +static int ipq4019_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct ipq4019_usb_phy *phy; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(phy->base)) { + dev_err(dev, "failed to remap register memory\n"); + return PTR_ERR(phy->base); + } + + phy->por_rst = devm_reset_control_get(phy->dev, "por_rst"); + if (IS_ERR(phy->por_rst)) { + if (PTR_ERR(phy->por_rst) != -EPROBE_DEFER) + dev_err(dev, "POR reset is missing\n"); + return PTR_ERR(phy->por_rst); + } + + phy->srif_rst = devm_reset_control_get_optional(phy->dev, "srif_rst"); + if (IS_ERR(phy->srif_rst)) + return PTR_ERR(phy->srif_rst); + + phy->phy = devm_phy_create(dev, NULL, of_device_get_match_data(dev)); + if (IS_ERR(phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy->phy); + } + phy_set_drvdata(phy->phy, phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver ipq4019_usb_phy_driver = { + .probe = ipq4019_usb_phy_probe, + .driver = { + .of_match_table = ipq4019_usb_phy_of_match, + .name = "ipq4019-usb-phy", + } +}; +module_platform_driver(ipq4019_usb_phy_driver); + +MODULE_DESCRIPTION("QCOM/IPQ4019 USB phy driver"); +MODULE_AUTHOR("John Crispin <john@phrozen.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index c190406246ab..e91040af3394 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -119,14 +119,17 @@ enum qphy_reg_layout { QPHY_PCS_AUTONOMOUS_MODE_CTRL, QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, QPHY_PCS_LFPS_RXTERM_IRQ_STATUS, + QPHY_PCS_POWER_DOWN_CONTROL, + /* Keep last to ensure regs_layout arrays are properly initialized */ + QPHY_LAYOUT_SIZE }; -static const unsigned int msm8996_ufsphy_regs_layout[] = { +static const unsigned int msm8996_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = 0x00, [QPHY_PCS_READY_STATUS] = 0x168, }; -static const unsigned int pciephy_regs_layout[] = { +static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_COM_SW_RESET] = 0x400, [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, [QPHY_COM_START_CONTROL] = 0x408, @@ -142,7 +145,7 @@ static const unsigned int pciephy_regs_layout[] = { [QPHY_PCS_STATUS] = 0x174, }; -static const unsigned int usb3phy_regs_layout[] = { +static const unsigned int usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_FLL_CNTRL1] = 0xc0, [QPHY_FLL_CNTRL2] = 0xc4, [QPHY_FLL_CNT_VAL_L] = 0xc8, @@ -156,7 +159,7 @@ static const unsigned int usb3phy_regs_layout[] = { [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178, }; -static const unsigned int qmp_v3_usb3phy_regs_layout[] = { +static const unsigned int qmp_v3_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_SW_RESET] = 0x00, [QPHY_START_CTRL] = 0x08, [QPHY_PCS_STATUS] = 0x174, @@ -165,27 +168,34 @@ static const unsigned int qmp_v3_usb3phy_regs_layout[] = { [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, }; -static const unsigned int sdm845_qmp_pciephy_regs_layout[] = { +static const unsigned int sdm845_qmp_pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_SW_RESET] = 0x00, [QPHY_START_CTRL] = 0x08, [QPHY_PCS_STATUS] = 0x174, }; -static const unsigned int sdm845_qhp_pciephy_regs_layout[] = { +static const unsigned int sdm845_qhp_pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_SW_RESET] = 0x00, [QPHY_START_CTRL] = 0x08, [QPHY_PCS_STATUS] = 0x2ac, }; -static const unsigned int sdm845_ufsphy_regs_layout[] = { +static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, +}; + +static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = 0x00, [QPHY_PCS_READY_STATUS] = 0x160, }; -static const unsigned int sm8150_ufsphy_regs_layout[] = { - [QPHY_START_CTRL] = QPHY_V4_PHY_START, - [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_READY_STATUS, - [QPHY_SW_RESET] = QPHY_V4_SW_RESET, +static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_START_CTRL] = QPHY_V4_PCS_UFS_PHY_START, + [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_UFS_READY_STATUS, + [QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET, }; static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { @@ -1272,13 +1282,121 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = { }; static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V4_RX_SIGDET_CTRL2, 0x6d), - QMP_PHY_INIT_CFG(QPHY_V4_TX_LARGE_AMP_DRV_LVL, 0x0a), - QMP_PHY_INIT_CFG(QPHY_V4_TX_SMALL_AMP_DRV_LVL, 0x02), - QMP_PHY_INIT_CFG(QPHY_V4_TX_MID_TERM_CTRL1, 0x43), - QMP_PHY_INIT_CFG(QPHY_V4_DEBUG_BUS_CLKSEL, 0x1f), - QMP_PHY_INIT_CFG(QPHY_V4_RX_MIN_HIBERN8_TIME, 0xff), - QMP_PHY_INIT_CFG(QPHY_V4_MULTI_LANE_CTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2, 0x6d), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME, 0xff), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x94), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = { + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), }; /* struct qmp_phy_cfg - per-PHY initialization config */ @@ -1445,6 +1563,10 @@ static const char * const sdm845_pciephy_clk_l[] = { "aux", "cfg_ahb", "ref", "refgen", }; +static const char * const qmp_v4_phy_clk_l[] = { + "aux", "ref_clk_src", "ref", "com_aux", +}; + static const char * const sdm845_ufs_phy_clk_l[] = { "ref", "ref_aux", }; @@ -1458,6 +1580,10 @@ static const char * const msm8996_usb3phy_reset_l[] = { "phy", "common", }; +static const char * const sc7180_usb3phy_reset_l[] = { + "phy", +}; + static const char * const sdm845_pciephy_reset_l[] = { "phy", }; @@ -1671,6 +1797,37 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .is_dual_lane_phy = true, }; +static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = qmp_v3_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), + .tx_tbl = qmp_v3_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), + .rx_tbl = qmp_v3_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), + .pcs_tbl = qmp_v3_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = sc7180_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, + .is_dual_lane_phy = true, +}; + static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -1798,6 +1955,37 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { .is_dual_lane_phy = true, }; +static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8150_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_tx_tbl), + .rx_tbl = sm8150_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_rx_tbl), + .pcs_tbl = sm8150_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, + + .has_phy_dp_com_ctrl = true, + .is_dual_lane_phy = true, +}; + static void qcom_qmp_phy_configure(void __iomem *base, const unsigned int *regs, const struct qmp_phy_init_tbl tbl[], @@ -1880,11 +2068,18 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); } - if (cfg->has_phy_com_ctrl) + if (cfg->has_phy_com_ctrl) { qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], SW_PWRDN); - else - qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); + } else { + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) + qphy_setbits(pcs, + cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + else + qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + } /* Serdes configuration */ qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl, @@ -2110,7 +2305,13 @@ static int qcom_qmp_phy_disable(struct phy *phy) qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); /* Put PHY into POWER DOWN state: active low */ - qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); + if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) { + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + cfg->pwrdn_ctrl); + } else { + qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, + cfg->pwrdn_ctrl); + } if (cfg->has_lane_rst) reset_control_assert(qphy->lane_rst); @@ -2516,6 +2717,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { .compatible = "qcom,ipq8074-qmp-pcie-phy", .data = &ipq8074_pciephy_cfg, }, { + .compatible = "qcom,sc7180-qmp-usb3-phy", + .data = &sc7180_usb3phy_cfg, + }, { .compatible = "qcom,sdm845-qhp-pcie-phy", .data = &sdm845_qhp_pciephy_cfg, }, { @@ -2536,6 +2740,12 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8150-qmp-ufs-phy", .data = &sm8150_ufsphy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-ufs-phy", + .data = &sm8150_ufsphy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-usb3-phy", + .data = &sm8150_usb3phy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index dece0e67704b..6d017a0c0c8d 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -125,7 +125,7 @@ #define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1DC #define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1E0 -/* Only for QMP V3 PHY - DP COM registers */ +/* Only for QMP V3 & V4 PHY - DP COM registers */ #define QPHY_V3_DP_COM_PHY_MODE_CTRL 0x00 #define QPHY_V3_DP_COM_SW_RESET 0x04 #define QPHY_V3_DP_COM_POWER_DOWN_CTRL 0x08 @@ -314,6 +314,14 @@ #define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60 /* Only for QMP V4 PHY - QSERDES COM registers */ +#define QSERDES_V4_COM_SSC_EN_CENTER 0x010 +#define QSERDES_V4_COM_SSC_PER1 0x01c +#define QSERDES_V4_COM_SSC_PER2 0x020 +#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0 0x024 +#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0 0x028 +#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1 0x030 +#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1 0x034 +#define QSERDES_V4_COM_SYSCLK_BUF_ENABLE 0x050 #define QSERDES_V4_COM_PLL_IVCO 0x058 #define QSERDES_V4_COM_CMN_IPTRIM 0x060 #define QSERDES_V4_COM_CP_CTRL_MODE0 0x074 @@ -330,10 +338,22 @@ #define QSERDES_V4_COM_DEC_START_MODE0 0x0bc #define QSERDES_V4_COM_LOCK_CMP2_MODE1 0x0b8 #define QSERDES_V4_COM_DEC_START_MODE1 0x0c4 +#define QSERDES_V4_COM_DIV_FRAC_START1_MODE0 0x0cc +#define QSERDES_V4_COM_DIV_FRAC_START2_MODE0 0x0d0 +#define QSERDES_V4_COM_DIV_FRAC_START3_MODE0 0x0d4 +#define QSERDES_V4_COM_DIV_FRAC_START1_MODE1 0x0d8 +#define QSERDES_V4_COM_DIV_FRAC_START2_MODE1 0x0dc +#define QSERDES_V4_COM_DIV_FRAC_START3_MODE1 0x0e0 #define QSERDES_V4_COM_VCO_TUNE_MAP 0x10c +#define QSERDES_V4_COM_VCO_TUNE1_MODE0 0x110 +#define QSERDES_V4_COM_VCO_TUNE2_MODE0 0x114 +#define QSERDES_V4_COM_VCO_TUNE1_MODE1 0x118 +#define QSERDES_V4_COM_VCO_TUNE2_MODE1 0x11c #define QSERDES_V4_COM_VCO_TUNE_INITVAL2 0x124 #define QSERDES_V4_COM_HSCLK_SEL 0x158 #define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL 0x15c +#define QSERDES_V4_COM_CORECLK_DIV_MODE1 0x16c +#define QSERDES_V4_COM_SVS_MODE_CLK_SEL 0x184 #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0 #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4 @@ -341,12 +361,16 @@ #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1 0x1b8 /* Only for QMP V4 PHY - TX registers */ +#define QSERDES_V4_TX_RES_CODE_LANE_TX 0x34 +#define QSERDES_V4_TX_RES_CODE_LANE_RX 0x38 #define QSERDES_V4_TX_LANE_MODE_1 0x84 +#define QSERDES_V4_TX_RCV_DETECT_LVL_2 0x9c #define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0xd8 #define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0xdC #define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0xe0 #define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0xe4 #define QSERDES_V4_TX_TRAN_DRVR_EMP_EN 0xb8 +#define QSERDES_V4_TX_PI_QEC_CTRL 0x104 /* Only for QMP V4 PHY - RX registers */ #define QSERDES_V4_RX_UCDR_FO_GAIN 0x008 @@ -354,17 +378,27 @@ #define QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN 0x030 #define QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034 #define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c +#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH 0x040 #define QSERDES_V4_RX_UCDR_PI_CONTROLS 0x044 #define QSERDES_V4_RX_UCDR_PI_CTRL2 0x048 +#define QSERDES_V4_RX_UCDR_SB2_THRESH1 0x04c +#define QSERDES_V4_RX_UCDR_SB2_THRESH2 0x050 +#define QSERDES_V4_RX_UCDR_SB2_GAIN1 0x054 +#define QSERDES_V4_RX_UCDR_SB2_GAIN2 0x058 +#define QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE 0x060 #define QSERDES_V4_RX_AC_JTAG_ENABLE 0x068 #define QSERDES_V4_RX_AC_JTAG_MODE 0x078 #define QSERDES_V4_RX_RX_TERM_BW 0x080 +#define QSERDES_V4_RX_VGA_CAL_CNTRL1 0x0d4 +#define QSERDES_V4_RX_VGA_CAL_CNTRL2 0x0d8 +#define QSERDES_V4_RX_GM_CAL 0x0dc #define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2 0x0ec #define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3 0x0f0 #define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4 0x0f4 #define QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW 0x0f8 #define QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH 0x0fc #define QSERDES_V4_RX_RX_IDAC_MEASURE_TIME 0x100 +#define QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110 #define QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x114 #define QSERDES_V4_RX_SIGDET_CNTRL 0x11c #define QSERDES_V4_RX_SIGDET_LVL 0x120 @@ -385,29 +419,32 @@ #define QSERDES_V4_RX_RX_MODE_10_HIGH2 0x1a0 #define QSERDES_V4_RX_RX_MODE_10_HIGH3 0x1a4 #define QSERDES_V4_RX_RX_MODE_10_HIGH4 0x1a8 +#define QSERDES_V4_RX_DFE_EN_TIMER 0x1b4 +#define QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET 0x1b8 #define QSERDES_V4_RX_DCC_CTRL1 0x1bc +#define QSERDES_V4_RX_VTH_CODE 0x1c4 -/* Only for QMP V4 PHY - PCS registers */ -#define QPHY_V4_PHY_START 0x000 -#define QPHY_V4_POWER_DOWN_CONTROL 0x004 -#define QPHY_V4_SW_RESET 0x008 -#define QPHY_V4_TIMER_20US_CORECLK_STEPS_MSB 0x00c -#define QPHY_V4_TIMER_20US_CORECLK_STEPS_LSB 0x010 -#define QPHY_V4_PLL_CNTL 0x02c -#define QPHY_V4_TX_LARGE_AMP_DRV_LVL 0x030 -#define QPHY_V4_TX_SMALL_AMP_DRV_LVL 0x038 -#define QPHY_V4_BIST_FIXED_PAT_CTRL 0x060 -#define QPHY_V4_TX_HSGEAR_CAPABILITY 0x074 -#define QPHY_V4_RX_HSGEAR_CAPABILITY 0x0b4 -#define QPHY_V4_DEBUG_BUS_CLKSEL 0x124 -#define QPHY_V4_LINECFG_DISABLE 0x148 -#define QPHY_V4_RX_MIN_HIBERN8_TIME 0x150 -#define QPHY_V4_RX_SIGDET_CTRL2 0x158 -#define QPHY_V4_TX_PWM_GEAR_BAND 0x160 -#define QPHY_V4_TX_HS_GEAR_BAND 0x168 -#define QPHY_V4_PCS_READY_STATUS 0x180 -#define QPHY_V4_TX_MID_TERM_CTRL1 0x1d8 -#define QPHY_V4_MULTI_LANE_CTRL1 0x1e0 +/* Only for QMP V4 PHY - UFS PCS registers */ +#define QPHY_V4_PCS_UFS_PHY_START 0x000 +#define QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL 0x004 +#define QPHY_V4_PCS_UFS_SW_RESET 0x008 +#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB 0x00c +#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB 0x010 +#define QPHY_V4_PCS_UFS_PLL_CNTL 0x02c +#define QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL 0x030 +#define QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL 0x038 +#define QPHY_V4_PCS_UFS_BIST_FIXED_PAT_CTRL 0x060 +#define QPHY_V4_PCS_UFS_TX_HSGEAR_CAPABILITY 0x074 +#define QPHY_V4_PCS_UFS_RX_HSGEAR_CAPABILITY 0x0b4 +#define QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL 0x124 +#define QPHY_V4_PCS_UFS_LINECFG_DISABLE 0x148 +#define QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME 0x150 +#define QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2 0x158 +#define QPHY_V4_PCS_UFS_TX_PWM_GEAR_BAND 0x160 +#define QPHY_V4_PCS_UFS_TX_HS_GEAR_BAND 0x168 +#define QPHY_V4_PCS_UFS_READY_STATUS 0x180 +#define QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1 0x1d8 +#define QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1 0x1e0 /* PCIE GEN3 COM registers */ #define PCIE_GEN3_QHP_COM_SSC_EN_CENTER 0x14 @@ -523,4 +560,161 @@ #define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5 0x16c #define PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG 0x174 +/* Only for QMP V4 PHY - USB/PCIe PCS registers */ +#define QPHY_V4_PCS_SW_RESET 0x000 +#define QPHY_V4_PCS_REVISION_ID0 0x004 +#define QPHY_V4_PCS_REVISION_ID1 0x008 +#define QPHY_V4_PCS_REVISION_ID2 0x00c +#define QPHY_V4_PCS_REVISION_ID3 0x010 +#define QPHY_V4_PCS_PCS_STATUS1 0x014 +#define QPHY_V4_PCS_PCS_STATUS2 0x018 +#define QPHY_V4_PCS_PCS_STATUS3 0x01c +#define QPHY_V4_PCS_PCS_STATUS4 0x020 +#define QPHY_V4_PCS_PCS_STATUS5 0x024 +#define QPHY_V4_PCS_PCS_STATUS6 0x028 +#define QPHY_V4_PCS_PCS_STATUS7 0x02c +#define QPHY_V4_PCS_DEBUG_BUS_0_STATUS 0x030 +#define QPHY_V4_PCS_DEBUG_BUS_1_STATUS 0x034 +#define QPHY_V4_PCS_DEBUG_BUS_2_STATUS 0x038 +#define QPHY_V4_PCS_DEBUG_BUS_3_STATUS 0x03c +#define QPHY_V4_PCS_POWER_DOWN_CONTROL 0x040 +#define QPHY_V4_PCS_START_CONTROL 0x044 +#define QPHY_V4_PCS_INSIG_SW_CTRL1 0x048 +#define QPHY_V4_PCS_INSIG_SW_CTRL2 0x04c +#define QPHY_V4_PCS_INSIG_SW_CTRL3 0x050 +#define QPHY_V4_PCS_INSIG_SW_CTRL4 0x054 +#define QPHY_V4_PCS_INSIG_SW_CTRL5 0x058 +#define QPHY_V4_PCS_INSIG_SW_CTRL6 0x05c +#define QPHY_V4_PCS_INSIG_SW_CTRL7 0x060 +#define QPHY_V4_PCS_INSIG_SW_CTRL8 0x064 +#define QPHY_V4_PCS_INSIG_MX_CTRL1 0x068 +#define QPHY_V4_PCS_INSIG_MX_CTRL2 0x06c +#define QPHY_V4_PCS_INSIG_MX_CTRL3 0x070 +#define QPHY_V4_PCS_INSIG_MX_CTRL4 0x074 +#define QPHY_V4_PCS_INSIG_MX_CTRL5 0x078 +#define QPHY_V4_PCS_INSIG_MX_CTRL7 0x07c +#define QPHY_V4_PCS_INSIG_MX_CTRL8 0x080 +#define QPHY_V4_PCS_OUTSIG_SW_CTRL1 0x084 +#define QPHY_V4_PCS_OUTSIG_MX_CTRL1 0x088 +#define QPHY_V4_PCS_CLAMP_ENABLE 0x08c +#define QPHY_V4_PCS_POWER_STATE_CONFIG1 0x090 +#define QPHY_V4_PCS_POWER_STATE_CONFIG2 0x094 +#define QPHY_V4_PCS_FLL_CNTRL1 0x098 +#define QPHY_V4_PCS_FLL_CNTRL2 0x09c +#define QPHY_V4_PCS_FLL_CNT_VAL_L 0x0a0 +#define QPHY_V4_PCS_FLL_CNT_VAL_H_TOL 0x0a4 +#define QPHY_V4_PCS_FLL_MAN_CODE 0x0a8 +#define QPHY_V4_PCS_TEST_CONTROL1 0x0ac +#define QPHY_V4_PCS_TEST_CONTROL2 0x0b0 +#define QPHY_V4_PCS_TEST_CONTROL3 0x0b4 +#define QPHY_V4_PCS_TEST_CONTROL4 0x0b8 +#define QPHY_V4_PCS_TEST_CONTROL5 0x0bc +#define QPHY_V4_PCS_TEST_CONTROL6 0x0c0 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG1 0x0c4 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG2 0x0c8 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG3 0x0cc +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG4 0x0d0 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG5 0x0d4 +#define QPHY_V4_PCS_LOCK_DETECT_CONFIG6 0x0d8 +#define QPHY_V4_PCS_REFGEN_REQ_CONFIG1 0x0dc +#define QPHY_V4_PCS_REFGEN_REQ_CONFIG2 0x0e0 +#define QPHY_V4_PCS_REFGEN_REQ_CONFIG3 0x0e4 +#define QPHY_V4_PCS_BIST_CTRL 0x0e8 +#define QPHY_V4_PCS_PRBS_POLY0 0x0ec +#define QPHY_V4_PCS_PRBS_POLY1 0x0f0 +#define QPHY_V4_PCS_FIXED_PAT0 0x0f4 +#define QPHY_V4_PCS_FIXED_PAT1 0x0f8 +#define QPHY_V4_PCS_FIXED_PAT2 0x0fc +#define QPHY_V4_PCS_FIXED_PAT3 0x100 +#define QPHY_V4_PCS_FIXED_PAT4 0x104 +#define QPHY_V4_PCS_FIXED_PAT5 0x108 +#define QPHY_V4_PCS_FIXED_PAT6 0x10c +#define QPHY_V4_PCS_FIXED_PAT7 0x110 +#define QPHY_V4_PCS_FIXED_PAT8 0x114 +#define QPHY_V4_PCS_FIXED_PAT9 0x118 +#define QPHY_V4_PCS_FIXED_PAT10 0x11c +#define QPHY_V4_PCS_FIXED_PAT11 0x120 +#define QPHY_V4_PCS_FIXED_PAT12 0x124 +#define QPHY_V4_PCS_FIXED_PAT13 0x128 +#define QPHY_V4_PCS_FIXED_PAT14 0x12c +#define QPHY_V4_PCS_FIXED_PAT15 0x130 +#define QPHY_V4_PCS_TXMGN_CONFIG 0x134 +#define QPHY_V4_PCS_G12S1_TXMGN_V0 0x138 +#define QPHY_V4_PCS_G12S1_TXMGN_V1 0x13c +#define QPHY_V4_PCS_G12S1_TXMGN_V2 0x140 +#define QPHY_V4_PCS_G12S1_TXMGN_V3 0x144 +#define QPHY_V4_PCS_G12S1_TXMGN_V4 0x148 +#define QPHY_V4_PCS_G12S1_TXMGN_V0_RS 0x14c +#define QPHY_V4_PCS_G12S1_TXMGN_V1_RS 0x150 +#define QPHY_V4_PCS_G12S1_TXMGN_V2_RS 0x154 +#define QPHY_V4_PCS_G12S1_TXMGN_V3_RS 0x158 +#define QPHY_V4_PCS_G12S1_TXMGN_V4_RS 0x15c +#define QPHY_V4_PCS_G3S2_TXMGN_MAIN 0x160 +#define QPHY_V4_PCS_G3S2_TXMGN_MAIN_RS 0x164 +#define QPHY_V4_PCS_G12S1_TXDEEMPH_M6DB 0x168 +#define QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB 0x16c +#define QPHY_V4_PCS_G3S2_PRE_GAIN 0x170 +#define QPHY_V4_PCS_G3S2_POST_GAIN 0x174 +#define QPHY_V4_PCS_G3S2_PRE_POST_OFFSET 0x178 +#define QPHY_V4_PCS_G3S2_PRE_GAIN_RS 0x17c +#define QPHY_V4_PCS_G3S2_POST_GAIN_RS 0x180 +#define QPHY_V4_PCS_G3S2_PRE_POST_OFFSET_RS 0x184 +#define QPHY_V4_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V4_PCS_RX_SIGDET_DTCT_CNTRL 0x18c +#define QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L 0x190 +#define QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H 0x194 +#define QPHY_V4_PCS_RATE_SLEW_CNTRL1 0x198 +#define QPHY_V4_PCS_RATE_SLEW_CNTRL2 0x19c +#define QPHY_V4_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x1a0 +#define QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L 0x1a4 +#define QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_H 0x1a8 +#define QPHY_V4_PCS_TSYNC_RSYNC_TIME 0x1ac +#define QPHY_V4_PCS_CDR_RESET_TIME 0x1b0 +#define QPHY_V4_PCS_TSYNC_DLY_TIME 0x1b4 +#define QPHY_V4_PCS_ELECIDLE_DLY_SEL 0x1b8 +#define QPHY_V4_PCS_CMN_ACK_OUT_SEL 0x1bc +#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG1 0x1c0 +#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG2 0x1c4 +#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG3 0x1c8 +#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG4 0x1cc +#define QPHY_V4_PCS_PCS_TX_RX_CONFIG 0x1d0 +#define QPHY_V4_PCS_RX_IDLE_DTCT_CNTRL 0x1d4 +#define QPHY_V4_PCS_RX_DCC_CAL_CONFIG 0x1d8 +#define QPHY_V4_PCS_EQ_CONFIG1 0x1dc +#define QPHY_V4_PCS_EQ_CONFIG2 0x1e0 +#define QPHY_V4_PCS_EQ_CONFIG3 0x1e4 +#define QPHY_V4_PCS_EQ_CONFIG4 0x1e8 +#define QPHY_V4_PCS_EQ_CONFIG5 0x1ec +#define QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1 0x300 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_STATUS 0x304 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL 0x308 +#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL2 0x30c +#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x310 +#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR 0x314 +#define QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL 0x318 +#define QPHY_V4_PCS_USB3_LFPS_TX_ECSTART 0x31c +#define QPHY_V4_PCS_USB3_LFPS_PER_TIMER_VAL 0x320 +#define QPHY_V4_PCS_USB3_LFPS_TX_END_CNT_U3_START 0x324 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_LOCK_TIME 0x328 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME 0x32c +#define QPHY_V4_PCS_USB3_RXEQTRAINING_CTLE_TIME 0x330 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2 0x334 +#define QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2 0x338 +#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_L 0x33c +#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_H 0x340 +#define QPHY_V4_PCS_USB3_ARCVR_DTCT_EN_PERIOD 0x344 +#define QPHY_V4_PCS_USB3_ARCVR_DTCT_CM_DLY 0x348 +#define QPHY_V4_PCS_USB3_TXONESZEROS_RUN_LENGTH 0x34c +#define QPHY_V4_PCS_USB3_ALFPS_DEGLITCH_VAL 0x350 +#define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL 0x354 +#define QPHY_V4_PCS_USB3_TEST_CONTROL 0x358 + +/* Only for QMP V4 PHY - PCS_MISC registers */ +#define QPHY_V4_PCS_MISC_TYPEC_CTRL 0x00 +#define QPHY_V4_PCS_MISC_TYPEC_PWRDN_CTRL 0x04 +#define QPHY_V4_PCS_MISC_PCS_MISC_CONFIG1 0x08 +#define QPHY_V4_PCS_MISC_CLAMP_ENABLE 0x0c +#define QPHY_V4_PCS_MISC_TYPEC_STATUS 0x10 +#define QPHY_V4_PCS_MISC_PLACEHOLDER_STATUS 0x14 + #endif diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c new file mode 100644 index 000000000000..4d74045271eb --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#define USB2_PHY_USB_PHY_UTMI_CTRL0 (0x3c) +#define SLEEPM BIT(0) +#define OPMODE_MASK GENMASK(4, 3) +#define OPMODE_NORMAL (0x00) +#define OPMODE_NONDRIVING BIT(3) +#define TERMSEL BIT(5) + +#define USB2_PHY_USB_PHY_UTMI_CTRL1 (0x40) +#define XCVRSEL BIT(0) + +#define USB2_PHY_USB_PHY_UTMI_CTRL5 (0x50) +#define POR BIT(1) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54) +#define RETENABLEN BIT(3) +#define FSEL_MASK GENMASK(7, 5) +#define FSEL_DEFAULT (0x3 << 4) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58) +#define VBUSVLDEXTSEL0 BIT(4) +#define PLLBTUNE BIT(5) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2 (0x5c) +#define VREGBYPASS BIT(0) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL1 (0x60) +#define VBUSVLDEXT0 BIT(0) + +#define USB2_PHY_USB_PHY_HS_PHY_CTRL2 (0x64) +#define USB2_AUTO_RESUME BIT(0) +#define USB2_SUSPEND_N BIT(2) +#define USB2_SUSPEND_N_SEL BIT(3) + +#define USB2_PHY_USB_PHY_CFG0 (0x94) +#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN BIT(0) +#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN BIT(1) + +#define USB2_PHY_USB_PHY_REFCLK_CTRL (0xa0) +#define REFCLK_SEL_MASK GENMASK(1, 0) +#define REFCLK_SEL_DEFAULT (0x2 << 0) + +static const char * const qcom_snps_hsphy_vreg_names[] = { + "vdda-pll", "vdda33", "vdda18", +}; + +#define SNPS_HS_NUM_VREGS ARRAY_SIZE(qcom_snps_hsphy_vreg_names) + +/** + * struct qcom_snps_hsphy - snps hs phy attributes + * + * @phy: generic phy + * @base: iomapped memory space for snps hs phy + * + * @cfg_ahb_clk: AHB2PHY interface clock + * @ref_clk: phy reference clock + * @iface_clk: phy interface clock + * @phy_reset: phy reset control + * @vregs: regulator supplies bulk data + * @phy_initialized: if PHY has been initialized correctly + */ +struct qcom_snps_hsphy { + struct phy *phy; + void __iomem *base; + + struct clk *cfg_ahb_clk; + struct clk *ref_clk; + struct reset_control *phy_reset; + struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS]; + + bool phy_initialized; +}; + +static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset, + u32 mask, u32 val) +{ + u32 reg; + + reg = readl_relaxed(base + offset); + reg &= ~mask; + reg |= val & mask; + writel_relaxed(reg, base + offset); + + /* Ensure above write is completed */ + readl_relaxed(base + offset); +} + +static int qcom_snps_hsphy_init(struct phy *phy) +{ + struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); + int ret; + + dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__); + + ret = regulator_bulk_enable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs); + if (ret) + return ret; + + ret = clk_prepare_enable(hsphy->cfg_ahb_clk); + if (ret) { + dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); + goto poweroff_phy; + } + + ret = reset_control_assert(hsphy->phy_reset); + if (ret) { + dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret); + goto disable_ahb_clk; + } + + usleep_range(100, 150); + + ret = reset_control_deassert(hsphy->phy_reset); + if (ret) { + dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret); + goto disable_ahb_clk; + } + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0, + UTMI_PHY_CMN_CTRL_OVERRIDE_EN, + UTMI_PHY_CMN_CTRL_OVERRIDE_EN); + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5, + POR, POR); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0, + FSEL_MASK, 0); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1, + PLLBTUNE, PLLBTUNE); + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_REFCLK_CTRL, + REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK); + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1, + VBUSVLDEXTSEL0, VBUSVLDEXTSEL0); + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1, + VBUSVLDEXT0, VBUSVLDEXT0); + + qcom_snps_hsphy_write_mask(hsphy->base, + USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2, + VREGBYPASS, VREGBYPASS); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2, + USB2_SUSPEND_N_SEL | USB2_SUSPEND_N, + USB2_SUSPEND_N_SEL | USB2_SUSPEND_N); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0, + SLEEPM, SLEEPM); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5, + POR, 0); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2, + USB2_SUSPEND_N_SEL, 0); + + qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0, + UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0); + + hsphy->phy_initialized = true; + + return 0; + +disable_ahb_clk: + clk_disable_unprepare(hsphy->cfg_ahb_clk); +poweroff_phy: + regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs); + + return ret; +} + +static int qcom_snps_hsphy_exit(struct phy *phy) +{ + struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy); + + reset_control_assert(hsphy->phy_reset); + clk_disable_unprepare(hsphy->cfg_ahb_clk); + regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs); + hsphy->phy_initialized = false; + + return 0; +} + +static const struct phy_ops qcom_snps_hsphy_gen_ops = { + .init = qcom_snps_hsphy_init, + .exit = qcom_snps_hsphy_exit, + .owner = THIS_MODULE, +}; + +static const struct of_device_id qcom_snps_hsphy_of_match_table[] = { + { .compatible = "qcom,sm8150-usb-hs-phy", }, + { .compatible = "qcom,usb-snps-hs-7nm-phy", }, + { .compatible = "qcom,usb-snps-femto-v2-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table); + +static int qcom_snps_hsphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qcom_snps_hsphy *hsphy; + struct phy_provider *phy_provider; + struct phy *generic_phy; + int ret, i; + int num; + + hsphy = devm_kzalloc(dev, sizeof(*hsphy), GFP_KERNEL); + if (!hsphy) + return -ENOMEM; + + hsphy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hsphy->base)) + return PTR_ERR(hsphy->base); + + hsphy->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(hsphy->ref_clk)) { + ret = PTR_ERR(hsphy->ref_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get ref clk, %d\n", ret); + return ret; + } + + hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(hsphy->phy_reset)) { + dev_err(dev, "failed to get phy core reset\n"); + return PTR_ERR(hsphy->phy_reset); + } + + num = ARRAY_SIZE(hsphy->vregs); + for (i = 0; i < num; i++) + hsphy->vregs[i].supply = qcom_snps_hsphy_vreg_names[i]; + + ret = devm_regulator_bulk_get(dev, num, hsphy->vregs); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get regulator supplies: %d\n", + ret); + return ret; + } + + generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create phy, %d\n", ret); + return ret; + } + hsphy->phy = generic_phy; + + dev_set_drvdata(dev, hsphy); + phy_set_drvdata(generic_phy, hsphy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (!IS_ERR(phy_provider)) + dev_dbg(dev, "Registered Qcom-SNPS HS phy\n"); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver qcom_snps_hsphy_driver = { + .probe = qcom_snps_hsphy_probe, + .driver = { + .name = "qcom-snps-hs-femto-v2-phy", + .of_match_table = qcom_snps_hsphy_of_match_table, + }, +}; + +module_platform_driver(qcom_snps_hsphy_driver); + +MODULE_DESCRIPTION("Qualcomm SNPS FEMTO USB HS PHY V2 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-s5pv210-usb2.c b/drivers/phy/samsung/phy-s5pv210-usb2.c index 56a5083fe6f9..32be62e49804 100644 --- a/drivers/phy/samsung/phy-s5pv210-usb2.c +++ b/drivers/phy/samsung/phy-s5pv210-usb2.c @@ -139,6 +139,10 @@ static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) udelay(10); rst &= ~rstbits; writel(rst, drv->reg_phy + S5PV210_UPHYRST); + /* The following delay is necessary for the reset sequence to be + * completed + */ + udelay(80); } else { pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); pwr |= phypwr; diff --git a/drivers/phy/ti/phy-am654-serdes.c b/drivers/phy/ti/phy-am654-serdes.c index 88a047b9fa6f..0a166d5a6414 100644 --- a/drivers/phy/ti/phy-am654-serdes.c +++ b/drivers/phy/ti/phy-am654-serdes.c @@ -77,6 +77,7 @@ static struct regmap_config serdes_am654_regmap_config = { .val_bits = 32, .reg_stride = 4, .fast_io = true, + .max_register = 0x1ffc, }; static const struct reg_field cmu_master_cdn_o = REG_FIELD(CMU_R07C, 24, 24); @@ -200,9 +201,91 @@ static int serdes_am654_power_off(struct phy *x) return 0; } -static int serdes_am654_init(struct phy *x) +#define SERDES_AM654_CFG(offset, a, b, val) \ + regmap_update_bits(phy->regmap, (offset),\ + GENMASK((a), (b)), (val) << (b)) + +static int serdes_am654_usb3_init(struct serdes_am654 *phy) +{ + SERDES_AM654_CFG(0x0000, 31, 24, 0x17); + SERDES_AM654_CFG(0x0004, 15, 8, 0x02); + SERDES_AM654_CFG(0x0004, 7, 0, 0x0e); + SERDES_AM654_CFG(0x0008, 23, 16, 0x2e); + SERDES_AM654_CFG(0x0008, 31, 24, 0x2e); + SERDES_AM654_CFG(0x0060, 7, 0, 0x4b); + SERDES_AM654_CFG(0x0060, 15, 8, 0x98); + SERDES_AM654_CFG(0x0060, 23, 16, 0x60); + SERDES_AM654_CFG(0x00d0, 31, 24, 0x45); + SERDES_AM654_CFG(0x00e8, 15, 8, 0x0e); + SERDES_AM654_CFG(0x0220, 7, 0, 0x34); + SERDES_AM654_CFG(0x0220, 15, 8, 0x34); + SERDES_AM654_CFG(0x0220, 31, 24, 0x37); + SERDES_AM654_CFG(0x0224, 7, 0, 0x37); + SERDES_AM654_CFG(0x0224, 15, 8, 0x37); + SERDES_AM654_CFG(0x0228, 23, 16, 0x37); + SERDES_AM654_CFG(0x0228, 31, 24, 0x37); + SERDES_AM654_CFG(0x022c, 7, 0, 0x37); + SERDES_AM654_CFG(0x022c, 15, 8, 0x37); + SERDES_AM654_CFG(0x0230, 15, 8, 0x2a); + SERDES_AM654_CFG(0x0230, 23, 16, 0x2a); + SERDES_AM654_CFG(0x0240, 23, 16, 0x10); + SERDES_AM654_CFG(0x0240, 31, 24, 0x34); + SERDES_AM654_CFG(0x0244, 7, 0, 0x40); + SERDES_AM654_CFG(0x0244, 23, 16, 0x34); + SERDES_AM654_CFG(0x0248, 15, 8, 0x0d); + SERDES_AM654_CFG(0x0258, 15, 8, 0x16); + SERDES_AM654_CFG(0x0258, 23, 16, 0x84); + SERDES_AM654_CFG(0x0258, 31, 24, 0xf2); + SERDES_AM654_CFG(0x025c, 7, 0, 0x21); + SERDES_AM654_CFG(0x0260, 7, 0, 0x27); + SERDES_AM654_CFG(0x0260, 15, 8, 0x04); + SERDES_AM654_CFG(0x0268, 15, 8, 0x04); + SERDES_AM654_CFG(0x0288, 15, 8, 0x2c); + SERDES_AM654_CFG(0x0330, 31, 24, 0xa0); + SERDES_AM654_CFG(0x0338, 23, 16, 0x03); + SERDES_AM654_CFG(0x0338, 31, 24, 0x00); + SERDES_AM654_CFG(0x033c, 7, 0, 0x00); + SERDES_AM654_CFG(0x0344, 31, 24, 0x18); + SERDES_AM654_CFG(0x034c, 7, 0, 0x18); + SERDES_AM654_CFG(0x039c, 23, 16, 0x3b); + SERDES_AM654_CFG(0x0a04, 7, 0, 0x03); + SERDES_AM654_CFG(0x0a14, 31, 24, 0x3c); + SERDES_AM654_CFG(0x0a18, 15, 8, 0x3c); + SERDES_AM654_CFG(0x0a38, 7, 0, 0x3e); + SERDES_AM654_CFG(0x0a38, 15, 8, 0x3e); + SERDES_AM654_CFG(0x0ae0, 7, 0, 0x07); + SERDES_AM654_CFG(0x0b6c, 23, 16, 0xcd); + SERDES_AM654_CFG(0x0b6c, 31, 24, 0x04); + SERDES_AM654_CFG(0x0b98, 23, 16, 0x03); + SERDES_AM654_CFG(0x1400, 7, 0, 0x3f); + SERDES_AM654_CFG(0x1404, 23, 16, 0x6f); + SERDES_AM654_CFG(0x1404, 31, 24, 0x6f); + SERDES_AM654_CFG(0x140c, 7, 0, 0x6f); + SERDES_AM654_CFG(0x140c, 15, 8, 0x6f); + SERDES_AM654_CFG(0x1410, 15, 8, 0x27); + SERDES_AM654_CFG(0x1414, 7, 0, 0x0c); + SERDES_AM654_CFG(0x1414, 23, 16, 0x07); + SERDES_AM654_CFG(0x1418, 23, 16, 0x40); + SERDES_AM654_CFG(0x141c, 7, 0, 0x00); + SERDES_AM654_CFG(0x141c, 15, 8, 0x1f); + SERDES_AM654_CFG(0x1428, 31, 24, 0x08); + SERDES_AM654_CFG(0x1434, 31, 24, 0x00); + SERDES_AM654_CFG(0x1444, 7, 0, 0x94); + SERDES_AM654_CFG(0x1460, 31, 24, 0x7f); + SERDES_AM654_CFG(0x1464, 7, 0, 0x43); + SERDES_AM654_CFG(0x1464, 23, 16, 0x6f); + SERDES_AM654_CFG(0x1464, 31, 24, 0x43); + SERDES_AM654_CFG(0x1484, 23, 16, 0x8f); + SERDES_AM654_CFG(0x1498, 7, 0, 0x4f); + SERDES_AM654_CFG(0x1498, 23, 16, 0x4f); + SERDES_AM654_CFG(0x007c, 31, 24, 0x0d); + SERDES_AM654_CFG(0x0b90, 15, 8, 0x0f); + + return 0; +} + +static int serdes_am654_pcie_init(struct serdes_am654 *phy) { - struct serdes_am654 *phy = phy_get_drvdata(x); int ret; ret = regmap_field_write(phy->config_version, VERSION); @@ -220,11 +303,28 @@ static int serdes_am654_init(struct phy *x) return 0; } +static int serdes_am654_init(struct phy *x) +{ + struct serdes_am654 *phy = phy_get_drvdata(x); + + switch (phy->type) { + case PHY_TYPE_PCIE: + return serdes_am654_pcie_init(phy); + case PHY_TYPE_USB3: + return serdes_am654_usb3_init(phy); + default: + return -EINVAL; + } +} + static int serdes_am654_reset(struct phy *x) { struct serdes_am654 *phy = phy_get_drvdata(x); int ret; + serdes_am654_disable_pll(phy); + serdes_am654_disable_txrx(phy); + ret = regmap_field_write(phy->por_en, 0x1); if (ret) return ret; diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index 7b51045df783..30ea5b207285 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -20,6 +20,7 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset-controller.h> +#include <dt-bindings/phy/phy.h> #define WIZ_SERDES_CTRL 0x404 #define WIZ_SERDES_TOP_CTRL 0x408 @@ -78,6 +79,8 @@ static const struct reg_field p_enable[WIZ_MAX_LANES] = { REG_FIELD(WIZ_LANECTL(3), 30, 31), }; +enum p_enable { P_ENABLE = 2, P_ENABLE_FORCE = 1, P_ENABLE_DISABLE = 0 }; + static const struct reg_field p_align[WIZ_MAX_LANES] = { REG_FIELD(WIZ_LANECTL(0), 29, 29), REG_FIELD(WIZ_LANECTL(1), 29, 29), @@ -220,6 +223,7 @@ struct wiz { struct reset_controller_dev wiz_phy_reset_dev; struct gpio_desc *gpio_typec_dir; int typec_dir_delay; + u32 lane_phy_type[WIZ_MAX_LANES]; }; static int wiz_reset(struct wiz *wiz) @@ -242,12 +246,17 @@ static int wiz_reset(struct wiz *wiz) static int wiz_mode_select(struct wiz *wiz) { u32 num_lanes = wiz->num_lanes; + enum wiz_lane_standard_mode mode; int ret; int i; for (i = 0; i < num_lanes; i++) { - ret = regmap_field_write(wiz->p_standard_mode[i], - LANE_MODE_GEN4); + if (wiz->lane_phy_type[i] == PHY_TYPE_DP) + mode = LANE_MODE_GEN1; + else + mode = LANE_MODE_GEN4; + + ret = regmap_field_write(wiz->p_standard_mode[i], mode); if (ret) return ret; } @@ -707,7 +716,7 @@ static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev, return ret; } - ret = regmap_field_write(wiz->p_enable[id - 1], false); + ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE_DISABLE); return ret; } @@ -734,7 +743,11 @@ static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev, return ret; } - ret = regmap_field_write(wiz->p_enable[id - 1], true); + if (wiz->lane_phy_type[id - 1] == PHY_TYPE_DP) + ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE); + else + ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE_FORCE); + return ret; } @@ -761,6 +774,40 @@ static const struct of_device_id wiz_id_table[] = { }; MODULE_DEVICE_TABLE(of, wiz_id_table); +static int wiz_get_lane_phy_types(struct device *dev, struct wiz *wiz) +{ + struct device_node *serdes, *subnode; + + serdes = of_get_child_by_name(dev->of_node, "serdes"); + if (!serdes) { + dev_err(dev, "%s: Getting \"serdes\"-node failed\n", __func__); + return -EINVAL; + } + + for_each_child_of_node(serdes, subnode) { + u32 reg, num_lanes = 1, phy_type = PHY_NONE; + int ret, i; + + ret = of_property_read_u32(subnode, "reg", ®); + if (ret) { + dev_err(dev, + "%s: Reading \"reg\" from \"%s\" failed: %d\n", + __func__, subnode->name, ret); + return ret; + } + of_property_read_u32(subnode, "cdns,num-lanes", &num_lanes); + of_property_read_u32(subnode, "cdns,phy-type", &phy_type); + + dev_dbg(dev, "%s: Lanes %u-%u have phy-type %u\n", __func__, + reg, reg + num_lanes - 1, phy_type); + + for (i = reg; i < reg + num_lanes; i++) + wiz->lane_phy_type[i] = phy_type; + } + + return 0; +} + static int wiz_probe(struct platform_device *pdev) { struct reset_controller_dev *phy_reset_dev; @@ -794,8 +841,10 @@ static int wiz_probe(struct platform_device *pdev) } base = devm_ioremap(dev, res.start, resource_size(&res)); - if (!base) + if (!base) { + ret = -ENOMEM; goto err_addr_to_resource; + } regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config); if (IS_ERR(regmap)) { @@ -812,6 +861,7 @@ static int wiz_probe(struct platform_device *pdev) if (num_lanes > WIZ_MAX_LANES) { dev_err(dev, "Cannot support %d lanes\n", num_lanes); + ret = -ENODEV; goto err_addr_to_resource; } @@ -844,6 +894,10 @@ static int wiz_probe(struct platform_device *pdev) } } + ret = wiz_get_lane_phy_types(dev, wiz); + if (ret) + return ret; + wiz->dev = dev; wiz->regmap = regmap; wiz->num_lanes = num_lanes; @@ -897,6 +951,7 @@ static int wiz_probe(struct platform_device *pdev) serdes_pdev = of_platform_device_create(child_node, NULL, dev); if (!serdes_pdev) { dev_WARN(dev, "Unable to create SERDES platform device\n"); + ret = -ENOMEM; goto err_pdev_create; } wiz->serdes_pdev = serdes_pdev; diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c index 3d74629d7423..cb2dd3230fa7 100644 --- a/drivers/phy/ti/phy-omap-usb2.c +++ b/drivers/phy/ti/phy-omap-usb2.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * omap-usb2.c - USB PHY, talking to musb controller in OMAP. + * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs. * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com * Author: Kishon Vijay Abraham I <kishon@ti.com> */ @@ -23,13 +23,65 @@ #include <linux/regmap.h> #include <linux/of_platform.h> -#define USB2PHY_DISCON_BYP_LATCH (1 << 31) -#define USB2PHY_ANA_CONFIG1 0x4c +#define USB2PHY_ANA_CONFIG1 0x4c +#define USB2PHY_DISCON_BYP_LATCH BIT(31) +/* SoC Specific USB2_OTG register definitions */ #define AM654_USB2_OTG_PD BIT(8) #define AM654_USB2_VBUS_DET_EN BIT(5) #define AM654_USB2_VBUSVALID_DET_EN BIT(4) +#define OMAP_DEV_PHY_PD BIT(0) +#define OMAP_USB2_PHY_PD BIT(28) + +#define AM437X_USB2_PHY_PD BIT(0) +#define AM437X_USB2_OTG_PD BIT(1) +#define AM437X_USB2_OTGVDET_EN BIT(19) +#define AM437X_USB2_OTGSESSEND_EN BIT(20) + +/* Driver Flags */ +#define OMAP_USB2_HAS_START_SRP BIT(0) +#define OMAP_USB2_HAS_SET_VBUS BIT(1) +#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT BIT(2) + +struct omap_usb { + struct usb_phy phy; + struct phy_companion *comparator; + void __iomem *pll_ctrl_base; + void __iomem *phy_base; + struct device *dev; + struct device *control_dev; + struct clk *wkupclk; + struct clk *optclk; + u8 flags; + struct regmap *syscon_phy_power; /* ctrl. reg. acces */ + unsigned int power_reg; /* power reg. index within syscon */ + u32 mask; + u32 power_on; + u32 power_off; +}; + +#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy) + +struct usb_phy_data { + const char *label; + u8 flags; + u32 mask; + u32 power_on; + u32 power_off; +}; + +static inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset) +{ + return __raw_readl(addr + offset); +} + +static inline void omap_usb_writel(void __iomem *addr, unsigned int offset, + u32 data) +{ + __raw_writel(data, addr + offset); +} + /** * omap_usb2_set_comparator - links the comparator present in the sytem with * this phy |