summaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/freescale/Kconfig1
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8m-pcie.c23
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8mq-usb.c240
-rw-r--r--drivers/phy/freescale/phy-fsl-samsung-hdmi.c10
-rw-r--r--drivers/phy/mediatek/phy-mtk-tphy.c4
-rw-r--r--drivers/phy/microchip/Kconfig1
-rw-r--r--drivers/phy/motorola/phy-mapphone-mdm6600.c4
-rw-r--r--drivers/phy/phy-can-transceiver.c13
-rw-r--r--drivers/phy/phy-core.c31
-rw-r--r--drivers/phy/qualcomm/Kconfig13
-rw-r--r--drivers/phy/qualcomm/Makefile1
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-pcie.c107
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h7
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v7.h67
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-ufs.c180
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp-usbc.c3
-rw-r--r--drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c286
-rw-r--r--drivers/phy/rockchip/Kconfig12
-rw-r--r--drivers/phy/rockchip/Makefile1
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-usb2.c4
-rw-r--r--drivers/phy/rockchip/phy-rockchip-naneng-combphy.c152
-rw-r--r--drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c1719
-rw-r--r--drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c1046
-rw-r--r--drivers/phy/rockchip/phy-rockchip-usbdp.c90
-rw-r--r--drivers/phy/samsung/Kconfig1
-rw-r--r--drivers/phy/samsung/Makefile1
-rw-r--r--drivers/phy/samsung/phy-exynos5-usbdrd.c202
-rw-r--r--drivers/phy/samsung/phy-exynosautov920-ufs.c168
-rw-r--r--drivers/phy/samsung/phy-samsung-ufs.c9
-rw-r--r--drivers/phy/samsung/phy-samsung-ufs.h4
-rw-r--r--drivers/phy/st/phy-stih407-usb.c24
31 files changed, 4191 insertions, 233 deletions
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index dcd9acff6d01..81f53564ee15 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -5,6 +5,7 @@ if (ARCH_MXC && ARM64) || COMPILE_TEST
config PHY_FSL_IMX8MQ_USB
tristate "Freescale i.MX8M USB3 PHY"
depends on OF && HAS_IOMEM
+ depends on TYPEC || TYPEC=n
select GENERIC_PHY
default ARCH_MXC && ARM64
diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
index e98361dcdead..7355d9921b64 100644
--- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
+++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
@@ -141,15 +141,9 @@ static int imx8_pcie_phy_power_on(struct phy *phy)
IMX8MM_GPR_PCIE_REF_CLK_PLL);
usleep_range(100, 200);
- switch (imx8_phy->drvdata->variant) {
- case IMX8MP:
- reset_control_deassert(imx8_phy->perst);
- fallthrough;
- case IMX8MM:
- reset_control_deassert(imx8_phy->reset);
- usleep_range(200, 500);
- break;
- }
+ reset_control_deassert(imx8_phy->perst);
+ reset_control_deassert(imx8_phy->reset);
+ usleep_range(200, 500);
/* Do the PHY common block reset */
regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14,
@@ -162,6 +156,16 @@ static int imx8_pcie_phy_power_on(struct phy *phy)
return ret;
}
+static int imx8_pcie_phy_power_off(struct phy *phy)
+{
+ struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
+
+ reset_control_assert(imx8_phy->reset);
+ reset_control_assert(imx8_phy->perst);
+
+ return 0;
+}
+
static int imx8_pcie_phy_init(struct phy *phy)
{
struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy);
@@ -182,6 +186,7 @@ static const struct phy_ops imx8_pcie_phy_ops = {
.init = imx8_pcie_phy_init,
.exit = imx8_pcie_phy_exit,
.power_on = imx8_pcie_phy_power_on,
+ .power_off = imx8_pcie_phy_power_off,
.owner = THIS_MODULE,
};
diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
index adc6394626ce..a974ef94de9a 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -10,6 +10,7 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/usb/typec_mux.h>
#define PHY_CTRL0 0x0
#define PHY_CTRL0_REF_SSP_EN BIT(2)
@@ -50,11 +51,66 @@
#define PHY_TUNE_DEFAULT 0xffffffff
+#define TCA_CLK_RST 0x00
+#define TCA_CLK_RST_SW BIT(9)
+#define TCA_CLK_RST_REF_CLK_EN BIT(1)
+#define TCA_CLK_RST_SUSPEND_CLK_EN BIT(0)
+
+#define TCA_INTR_EN 0x04
+#define TCA_INTR_STS 0x08
+
+#define TCA_GCFG 0x10
+#define TCA_GCFG_ROLE_HSTDEV BIT(4)
+#define TCA_GCFG_OP_MODE GENMASK(1, 0)
+#define TCA_GCFG_OP_MODE_SYSMODE 0
+#define TCA_GCFG_OP_MODE_SYNCMODE 1
+
+#define TCA_TCPC 0x14
+#define TCA_TCPC_VALID BIT(4)
+#define TCA_TCPC_LOW_POWER_EN BIT(3)
+#define TCA_TCPC_ORIENTATION_NORMAL BIT(2)
+#define TCA_TCPC_MUX_CONTRL GENMASK(1, 0)
+#define TCA_TCPC_MUX_CONTRL_NO_CONN 0
+#define TCA_TCPC_MUX_CONTRL_USB_CONN 1
+
+#define TCA_SYSMODE_CFG 0x18
+#define TCA_SYSMODE_TCPC_DISABLE BIT(3)
+#define TCA_SYSMODE_TCPC_FLIP BIT(2)
+
+#define TCA_CTRLSYNCMODE_CFG0 0x20
+#define TCA_CTRLSYNCMODE_CFG1 0x20
+
+#define TCA_PSTATE 0x30
+#define TCA_PSTATE_CM_STS BIT(4)
+#define TCA_PSTATE_TX_STS BIT(3)
+#define TCA_PSTATE_RX_PLL_STS BIT(2)
+#define TCA_PSTATE_PIPE0_POWER_DOWN GENMASK(1, 0)
+
+#define TCA_GEN_STATUS 0x34
+#define TCA_GEN_DEV_POR BIT(12)
+#define TCA_GEN_REF_CLK_SEL BIT(8)
+#define TCA_GEN_TYPEC_FLIP_INVERT BIT(4)
+#define TCA_GEN_PHY_TYPEC_DISABLE BIT(3)
+#define TCA_GEN_PHY_TYPEC_FLIP BIT(2)
+
+#define TCA_VBUS_CTRL 0x40
+#define TCA_VBUS_STATUS 0x44
+
+#define TCA_INFO 0xfc
+
+struct tca_blk {
+ struct typec_switch_dev *sw;
+ void __iomem *base;
+ struct mutex mutex;
+ enum typec_orientation orientation;
+};
+
struct imx8mq_usb_phy {
struct phy *phy;
struct clk *clk;
void __iomem *base;
struct regulator *vbus;
+ struct tca_blk *tca;
u32 pcs_tx_swing_full;
u32 pcs_tx_deemph_3p5db;
u32 tx_vref_tune;
@@ -64,6 +120,172 @@ struct imx8mq_usb_phy {
u32 comp_dis_tune;
};
+
+static void tca_blk_orientation_set(struct tca_blk *tca,
+ enum typec_orientation orientation);
+
+#ifdef CONFIG_TYPEC
+
+static int tca_blk_typec_switch_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct imx8mq_usb_phy *imx_phy = typec_switch_get_drvdata(sw);
+ struct tca_blk *tca = imx_phy->tca;
+ int ret;
+
+ if (tca->orientation == orientation)
+ return 0;
+
+ ret = clk_prepare_enable(imx_phy->clk);
+ if (ret)
+ return ret;
+
+ tca_blk_orientation_set(tca, orientation);
+ clk_disable_unprepare(imx_phy->clk);
+
+ return 0;
+}
+
+static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device *pdev,
+ struct imx8mq_usb_phy *imx_phy)
+{
+ struct device *dev = &pdev->dev;
+ struct typec_switch_dev *sw;
+ struct typec_switch_desc sw_desc = { };
+
+ sw_desc.drvdata = imx_phy;
+ sw_desc.fwnode = dev->fwnode;
+ sw_desc.set = tca_blk_typec_switch_set;
+ sw_desc.name = NULL;
+
+ sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(sw)) {
+ dev_err(dev, "Error register tca orientation switch: %ld",
+ PTR_ERR(sw));
+ return NULL;
+ }
+
+ return sw;
+}
+
+static void tca_blk_put_typec_switch(struct typec_switch_dev *sw)
+{
+ typec_switch_unregister(sw);
+}
+
+#else
+
+static struct typec_switch_dev *tca_blk_get_typec_switch(struct platform_device *pdev,
+ struct imx8mq_usb_phy *imx_phy)
+{
+ return NULL;
+}
+
+static void tca_blk_put_typec_switch(struct typec_switch_dev *sw) {}
+
+#endif /* CONFIG_TYPEC */
+
+static void tca_blk_orientation_set(struct tca_blk *tca,
+ enum typec_orientation orientation)
+{
+ u32 val;
+
+ mutex_lock(&tca->mutex);
+
+ if (orientation == TYPEC_ORIENTATION_NONE) {
+ /*
+ * use Controller Synced Mode for TCA low power enable and
+ * put PHY to USB safe state.
+ */
+ val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYNCMODE);
+ writel(val, tca->base + TCA_GCFG);
+
+ val = TCA_TCPC_VALID | TCA_TCPC_LOW_POWER_EN;
+ writel(val, tca->base + TCA_TCPC);
+
+ goto out;
+ }
+
+ /* use System Configuration Mode for TCA mux control. */
+ val = FIELD_PREP(TCA_GCFG_OP_MODE, TCA_GCFG_OP_MODE_SYSMODE);
+ writel(val, tca->base + TCA_GCFG);
+
+ /* Disable TCA module */
+ val = readl(tca->base + TCA_SYSMODE_CFG);
+ val |= TCA_SYSMODE_TCPC_DISABLE;
+ writel(val, tca->base + TCA_SYSMODE_CFG);
+
+ if (orientation == TYPEC_ORIENTATION_REVERSE)
+ val |= TCA_SYSMODE_TCPC_FLIP;
+ else if (orientation == TYPEC_ORIENTATION_NORMAL)
+ val &= ~TCA_SYSMODE_TCPC_FLIP;
+
+ writel(val, tca->base + TCA_SYSMODE_CFG);
+
+ /* Enable TCA module */
+ val &= ~TCA_SYSMODE_TCPC_DISABLE;
+ writel(val, tca->base + TCA_SYSMODE_CFG);
+
+out:
+ tca->orientation = orientation;
+ mutex_unlock(&tca->mutex);
+}
+
+static void tca_blk_init(struct tca_blk *tca)
+{
+ u32 val;
+
+ /* reset XBar block */
+ val = readl(tca->base + TCA_CLK_RST);
+ val &= ~TCA_CLK_RST_SW;
+ writel(val, tca->base + TCA_CLK_RST);
+
+ udelay(100);
+
+ /* clear reset */
+ val |= TCA_CLK_RST_SW;
+ writel(val, tca->base + TCA_CLK_RST);
+
+ tca_blk_orientation_set(tca, tca->orientation);
+}
+
+static struct tca_blk *imx95_usb_phy_get_tca(struct platform_device *pdev,
+ struct imx8mq_usb_phy *imx_phy)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct tca_blk *tca;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return NULL;
+
+ tca = devm_kzalloc(dev, sizeof(*tca), GFP_KERNEL);
+ if (!tca)
+ return ERR_PTR(-ENOMEM);
+
+ tca->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tca->base))
+ return ERR_CAST(tca->base);
+
+ mutex_init(&tca->mutex);
+
+ tca->orientation = TYPEC_ORIENTATION_NORMAL;
+ tca->sw = tca_blk_get_typec_switch(pdev, imx_phy);
+
+ return tca;
+}
+
+static void imx95_usb_phy_put_tca(struct imx8mq_usb_phy *imx_phy)
+{
+ struct tca_blk *tca = imx_phy->tca;
+
+ if (!tca)
+ return;
+
+ tca_blk_put_typec_switch(tca->sw);
+}
+
static u32 phy_tx_vref_tune_from_property(u32 percent)
{
percent = clamp(percent, 94U, 124U);
@@ -315,6 +537,9 @@ static int imx8mp_usb_phy_init(struct phy *phy)
imx8m_phy_tune(imx_phy);
+ if (imx_phy->tca)
+ tca_blk_init(imx_phy->tca);
+
return 0;
}
@@ -359,6 +584,8 @@ static const struct of_device_id imx8mq_usb_phy_of_match[] = {
.data = &imx8mq_usb_phy_ops,},
{.compatible = "fsl,imx8mp-usb-phy",
.data = &imx8mp_usb_phy_ops,},
+ {.compatible = "fsl,imx95-usb-phy",
+ .data = &imx8mp_usb_phy_ops,},
{ }
};
MODULE_DEVICE_TABLE(of, imx8mq_usb_phy_of_match);
@@ -398,6 +625,11 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
phy_set_drvdata(imx_phy->phy, imx_phy);
+ imx_phy->tca = imx95_usb_phy_get_tca(pdev, imx_phy);
+ if (IS_ERR(imx_phy->tca))
+ return dev_err_probe(dev, PTR_ERR(imx_phy->tca),
+ "failed to get tca\n");
+
imx8m_get_phy_tuning_data(imx_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
@@ -405,8 +637,16 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
+static void imx8mq_usb_phy_remove(struct platform_device *pdev)
+{
+ struct imx8mq_usb_phy *imx_phy = platform_get_drvdata(pdev);
+
+ imx95_usb_phy_put_tca(imx_phy);
+}
+
static struct platform_driver imx8mq_usb_phy_driver = {
.probe = imx8mq_usb_phy_probe,
+ .remove = imx8mq_usb_phy_remove,
.driver = {
.name = "imx8mq-usb-phy",
.of_match_table = imx8mq_usb_phy_of_match,
diff --git a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
index e4c0a82d16d9..10fbe8dee116 100644
--- a/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
+++ b/drivers/phy/freescale/phy-fsl-samsung-hdmi.c
@@ -668,7 +668,7 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
if (IS_ERR(phy->regs))
return PTR_ERR(phy->regs);
- phy->apbclk = devm_clk_get(phy->dev, "apb");
+ phy->apbclk = devm_clk_get_enabled(phy->dev, "apb");
if (IS_ERR(phy->apbclk))
return dev_err_probe(phy->dev, PTR_ERR(phy->apbclk),
"failed to get apb clk\n");
@@ -678,12 +678,6 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
return dev_err_probe(phy->dev, PTR_ERR(phy->refclk),
"failed to get ref clk\n");
- ret = clk_prepare_enable(phy->apbclk);
- if (ret) {
- dev_err(phy->dev, "failed to enable apbclk\n");
- return ret;
- }
-
pm_runtime_get_noresume(phy->dev);
pm_runtime_set_active(phy->dev);
pm_runtime_enable(phy->dev);
@@ -699,8 +693,6 @@ static int fsl_samsung_hdmi_phy_probe(struct platform_device *pdev)
return 0;
register_clk_failed:
- clk_disable_unprepare(phy->apbclk);
-
return ret;
}
diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c
index a496fbe3352b..644a34bd2b0b 100644
--- a/drivers/phy/mediatek/phy-mtk-tphy.c
+++ b/drivers/phy/mediatek/phy-mtk-tphy.c
@@ -1195,7 +1195,7 @@ static int phy_type_syscon_get(struct mtk_phy_instance *instance,
int ret;
/* type switch function is optional */
- if (!of_property_read_bool(dn, "mediatek,syscon-type"))
+ if (!of_property_present(dn, "mediatek,syscon-type"))
return 0;
ret = of_parse_phandle_with_fixed_args(dn, "mediatek,syscon-type",
@@ -1258,7 +1258,7 @@ static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instanc
}
/* software efuse is optional */
- instance->efuse_sw_en = device_property_read_bool(dev, "nvmem-cells");
+ instance->efuse_sw_en = device_property_present(dev, "nvmem-cells");
if (!instance->efuse_sw_en)
return 0;
diff --git a/drivers/phy/microchip/Kconfig b/drivers/phy/microchip/Kconfig
index 38039ed0754c..2f0045e874ac 100644
--- a/drivers/phy/microchip/Kconfig
+++ b/drivers/phy/microchip/Kconfig
@@ -15,6 +15,7 @@ config PHY_SPARX5_SERDES
config PHY_LAN966X_SERDES
tristate "SerDes PHY driver for Microchip LAN966X"
select GENERIC_PHY
+ depends on SOC_LAN966 || MCHP_LAN966X_PCI || COMPILE_TEST
depends on OF
depends on MFD_SYSCON
help
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 152344e4f7e4..fd0e0cd1c1cf 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -177,9 +177,7 @@ static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
values[0] = val;
- gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc,
- ddata->cmd_gpios->info, values);
+ gpiod_multi_set_value_cansleep(ddata->cmd_gpios, values);
}
/**
diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
index ee4ce4249698..2bec70615449 100644
--- a/drivers/phy/phy-can-transceiver.c
+++ b/drivers/phy/phy-can-transceiver.c
@@ -103,6 +103,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
struct phy *phy;
struct gpio_desc *standby_gpio;
struct gpio_desc *enable_gpio;
+ struct mux_state *mux_state;
u32 max_bitrate = 0;
int err;
@@ -113,13 +114,11 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
match = of_match_node(can_transceiver_phy_ids, pdev->dev.of_node);
drvdata = match->data;
- if (of_property_read_bool(dev->of_node, "mux-states")) {
- struct mux_state *mux_state;
-
- mux_state = devm_mux_state_get(dev, NULL);
- if (IS_ERR(mux_state))
- return dev_err_probe(&pdev->dev, PTR_ERR(mux_state),
- "failed to get mux\n");
+ mux_state = devm_mux_state_get(dev, NULL);
+ if (IS_ERR(mux_state)) {
+ if (PTR_ERR(mux_state) == -EPROBE_DEFER)
+ return PTR_ERR(mux_state);
+ } else {
can_transceiver_phy->mux_state = mux_state;
}
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 8dfdce605a90..8e2daea81666 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -214,30 +214,6 @@ int phy_pm_runtime_put_sync(struct phy *phy)
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync);
-void phy_pm_runtime_allow(struct phy *phy)
-{
- if (!phy)
- return;
-
- if (!pm_runtime_enabled(&phy->dev))
- return;
-
- pm_runtime_allow(&phy->dev);
-}
-EXPORT_SYMBOL_GPL(phy_pm_runtime_allow);
-
-void phy_pm_runtime_forbid(struct phy *phy)
-{
- if (!phy)
- return;
-
- if (!pm_runtime_enabled(&phy->dev))
- return;
-
- pm_runtime_forbid(&phy->dev);
-}
-EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
-
/**
* phy_init - phy internal initialization before phy operation
* @phy: the phy returned by phy_get()
@@ -405,13 +381,14 @@ EXPORT_SYMBOL_GPL(phy_power_off);
int phy_set_mode_ext(struct phy *phy, enum phy_mode mode, int submode)
{
- int ret;
+ int ret = 0;
- if (!phy || !phy->ops->set_mode)
+ if (!phy)
return 0;
mutex_lock(&phy->mutex);
- ret = phy->ops->set_mode(phy, mode, submode);
+ if (phy->ops->set_mode)
+ ret = phy->ops->set_mode(phy, mode, submode);
if (!ret)
phy->attrs.mode = mode;
mutex_unlock(&phy->mutex);
diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index 846f8c99547f..c1e0a11ddd76 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -154,6 +154,19 @@ config PHY_QCOM_M31_USB
management. This driver is required even for peripheral only or
host only mode configurations.
+config PHY_QCOM_UNIPHY_PCIE_28LP
+ bool "PCIE UNIPHY 28LP PHY driver"
+ depends on ARCH_QCOM
+ depends on COMMON_CLK
+ depends on HAS_IOMEM
+ depends on OF
+ select GENERIC_PHY
+ help
+ Enable this to support the PCIe UNIPHY 28LP phy transceiver that
+ is used with PCIe controllers on Qualcomm IPQ5332 chips. It
+ handles PHY initialization, clock management required after
+ resetting the hardware and power management.
+
config PHY_QCOM_USB_HS
tristate "Qualcomm USB HS PHY module"
depends on USB_ULPI_BUS
diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile
index eb60e950ad53..42038bc30974 100644
--- a/drivers/phy/qualcomm/Makefile
+++ b/drivers/phy/qualcomm/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY) += phy-qcom-qmp-usb-legacy.o
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
obj-$(CONFIG_PHY_QCOM_SNPS_EUSB2) += phy-qcom-snps-eusb2.o
obj-$(CONFIG_PHY_QCOM_EUSB2_REPEATER) += phy-qcom-eusb2-repeater.o
+obj-$(CONFIG_PHY_QCOM_UNIPHY_PCIE_28LP) += phy-qcom-uniphy-pcie-28lp.o
obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o
obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
index 018bbb300830..c232b8fe9846 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c
@@ -805,6 +805,58 @@ static const struct qmp_phy_init_tbl qcs615_pcie_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0, 0xe),
};
+static const struct qmp_phy_init_tbl qcs8300_qmp_gen4x2_pcie_rx_alt_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_PI_CONTROLS, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B0, 0x9b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1, 0xb0),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2, 0xd2),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3, 0xf0),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B4, 0x42),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B0, 0x9b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B1, 0xfb),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B2, 0xd2),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B3, 0xec),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B4, 0x43),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B5, 0xdd),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B6, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B0, 0xf3),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B1, 0xf8),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B2, 0xec),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B3, 0xd6),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B4, 0x83),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B5, 0xf5),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B6, 0x5e),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_PHPRE_CTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3, 0x37),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_3, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_Q_PI_INTRINSIC_BIAS_RATE32, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_SO_GAIN_RATE3, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_CNTRL1, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_MAN_VAL, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0b),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x7c),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_IDAC_SAOFFSET, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_DAC_ENABLE1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_GM_CAL, 0x05),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2, 0x1f),
+};
+
static const struct qmp_phy_init_tbl sdm845_qmp_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
@@ -3336,6 +3388,40 @@ static const struct qmp_phy_cfg qcs615_pciephy_cfg = {
.phy_status = PHYSTATUS,
};
+static const struct qmp_phy_cfg qcs8300_qmp_gen4x2_pciephy_cfg = {
+ .lanes = 2,
+ .offsets = &qmp_pcie_offsets_v5_20,
+
+ .tbls = {
+ .serdes = sa8775p_qmp_gen4x2_pcie_serdes_alt_tbl,
+ .serdes_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_serdes_alt_tbl),
+ .tx = sa8775p_qmp_gen4_pcie_tx_tbl,
+ .tx_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_tx_tbl),
+ .rx = qcs8300_qmp_gen4x2_pcie_rx_alt_tbl,
+ .rx_num = ARRAY_SIZE(qcs8300_qmp_gen4x2_pcie_rx_alt_tbl),
+ .pcs = sa8775p_qmp_gen4x2_pcie_pcs_alt_tbl,
+ .pcs_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_pcs_alt_tbl),
+ .pcs_misc = sa8775p_qmp_gen4_pcie_pcs_misc_tbl,
+ .pcs_misc_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_pcs_misc_tbl),
+ },
+
+ .tbls_rc = &(const struct qmp_phy_cfg_tbls) {
+ .serdes = sa8775p_qmp_gen4x2_pcie_rc_serdes_alt_tbl,
+ .serdes_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_rc_serdes_alt_tbl),
+ .pcs_misc = sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl,
+ .pcs_misc_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl),
+ },
+
+ .reset_list = sdm845_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = pciephy_v5_regs_layout,
+
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS_4_20,
+};
+
static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = {
.lanes = 1,
@@ -4156,6 +4242,21 @@ static const struct qmp_phy_cfg x1e80100_qmp_gen4x8_pciephy_cfg = {
.has_nocsr_reset = true,
};
+static const struct qmp_phy_cfg qmp_v6_gen4x4_pciephy_cfg = {
+ .lanes = 4,
+
+ .offsets = &qmp_pcie_offsets_v6_20,
+
+ .reset_list = sdm845_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = pciephy_v6_regs_layout,
+
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .phy_status = PHYSTATUS_4_20,
+};
+
static void qmp_pcie_init_port_b(struct qmp_pcie *qmp, const struct qmp_phy_cfg_tbls *tbls)
{
const struct qmp_phy_cfg *cfg = qmp->cfg;
@@ -4877,6 +4978,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = {
.compatible = "qcom,qcs615-qmp-gen3x1-pcie-phy",
.data = &qcs615_pciephy_cfg,
}, {
+ .compatible = "qcom,qcs8300-qmp-gen4x2-pcie-phy",
+ .data = &qcs8300_qmp_gen4x2_pciephy_cfg,
+ }, {
.compatible = "qcom,sa8775p-qmp-gen4x2-pcie-phy",
.data = &sa8775p_qmp_gen4x2_pciephy_cfg,
}, {
@@ -4960,6 +5064,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = {
}, {
.compatible = "qcom,x1e80100-qmp-gen4x8-pcie-phy",
.data = &x1e80100_qmp_gen4x8_pciephy_cfg,
+ }, {
+ .compatible = "qcom,x1p42100-qmp-gen4x4-pcie-phy",
+ .data = &qmp_v6_gen4x4_pciephy_cfg,
},
{ },
};
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h
index 328c6c0b0b09..258f3d30742e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v6.h
@@ -86,4 +86,11 @@
#define QSERDES_V6_COM_CMN_STATUS 0x1d0
#define QSERDES_V6_COM_C_READY_STATUS 0x1f8
+#define QSERDES_V6_COM_ADAPTIVE_ANALOG_CONFIG 0x268
+#define QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE0 0x26c
+#define QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE0 0x270
+#define QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE0 0x274
+#define QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE1 0x278
+#define QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE1 0x27c
+#define QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE1 0x280
#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v7.h
new file mode 100644
index 000000000000..3f0522492f85
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-ufs-v7.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2024, Linaro Limited
+ */
+
+#ifndef QCOM_PHY_QMP_QSERDES_TXRX_UFS_V7_H_
+#define QCOM_PHY_QMP_QSERDES_TXRX_UFS_V7_H_
+
+#define QSERDES_UFS_V7_TX_RES_CODE_LANE_TX 0x28
+#define QSERDES_UFS_V7_TX_RES_CODE_LANE_RX 0x2c
+#define QSERDES_UFS_V7_TX_RES_CODE_LANE_OFFSET_TX 0x30
+#define QSERDES_UFS_V7_TX_RES_CODE_LANE_OFFSET_RX 0x34
+#define QSERDES_UFS_V7_TX_LANE_MODE_1 0x7c
+#define QSERDES_UFS_V7_TX_FR_DCC_CTRL 0x108
+
+#define QSERDES_UFS_V7_RX_UCDR_FASTLOCK_FO_GAIN_RATE4 0x10
+#define QSERDES_UFS_V7_RX_UCDR_FASTLOCK_SO_GAIN_RATE4 0x24
+#define QSERDES_UFS_V7_RX_UCDR_SO_SATURATION 0x28
+#define QSERDES_UFS_V7_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4 0x54
+#define QSERDES_UFS_V7_RX_UCDR_PI_CTRL1 0x58
+#define QSERDES_UFS_V7_RX_TERM_BW_CTRL0 0xc4
+#define QSERDES_UFS_V7_RX_UCDR_FO_GAIN_RATE2 0xd4
+#define QSERDES_UFS_V7_RX_UCDR_FO_GAIN_RATE4 0xdc
+#define QSERDES_UFS_V7_RX_UCDR_SO_GAIN_RATE4 0xf0
+#define QSERDES_UFS_V7_RX_UCDR_PI_CONTROLS 0xf4
+#define QSERDES_UFS_V7_RX_VGA_CAL_MAN_VAL 0x178
+#define QSERDES_UFS_V7_RX_EQU_ADAPTOR_CNTRL4 0x1b4
+#define QSERDES_UFS_V7_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x1cc
+#define QSERDES_UFS_V7_RX_OFFSET_ADAPTOR_CNTRL3 0x1d4
+#define QSERDES_UFS_V7_RX_INTERFACE_MODE 0x1f0
+#define QSERDES_UFS_V7_RX_MODE_RATE_0_1_B0 0x218
+#define QSERDES_UFS_V7_RX_MODE_RATE_0_1_B1 0x21C
+#define QSERDES_UFS_V7_RX_MODE_RATE_0_1_B2 0x220
+#define QSERDES_UFS_V7_RX_MODE_RATE_0_1_B3 0x224
+#define QSERDES_UFS_V7_RX_MODE_RATE_0_1_B4 0x228
+#define QSERDES_UFS_V7_RX_MODE_RATE_0_1_B6 0x230
+#define QSERDES_UFS_V7_RX_MODE_RATE_0_1_B7 0x234
+#define QSERDES_UFS_V7_RX_MODE_RATE2_B3 0x248
+#define QSERDES_UFS_V7_RX_MODE_RATE2_B6 0x254
+#define QSERDES_UFS_V7_RX_MODE_RATE2_B7 0x258
+#define QSERDES_UFS_V7_RX_MODE_RATE3_B0 0x260
+#define QSERDES_UFS_V7_RX_MODE_RATE3_B1 0x264
+#define QSERDES_UFS_V7_RX_MODE_RATE3_B2 0x268
+#define QSERDES_UFS_V7_RX_MODE_RATE3_B3 0x26c
+#define QSERDES_UFS_V7_RX_MODE_RATE3_B4 0x270
+#define QSERDES_UFS_V7_RX_MODE_RATE3_B5 0x274
+#define QSERDES_UFS_V7_RX_MODE_RATE3_B7 0x27c
+#define QSERDES_UFS_V7_RX_MODE_RATE3_B8 0x280
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SA_B0 0x284
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SA_B1 0x288
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SA_B2 0x28c
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SA_B3 0x290
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SA_B4 0x294
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SA_B5 0x298
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SA_B6 0x29c
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SA_B7 0x2a0
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SB_B0 0x2a8
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SB_B1 0x2ac
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SB_B2 0x2b0
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SB_B3 0x2b4
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SB_B4 0x2b8
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SB_B5 0x2bc
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SB_B6 0x2c0
+#define QSERDES_UFS_V7_RX_MODE_RATE4_SB_B7 0x2c4
+#define QSERDES_UFS_V7_RX_DLL0_FTUNE_CTRL 0x348
+#define QSERDES_UFS_V7_RX_SIGDET_CAL_TRIM 0x380
+#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
index d964bdfe8700..45b3b792696e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c
@@ -31,6 +31,7 @@
#include "phy-qcom-qmp-pcs-ufs-v6.h"
#include "phy-qcom-qmp-qserdes-txrx-ufs-v6.h"
+#include "phy-qcom-qmp-qserdes-txrx-ufs-v7.h"
/* QPHY_PCS_READY_STATUS bit */
#define PCS_READY BIT(0)
@@ -949,6 +950,124 @@ static const struct qmp_phy_init_tbl sm8650_ufsphy_g5_pcs[] = {
QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSG5_SYNC_WAIT_TIME, 0x9e),
};
+static const struct qmp_phy_init_tbl sm8750_ufsphy_serdes[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_SYSCLK_EN_SEL, 0xd9),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_CONFIG_1, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_SEL_1, 0x11),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_HSCLK_HS_SWITCH_SEL_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP_CFG, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_IVCO_MODE1, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IETRIM, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CMN_IPTRIM, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_MAP, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_VCO_TUNE_CTRL, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_ADAPTIVE_ANALOG_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE0, 0x41),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE0, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE0, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE0, 0x7f),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE0, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x92),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_DEC_START_MODE1, 0x4c),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCTRL_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_CP_CTRL_ADAPTIVE_MODE1, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_RCCTRL_ADAPTIVE_MODE1, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_PLL_CCTRL_ADAPTIVE_MODE1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP1_MODE1, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_LOCK_CMP2_MODE1, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xbe),
+ QMP_PHY_INIT_CFG(QSERDES_V6_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x23),
+};
+
+static const struct qmp_phy_init_tbl sm8750_ufsphy_tx[] = {
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_TX_LANE_MODE_1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_TX_RES_CODE_LANE_OFFSET_TX, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_TX_RES_CODE_LANE_OFFSET_RX, 0x17),
+};
+
+static const struct qmp_phy_init_tbl sm8750_ufsphy_rx[] = {
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_FO_GAIN_RATE2, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_FO_GAIN_RATE4, 0x0c),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_SO_GAIN_RATE4, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_PI_CONTROLS, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_OFFSET_ADAPTOR_CNTRL3, 0x0e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_FASTLOCK_COUNT_HIGH_RATE4, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_FASTLOCK_FO_GAIN_RATE4, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_FASTLOCK_SO_GAIN_RATE4, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_VGA_CAL_MAN_VAL, 0x8e),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_EQU_ADAPTOR_CNTRL4, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE_0_1_B0, 0xce),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE_0_1_B1, 0xce),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE_0_1_B2, 0x18),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE_0_1_B3, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE_0_1_B4, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE_0_1_B6, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE_0_1_B7, 0x62),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE2_B3, 0x9a),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE2_B6, 0xe2),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE2_B7, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE3_B0, 0x1b),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE3_B1, 0x1b),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE3_B2, 0x98),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE3_B3, 0x9b),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE3_B4, 0x2a),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE3_B5, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE3_B7, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE3_B8, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SA_B0, 0x93),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SA_B1, 0x93),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SA_B2, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SA_B3, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SA_B4, 0x5f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SA_B5, 0x92),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SA_B6, 0xe3),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SA_B7, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SB_B0, 0x9b),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SB_B1, 0x9b),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SB_B2, 0x60),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SB_B3, 0x99),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SB_B4, 0x5f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SB_B5, 0x92),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SB_B6, 0xfb),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_MODE_RATE4_SB_B7, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_SO_SATURATION, 0x1f),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_UCDR_PI_CTRL1, 0x94),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_TERM_BW_CTRL0, 0xfa),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_DLL0_FTUNE_CTRL, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_UFS_V7_RX_SIGDET_CAL_TRIM, 0x77),
+};
+
+static const struct qmp_phy_init_tbl sm8750_ufsphy_pcs[] = {
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PCS_CTRL1, 0x40),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x68),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S4, 0x0e),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S5, 0x12),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S6, 0x15),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S7, 0x19),
+};
+
+static const struct qmp_phy_init_tbl sm8750_ufsphy_g4_pcs[] = {
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_HSGEAR_CAPABILITY, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_HSGEAR_CAPABILITY, 0x04),
+};
+
+static const struct qmp_phy_init_tbl sm8750_ufsphy_hs_b_pcs[] = {
+ QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PCS_CTRL1, 0x41),
+};
+
struct qmp_ufs_offsets {
u16 serdes;
u16 pcs;
@@ -1523,6 +1642,45 @@ static const struct qmp_phy_cfg sm8650_ufsphy_cfg = {
.regs = ufsphy_v6_regs_layout,
};
+static const struct qmp_phy_cfg sm8750_ufsphy_cfg = {
+ .lanes = 2,
+
+ .offsets = &qmp_ufs_offsets_v6,
+ .max_supported_gear = UFS_HS_G5,
+
+ .tbls = {
+ .serdes = sm8750_ufsphy_serdes,
+ .serdes_num = ARRAY_SIZE(sm8750_ufsphy_serdes),
+ .tx = sm8750_ufsphy_tx,
+ .tx_num = ARRAY_SIZE(sm8750_ufsphy_tx),
+ .rx = sm8750_ufsphy_rx,
+ .rx_num = ARRAY_SIZE(sm8750_ufsphy_rx),
+ .pcs = sm8750_ufsphy_pcs,
+ .pcs_num = ARRAY_SIZE(sm8750_ufsphy_pcs),
+ },
+
+ .tbls_hs_b = {
+ .pcs = sm8750_ufsphy_hs_b_pcs,
+ .pcs_num = ARRAY_SIZE(sm8750_ufsphy_hs_b_pcs),
+ },
+
+ .tbls_hs_overlay[0] = {
+ .pcs = sm8750_ufsphy_g4_pcs,
+ .pcs_num = ARRAY_SIZE(sm8750_ufsphy_g4_pcs),
+ .max_gear = UFS_HS_G4,
+ },
+ .tbls_hs_overlay[1] = {
+ .pcs = sm8650_ufsphy_g5_pcs,
+ .pcs_num = ARRAY_SIZE(sm8650_ufsphy_g5_pcs),
+ .max_gear = UFS_HS_G5,
+ },
+
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = ufsphy_v6_regs_layout,
+
+};
+
static void qmp_ufs_serdes_init(struct qmp_ufs *qmp, const struct qmp_phy_cfg_tbls *tbls)
{
void __iomem *serdes = qmp->serdes;
@@ -1578,23 +1736,25 @@ static int qmp_ufs_get_gear_overlay(struct qmp_ufs *qmp, const struct qmp_phy_cf
return ret;
}
+static void qmp_ufs_init_all(struct qmp_ufs *qmp, const struct qmp_phy_cfg_tbls *tbls)
+{
+ qmp_ufs_serdes_init(qmp, tbls);
+ qmp_ufs_lanes_init(qmp, tbls);
+ qmp_ufs_pcs_init(qmp, tbls);
+}
+
static void qmp_ufs_init_registers(struct qmp_ufs *qmp, const struct qmp_phy_cfg *cfg)
{
int i;
- qmp_ufs_serdes_init(qmp, &cfg->tbls);
- qmp_ufs_lanes_init(qmp, &cfg->tbls);
- qmp_ufs_pcs_init(qmp, &cfg->tbls);
+ qmp_ufs_init_all(qmp, &cfg->tbls);
i = qmp_ufs_get_gear_overlay(qmp, cfg);
if (i >= 0) {
- qmp_ufs_serdes_init(qmp, &cfg->tbls_hs_overlay[i]);
- qmp_ufs_lanes_init(qmp, &cfg->tbls_hs_overlay[i]);
- qmp_ufs_pcs_init(qmp, &cfg->tbls_hs_overlay[i]);
+ qmp_ufs_init_all(qmp, &cfg->tbls_hs_overlay[i]);
}
- if (qmp->mode == PHY_MODE_UFS_HS_B)
- qmp_ufs_serdes_init(qmp, &cfg->tbls_hs_b);
+ qmp_ufs_init_all(qmp, &cfg->tbls_hs_b);
}
static int qmp_ufs_com_init(struct qmp_ufs *qmp)
@@ -2061,7 +2221,11 @@ static const struct of_device_id qmp_ufs_of_match_table[] = {
}, {
.compatible = "qcom,sm8650-qmp-ufs-phy",
.data = &sm8650_ufsphy_cfg,
+ }, {
+ .compatible = "qcom,sm8750-qmp-ufs-phy",
+ .data = &sm8750_ufsphy_cfg,
},
+
{ },
};
MODULE_DEVICE_TABLE(of, qmp_ufs_of_match_table);
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c
index cf12a6f12134..5e7fcb26744a 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp-usbc.c
@@ -1125,6 +1125,9 @@ static const struct of_device_id qmp_usbc_of_match_table[] = {
.compatible = "qcom,qcm2290-qmp-usb3-phy",
.data = &qcm2290_usb3phy_cfg,
}, {
+ .compatible = "qcom,qcs615-qmp-usb3-phy",
+ .data = &qcm2290_usb3phy_cfg,
+ }, {
.compatible = "qcom,sdm660-qmp-usb3-phy",
.data = &sdm660_usb3phy_cfg,
}, {
diff --git a/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c
new file mode 100644
index 000000000000..c8b2a3818880
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-uniphy-pcie-28lp.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/units.h>
+
+#define RST_ASSERT_DELAY_MIN_US 100
+#define RST_ASSERT_DELAY_MAX_US 150
+#define PIPE_CLK_DELAY_MIN_US 5000
+#define PIPE_CLK_DELAY_MAX_US 5100
+#define CLK_EN_DELAY_MIN_US 30
+#define CLK_EN_DELAY_MAX_US 50
+#define CDR_CTRL_REG_1 0x80
+#define CDR_CTRL_REG_2 0x84
+#define CDR_CTRL_REG_3 0x88
+#define CDR_CTRL_REG_4 0x8c
+#define CDR_CTRL_REG_5 0x90
+#define CDR_CTRL_REG_6 0x94
+#define CDR_CTRL_REG_7 0x98
+#define SSCG_CTRL_REG_1 0x9c
+#define SSCG_CTRL_REG_2 0xa0
+#define SSCG_CTRL_REG_3 0xa4
+#define SSCG_CTRL_REG_4 0xa8
+#define SSCG_CTRL_REG_5 0xac
+#define SSCG_CTRL_REG_6 0xb0
+#define PCS_INTERNAL_CONTROL_2 0x2d8
+
+#define PHY_CFG_PLLCFG 0x220
+#define PHY_CFG_EIOS_DTCT_REG 0x3e4
+#define PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME 0x3e8
+
+enum qcom_uniphy_pcie_type {
+ PHY_TYPE_PCIE = 1,
+ PHY_TYPE_PCIE_GEN2,
+ PHY_TYPE_PCIE_GEN3,
+};
+
+struct qcom_uniphy_pcie_regs {
+ u32 offset;
+ u32 val;
+};
+
+struct qcom_uniphy_pcie_data {
+ int lane_offset; /* offset between the lane register bases */
+ u32 phy_type;
+ const struct qcom_uniphy_pcie_regs *init_seq;
+ u32 init_seq_num;
+ u32 pipe_clk_rate;
+};
+
+struct qcom_uniphy_pcie {
+ struct phy phy;
+ struct device *dev;
+ const struct qcom_uniphy_pcie_data *data;
+ struct clk_bulk_data *clks;
+ int num_clks;
+ struct reset_control *resets;
+ void __iomem *base;
+ int lanes;
+};
+
+#define phy_to_dw_phy(x) container_of((x), struct qca_uni_pcie_phy, phy)
+
+static const struct qcom_uniphy_pcie_regs ipq5332_regs[] = {
+ {
+ .offset = PHY_CFG_PLLCFG,
+ .val = 0x30,
+ }, {
+ .offset = PHY_CFG_EIOS_DTCT_REG,
+ .val = 0x53ef,
+ }, {
+ .offset = PHY_CFG_GEN3_ALIGN_HOLDOFF_TIME,
+ .val = 0xcf,
+ },
+};
+
+static const struct qcom_uniphy_pcie_data ipq5332_data = {
+ .lane_offset = 0x800,
+ .phy_type = PHY_TYPE_PCIE_GEN3,
+ .init_seq = ipq5332_regs,
+ .init_seq_num = ARRAY_SIZE(ipq5332_regs),
+ .pipe_clk_rate = 250 * MEGA,
+};
+
+static void qcom_uniphy_pcie_init(struct qcom_uniphy_pcie *phy)
+{
+ const struct qcom_uniphy_pcie_data *data = phy->data;
+ const struct qcom_uniphy_pcie_regs *init_seq;
+ void __iomem *base = phy->base;
+ int lane, i;
+
+ for (lane = 0; lane < phy->lanes; lane++) {
+ init_seq = data->init_seq;
+
+ for (i = 0; i < data->init_seq_num; i++)
+ writel(init_seq[i].val, base + init_seq[i].offset);
+
+ base += data->lane_offset;
+ }
+}
+
+static int qcom_uniphy_pcie_power_off(struct phy *x)
+{
+ struct qcom_uniphy_pcie *phy = phy_get_drvdata(x);
+
+ clk_bulk_disable_unprepare(phy->num_clks, phy->clks);
+
+ return reset_control_assert(phy->resets);
+}
+
+static int qcom_uniphy_pcie_power_on(struct phy *x)
+{
+ struct qcom_uniphy_pcie *phy = phy_get_drvdata(x);
+ int ret;
+
+ ret = reset_control_assert(phy->resets);
+ if (ret) {
+ dev_err(phy->dev, "reset assert failed (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(RST_ASSERT_DELAY_MIN_US, RST_ASSERT_DELAY_MAX_US);
+
+ ret = reset_control_deassert(phy->resets);
+ if (ret) {
+ dev_err(phy->dev, "reset deassert failed (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(PIPE_CLK_DELAY_MIN_US, PIPE_CLK_DELAY_MAX_US);
+
+ ret = clk_bulk_prepare_enable(phy->num_clks, phy->clks);
+ if (ret) {
+ dev_err(phy->dev, "clk prepare and enable failed %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CLK_EN_DELAY_MIN_US, CLK_EN_DELAY_MAX_US);
+
+ qcom_uniphy_pcie_init(phy);
+
+ return 0;
+}
+
+static inline int qcom_uniphy_pcie_get_resources(struct platform_device *pdev,
+ struct qcom_uniphy_pcie *phy)
+{
+ struct resource *res;
+
+ phy->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(phy->base))
+ return PTR_ERR(phy->base);
+
+ phy->num_clks = devm_clk_bulk_get_all(phy->dev, &phy->clks);
+ if (phy->num_clks < 0)
+ return phy->num_clks;
+
+ phy->resets = devm_reset_control_array_get_exclusive(phy->dev);
+ if (IS_ERR(phy->resets))
+ return PTR_ERR(phy->resets);
+
+ return 0;
+}
+
+/*
+ * Register a fixed rate pipe clock.
+ *
+ * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
+ * controls it. The <s>_pipe_clk coming out of the GCC is requested
+ * by the PHY driver for its operations.
+ * We register the <s>_pipe_clksrc here. The gcc driver takes care
+ * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
+ * Below picture shows this relationship.
+ *
+ * +---------------+
+ * | PHY block |<<---------------------------------------+
+ * | | |
+ * | +-------+ | +-----+ |
+ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
+ * clk | +-------+ | +-----+
+ * +---------------+
+ */
+static inline int phy_pipe_clk_register(struct qcom_uniphy_pcie *phy, int id)
+{
+ const struct qcom_uniphy_pcie_data *data = phy->data;
+ struct clk_hw *hw;
+ char name[64];
+
+ snprintf(name, sizeof(name), "phy%d_pipe_clk_src", id);
+ hw = devm_clk_hw_register_fixed_rate(phy->dev, name, NULL, 0,
+ data->pipe_clk_rate);
+ if (IS_ERR(hw))
+ return dev_err_probe(phy->dev, PTR_ERR(hw),
+ "Unable to register %s\n", name);
+
+ return devm_of_clk_add_hw_provider(phy->dev, of_clk_hw_simple_get, hw);
+}
+
+static const struct of_device_id qcom_uniphy_pcie_id_table[] = {
+ {
+ .compatible = "qcom,ipq5332-uniphy-pcie-phy",
+ .data = &ipq5332_data,
+ }, {
+ /* Sentinel */
+ },
+};
+MODULE_DEVICE_TABLE(of, qcom_uniphy_pcie_id_table);
+
+static const struct phy_ops pcie_ops = {
+ .power_on = qcom_uniphy_pcie_power_on,
+ .power_off = qcom_uniphy_pcie_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int qcom_uniphy_pcie_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct device *dev = &pdev->dev;
+ struct qcom_uniphy_pcie *phy;
+ struct phy *generic_phy;
+ int ret;
+
+ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, phy);
+ phy->dev = &pdev->dev;
+
+ phy->data = of_device_get_match_data(dev);
+ if (!phy->data)
+ return -EINVAL;
+
+ ret = of_property_read_u32(dev_of_node(dev), "num-lanes", &phy->lanes);
+ if (ret)
+ return dev_err_probe(dev, ret, "Couldn't read num-lanes\n");
+
+ ret = qcom_uniphy_pcie_get_resources(pdev, phy);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to get resources: %d\n", ret);
+
+ generic_phy = devm_phy_create(phy->dev, NULL, &pcie_ops);
+ if (IS_ERR(generic_phy))
+ return PTR_ERR(generic_phy);
+
+ phy_set_drvdata(generic_phy, phy);
+
+ ret = phy_pipe_clk_register(phy, generic_phy->id);
+ if (ret)
+ dev_err(&pdev->dev, "failed to register phy pipe clk\n");
+
+ phy_provider = devm_of_phy_provider_register(phy->dev,
+ of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ return 0;
+}
+
+static struct platform_driver qcom_uniphy_pcie_driver = {
+ .probe = qcom_uniphy_pcie_probe,
+ .driver = {
+ .name = "qcom-uniphy-pcie",
+ .of_match_table = qcom_uniphy_pcie_id_table,
+ },
+};
+
+module_platform_driver(qcom_uniphy_pcie_driver);
+
+MODULE_DESCRIPTION("PCIE QCOM UNIPHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index dcb8e1628632..14698571b607 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -83,6 +83,18 @@ config PHY_ROCKCHIP_PCIE
help
Enable this to support the Rockchip PCIe PHY.
+config PHY_ROCKCHIP_SAMSUNG_DCPHY
+ tristate "Rockchip Samsung MIPI DCPHY driver"
+ depends on (ARCH_ROCKCHIP || COMPILE_TEST)
+ select GENERIC_PHY
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Enable this to support the Rockchip MIPI DCPHY with
+ Samsung IP block.
+
+ To compile this driver as a module, choose M here: the module
+ will be called phy-rockchip-samsung-dcphy
+
config PHY_ROCKCHIP_SAMSUNG_HDPTX
tristate "Rockchip Samsung HDMI/eDP Combo PHY driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index 010a824e32ce..117aaffd037d 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBO_PHY) += phy-rockchip-naneng-combphy.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
+obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_DCPHY) += phy-rockchip-samsung-dcphy.o
obj-$(CONFIG_PHY_ROCKCHIP_SAMSUNG_HDPTX) += phy-rockchip-samsung-hdptx.o
obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3) += phy-rockchip-snps-pcie3.o
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 96f3d868a526..b5e6a864deeb 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -440,7 +440,7 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
struct extcon_dev *edev;
int ret;
- if (of_property_read_bool(node, "extcon")) {
+ if (of_property_present(node, "extcon")) {
edev = extcon_get_edev_by_phandle(rphy->dev, 0);
if (IS_ERR(edev))
return dev_err_probe(rphy->dev, PTR_ERR(edev),
@@ -1323,7 +1323,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
goto out;
}
- if (!of_property_read_bool(rphy->dev->of_node, "extcon")) {
+ if (!of_property_present(rphy->dev->of_node, "extcon")) {
/* do initial sync of usb state */
id = property_enabled(rphy->grf, &rport->port_cfg->utmi_id);
extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, !id);
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
index 8c3ce57f8915..ce91fb1d5167 100644
--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -396,6 +396,154 @@ static int rockchip_combphy_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(phy_provider);
}
+static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv)
+{
+ const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+ unsigned long rate;
+ u32 val;
+
+ switch (priv->type) {
+ case PHY_TYPE_PCIE:
+ /* Set SSC downward spread spectrum */
+ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK,
+ PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT,
+ PHYREG32);
+
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true);
+ break;
+ case PHY_TYPE_USB3:
+ /* Set SSC downward spread spectrum */
+ rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK,
+ PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT,
+ PHYREG32);
+
+ /* Enable adaptive CTLE for USB3.0 Rx */
+ rockchip_combphy_updatel(priv, PHYREG15_CTLE_EN,
+ PHYREG15_CTLE_EN, PHYREG15);
+
+ /* Set PLL KVCO fine tuning signals */
+ rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, BIT(3), PHYREG33);
+
+ /* Set PLL LPF R1 to su_trim[10:7]=1001 */
+ writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12);
+
+ /* Set PLL input clock divider 1/2 */
+ val = FIELD_PREP(PHYREG6_PLL_DIV_MASK, PHYREG6_PLL_DIV_2);
+ rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, val, PHYREG6);
+
+ /* Set PLL loop divider */
+ writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18);
+
+ /* Set PLL KVCO to min and set PLL charge pump current to max */
+ writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11);
+
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_usb, true);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false);
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true);
+ break;
+ default:
+ dev_err(priv->dev, "incompatible PHY type\n");
+ return -EINVAL;
+ }
+
+ rate = clk_get_rate(priv->refclk);
+
+ switch (rate) {
+ case REF_CLOCK_24MHz:
+ if (priv->type == PHY_TYPE_USB3) {
+ /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */
+ val = FIELD_PREP(PHYREG15_SSC_CNT_MASK, PHYREG15_SSC_CNT_VALUE);
+ rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK,
+ val, PHYREG15);
+
+ writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16);
+ }
+ break;
+ case REF_CLOCK_25MHz:
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_25m, true);
+ break;
+ case REF_CLOCK_100MHz:
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
+ if (priv->type == PHY_TYPE_PCIE) {
+ /* PLL KVCO tuning fine */
+ val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE);
+ rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK,
+ val, PHYREG33);
+
+ /* Enable controlling random jitter, aka RMJ */
+ writel(0x4, priv->mmio + PHYREG12);
+
+ val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT;
+ rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK,
+ val, PHYREG6);
+
+ writel(0x32, priv->mmio + PHYREG18);
+ writel(0xf0, priv->mmio + PHYREG11);
+ }
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported rate: %lu\n", rate);
+ return -EINVAL;
+ }
+
+ if (priv->ext_refclk) {
+ rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true);
+ if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) {
+ val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT;
+ val |= PHYREG13_CKRCV_AMP0;
+ rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13);
+
+ val = readl(priv->mmio + PHYREG14);
+ val |= PHYREG14_CKRCV_AMP1;
+ writel(val, priv->mmio + PHYREG14);
+ }
+ }
+
+ if (priv->enable_ssc) {
+ val = readl(priv->mmio + PHYREG8);
+ val |= PHYREG8_SSC_EN;
+ writel(val, priv->mmio + PHYREG8);
+ }
+
+ return 0;
+}
+
+static const struct rockchip_combphy_grfcfg rk3562_combphy_grfcfgs = {
+ /* pipe-phy-grf */
+ .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 },
+ .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 },
+ .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 },
+ .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 },
+ .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 },
+ .pipe_clk_25m = { 0x0004, 14, 13, 0x00, 0x01 },
+ .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 },
+ .pipe_phymode_sel = { 0x0008, 1, 1, 0x00, 0x01 },
+ .pipe_rate_sel = { 0x0008, 2, 2, 0x00, 0x01 },
+ .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 },
+ .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 },
+ .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 },
+ .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 },
+ .pipe_sel_usb = { 0x000c, 14, 13, 0x00, 0x01 },
+ .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 },
+ .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x1000 },
+ .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x0000 },
+ .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x0101 },
+ .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 },
+};
+
+static const struct rockchip_combphy_cfg rk3562_combphy_cfgs = {
+ .num_phys = 1,
+ .phy_ids = {
+ 0xff750000
+ },
+ .grfcfg = &rk3562_combphy_grfcfgs,
+ .combphy_cfg = rk3562_combphy_cfg,
+};
+
static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv)
{
const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
@@ -1050,6 +1198,10 @@ static const struct rockchip_combphy_cfg rk3588_combphy_cfgs = {
static const struct of_device_id rockchip_combphy_of_match[] = {
{
+ .compatible = "rockchip,rk3562-naneng-combphy",
+ .data = &rk3562_combphy_cfgs,
+ },
+ {
.compatible = "rockchip,rk3568-naneng-combphy",
.data = &rk3568_combphy_cfgs,
},
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
new file mode 100644
index 000000000000..08c78c1bafc9
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -0,0 +1,1719 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Rockchip Electronics Co.Ltd
+ * Author:
+ * Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define FIELD_PREP_HIWORD(_mask, _val) \
+ ( \
+ FIELD_PREP((_mask), (_val)) | \
+ ((_mask) << 16) \
+ )
+
+#define BIAS_CON0 0x0000
+#define I_RES_CNTL_MASK GENMASK(6, 4)
+#define I_RES_CNTL(x) FIELD_PREP(I_RES_CNTL_MASK, x)
+#define I_RES_059_2UA I_RES_CNTL(0)
+#define I_RES_100_2UA I_RES_CNTL(1)
+#define I_RES_094_2UA I_RES_CNTL(2)
+#define I_RES_113_8UA I_RES_CNTL(3)
+#define I_RES_089_7UA I_RES_CNTL(4)
+#define I_RES_111_8UA I_RES_CNTL(5)
+#define I_RES_108_2UA I_RES_CNTL(6)
+#define I_RES_120_8UA I_RES_CNTL(7)
+#define I_DEV_SEL_MASK GENMASK(1, 0)
+#define I_DEV_SEL(x) FIELD_PREP(I_DEV_SEL_MASK, x)
+#define I_DEV_DIV_6 I_DEV_SEL(0)
+#define I_DEV_DIV_12 I_DEV_SEL(1)
+#define I_DEV_DIV_20 I_DEV_SEL(2)
+#define I_DEV_DIV_40 I_DEV_SEL(3)
+
+#define BIAS_CON1 0x0004
+#define I_VBG_SEL_MASK GENMASK(9, 8)
+#define I_VBG_SEL(x) FIELD_PREP(I_VBG_SEL_MASK, x)
+#define I_VBG_SEL_780MV I_VBG_SEL(0)
+#define I_VBG_SEL_820MV I_VBG_SEL(1)
+#define I_VBG_SEL_860MV I_VBG_SEL(2)
+#define I_VBG_SEL_900MV I_VBG_SEL(3)
+#define I_BGR_VREF_SEL_MASK GENMASK(5, 4)
+#define I_BGR_VREF_SEL(x) FIELD_PREP(I_BGR_VREF_SEL_MASK, x)
+#define I_BGR_VREF_810MV I_BGR_VREF_SEL(0)
+#define I_BGR_VREF_820MV I_BGR_VREF_SEL(1)
+#define I_BGR_VREF_830MV I_BGR_VREF_SEL(2)
+#define I_BGR_VREF_840MV I_BGR_VREF_SEL(3)
+#define I_LADDER_SEL_MASK GENMASK(2, 0)
+#define I_LADDER_SEL(x) FIELD_PREP(I_LADDER_SEL_MASK, x)
+#define I_LADDER_1_00V I_LADDER_SEL(0)
+#define I_LADDER_0_96V I_LADDER_SEL(1)
+#define I_LADDER_0_92V I_LADDER_SEL(2)
+#define I_LADDER_0_88V I_LADDER_SEL(3)
+#define I_LADDER_0_84V I_LADDER_SEL(4)
+#define I_LADDER_0_80V I_LADDER_SEL(5)
+#define I_LADDER_0_76V I_LADDER_SEL(6)
+#define I_LADDER_0_72V I_LADDER_SEL(7)
+
+/*
+ * Voltage corrections around reference voltages
+ * The selection between the 400-based or 200-based values for REG_400M
+ * is done by the hw depending on I_MUX below being 400MV or 200MV.
+ */
+#define BIAS_CON2 0x0008
+#define REG_325M_MASK GENMASK(14, 12)
+#define REG_325M(x) FIELD_PREP(REG_325M_MASK, x)
+#define REG_325M_295MV REG_325M(0)
+#define REG_325M_305MV REG_325M(1)
+#define REG_325M_315MV REG_325M(2)
+#define REG_325M_325MV REG_325M(3)
+#define REG_325M_335MV REG_325M(4)
+#define REG_325M_345MV REG_325M(5)
+#define REG_325M_355MV REG_325M(6)
+#define REG_325M_365MV REG_325M(7)
+#define REG_LP_400M_MASK GENMASK(10, 8)
+#define REG_LP_400M(x) FIELD_PREP(REG_LP_400M_MASK, x)
+#define REG_LP_400M_380MV REG_LP_400M(0)
+#define REG_LP_400M_390MV REG_LP_400M(1)
+#define REG_LP_400M_400MV REG_LP_400M(2)
+#define REG_LP_400M_410MV REG_LP_400M(3)
+#define REG_LP_400M_420MV REG_LP_400M(4)
+#define REG_LP_400M_430MV REG_LP_400M(5)
+#define REG_LP_400M_440MV REG_LP_400M(6)
+#define REG_LP_400M_450MV REG_LP_400M(7)
+#define REG_400M_MASK GENMASK(6, 4)
+#define REG_400M(x) FIELD_PREP(REG_400M_MASK, x)
+#define REG_400M_380MV REG_400M(0)
+#define REG_400M_390MV REG_400M(1)
+#define REG_400M_400MV REG_400M(2)
+#define REG_400M_410MV REG_400M(3)
+#define REG_400M_420MV REG_400M(4)
+#define REG_400M_430MV REG_400M(5)
+#define REG_400M_440MV REG_400M(6)
+#define REG_400M_450MV REG_400M(7)
+#define REG_400M_230MV REG_400M(0)
+#define REG_400M_220MV REG_400M(1)
+#define REG_400M_210MV REG_400M(2)
+#define REG_400M_200MV REG_400M(3)
+#define REG_400M_190MV REG_400M(4)
+#define REG_400M_180MV REG_400M(5)
+#define REG_400M_170MV REG_400M(6)
+#define REG_400M_160MV REG_400M(7)
+#define REG_645M_MASK GENMASK(2, 0)
+#define REG_645M(x) FIELD_PREP(REG_645M_MASK, x)
+#define REG_645M_605MV REG_645M(0)
+#define REG_645M_625MV REG_645M(1)
+#define REG_645M_635MV REG_645M(2)
+#define REG_645M_645MV REG_645M(3)
+#define REG_645M_655MV REG_645M(4)
+#define REG_645M_665MV REG_645M(5)
+#define REG_645M_685MV REG_645M(6)
+#define REG_645M_725MV REG_645M(7)
+
+#define BIAS_CON4 0x0010
+#define I_MUX_SEL_MASK GENMASK(6, 5)
+#define I_MUX_SEL(x) FIELD_PREP(I_MUX_SEL_MASK, x)
+#define I_MUX_400MV I_MUX_SEL(0)
+#define I_MUX_200MV I_MUX_SEL(1)
+#define I_MUX_530MV I_MUX_SEL(2)
+
+#define PLL_CON0 0x0100
+#define PLL_EN BIT(12)
+#define S_MASK GENMASK(10, 8)
+#define S(x) FIELD_PREP(S_MASK, x)
+#define P_MASK GENMASK(5, 0)
+#define P(x) FIELD_PREP(P_MASK, x)
+#define PLL_CON1 0x0104
+#define PLL_CON2 0x0108
+#define M_MASK GENMASK(9, 0)
+#define M(x) FIELD_PREP(M_MASK, x)
+#define PLL_CON3 0x010c
+#define MRR_MASK GENMASK(13, 8)
+#define MRR(x) FIELD_PREP(MRR_MASK, x)
+#define MFR_MASK GENMASK(7, 0)
+#define MFR(x) FIELD_PREP(MFR_MASK, x)
+#define PLL_CON4 0x0110
+#define SSCG_EN BIT(11)
+#define PLL_CON5 0x0114
+#define RESET_N_SEL BIT(10)
+#define PLL_ENABLE_SEL BIT(8)
+#define PLL_CON6 0x0118
+#define PLL_CON7 0x011c
+#define PLL_LOCK_CNT(x) FIELD_PREP(GENMASK(15, 0), x)
+#define PLL_CON8 0x0120
+#define PLL_STB_CNT(x) FIELD_PREP(GENMASK(15, 0), x)
+#define PLL_STAT0 0x0140
+#define PLL_LOCK BIT(0)
+
+#define DPHY_MC_GNR_CON0 0x0300
+#define PHY_READY BIT(1)
+#define PHY_ENABLE BIT(0)
+#define DPHY_MC_GNR_CON1 0x0304
+#define T_PHY_READY(x) FIELD_PREP(GENMASK(15, 0), x)
+#define DPHY_MC_ANA_CON0 0x0308
+#define EDGE_CON(x) FIELD_PREP(GENMASK(14, 12), x)
+#define EDGE_CON_DIR(x) FIELD_PREP(BIT(9), x)
+#define EDGE_CON_EN BIT(8)
+#define RES_UP(x) FIELD_PREP(GENMASK(7, 4), x)
+#define RES_DN(x) FIELD_PREP(GENMASK(3, 0), x)
+#define DPHY_MC_ANA_CON1 0x030c
+#define DPHY_MC_ANA_CON2 0x0310
+#define HS_VREG_AMP_ICON(x) FIELD_PREP(GENMASK(1, 0), x)
+#define DPHY_MC_TIME_CON0 0x0330
+#define HSTX_CLK_SEL BIT(12)
+#define T_LPX(x) FIELD_PREP(GENMASK(11, 4), x)
+#define DPHY_MC_TIME_CON1 0x0334
+#define T_CLK_ZERO(x) FIELD_PREP(GENMASK(15, 8), x)
+#define T_CLK_PREPARE(x) FIELD_PREP(GENMASK(7, 0), x)
+#define DPHY_MC_TIME_CON2 0x0338
+#define T_HS_EXIT(x) FIELD_PREP(GENMASK(15, 8), x)
+#define T_CLK_TRAIL(x) FIELD_PREP(GENMASK(7, 0), x)
+#define DPHY_MC_TIME_CON3 0x033c
+#define T_CLK_POST(x) FIELD_PREP(GENMASK(7, 0), x)
+#define DPHY_MC_TIME_CON4 0x0340
+#define T_ULPS_EXIT(x) FIELD_PREP(GENMASK(9, 0), x)
+#define DPHY_MC_DESKEW_CON0 0x0350
+#define SKEW_CAL_RUN_TIME(x) FIELD_PREP(GENMASK(15, 12), x)
+
+#define SKEW_CAL_INIT_RUN_TIME(x) FIELD_PREP(GENMASK(11, 8), x)
+#define SKEW_CAL_INIT_WAIT_TIME(x) FIELD_PREP(GENMASK(7, 4), x)
+#define SKEW_CAL_EN BIT(0)
+
+#define COMBO_MD0_GNR_CON0 0x0400
+#define COMBO_MD0_GNR_CON1 0x0404
+#define COMBO_MD0_ANA_CON0 0x0408
+#define COMBO_MD0_ANA_CON1 0x040c
+#define COMBO_MD0_ANA_CON2 0x0410
+
+#define COMBO_MD0_TIME_CON0 0x0430
+#define COMBO_MD0_TIME_CON1 0x0434
+#define COMBO_MD0_TIME_CON2 0x0438
+#define COMBO_MD0_TIME_CON3 0x043c
+#define COMBO_MD0_TIME_CON4 0x0440
+#define COMBO_MD0_DATA_CON0 0x0444
+
+#define COMBO_MD1_GNR_CON0 0x0500
+#define COMBO_MD1_GNR_CON1 0x0504
+#define COMBO_MD1_ANA_CON0 0x0508
+#define COMBO_MD1_ANA_CON1 0x050c
+#define COMBO_MD1_ANA_CON2 0x0510
+#define COMBO_MD1_TIME_CON0 0x0530
+#define COMBO_MD1_TIME_CON1 0x0534
+#define COMBO_MD1_TIME_CON2 0x0538
+#define COMBO_MD1_TIME_CON3 0x053c
+#define COMBO_MD1_TIME_CON4 0x0540
+#define COMBO_MD1_DATA_CON0 0x0544
+
+#define COMBO_MD2_GNR_CON0 0x0600
+#define COMBO_MD2_GNR_CON1 0x0604
+#define COMBO_MD2_ANA_CON0 0X0608
+#define COMBO_MD2_ANA_CON1 0X060c
+#define COMBO_MD2_ANA_CON2 0X0610
+#define COMBO_MD2_TIME_CON0 0x0630
+#define COMBO_MD2_TIME_CON1 0x0634
+#define COMBO_MD2_TIME_CON2 0x0638
+#define COMBO_MD2_TIME_CON3 0x063c
+#define COMBO_MD2_TIME_CON4 0x0640
+#define COMBO_MD2_DATA_CON0 0x0644
+
+#define DPHY_MD3_GNR_CON0 0x0700
+#define DPHY_MD3_GNR_CON1 0x0704
+#define DPHY_MD3_ANA_CON0 0X0708
+#define DPHY_MD3_ANA_CON1 0X070c
+#define DPHY_MD3_ANA_CON2 0X0710
+#define DPHY_MD3_TIME_CON0 0x0730
+#define DPHY_MD3_TIME_CON1 0x0734
+#define DPHY_MD3_TIME_CON2 0x0738
+#define DPHY_MD3_TIME_CON3 0x073c
+#define DPHY_MD3_TIME_CON4 0x0740
+#define DPHY_MD3_DATA_CON0 0x0744
+
+#define T_LP_EXIT_SKEW(x) FIELD_PREP(GENMASK(3, 2), x)
+#define T_LP_ENTRY_SKEW(x) FIELD_PREP(GENMASK(1, 0), x)
+#define T_HS_ZERO(x) FIELD_PREP(GENMASK(15, 8), x)
+#define T_HS_PREPARE(x) FIELD_PREP(GENMASK(7, 0), x)
+#define T_HS_EXIT(x) FIELD_PREP(GENMASK(15, 8), x)
+#define T_HS_TRAIL(x) FIELD_PREP(GENMASK(7, 0), x)
+#define T_TA_GET(x) FIELD_PREP(GENMASK(7, 4), x)
+#define T_TA_GO(x) FIELD_PREP(GENMASK(3, 0), x)
+
+/* MIPI_CDPHY_GRF registers */
+#define MIPI_DCPHY_GRF_CON0 0x0000
+#define S_CPHY_MODE FIELD_PREP_HIWORD(BIT(3), 1)
+#define M_CPHY_MODE FIELD_PREP_HIWORD(BIT(0), 1)
+
+enum hs_drv_res_ohm {
+ STRENGTH_30_OHM = 0x8,
+ STRENGTH_31_2_OHM,
+ STRENGTH_32_5_OHM,
+ STRENGTH_34_OHM,
+ STRENGTH_35_5_OHM,
+ STRENGTH_37_OHM,
+ STRENGTH_39_OHM,
+ STRENGTH_41_OHM,
+ STRENGTH_43_OHM = 0x0,
+ STRENGTH_46_OHM,
+ STRENGTH_49_OHM,
+ STRENGTH_52_OHM,
+ STRENGTH_56_OHM,
+ STRENGTH_60_OHM,
+ STRENGTH_66_OHM,
+ STRENGTH_73_OHM,
+};
+
+struct hs_drv_res_cfg {
+ enum hs_drv_res_ohm clk_hs_drv_up_ohm;
+ enum hs_drv_res_ohm clk_hs_drv_down_ohm;
+ enum hs_drv_res_ohm data_hs_drv_up_ohm;
+ enum hs_drv_res_ohm data_hs_drv_down_ohm;
+};
+
+struct samsung_mipi_dcphy_plat_data {
+ const struct hs_drv_res_cfg *dphy_hs_drv_res_cfg;
+ u32 dphy_tx_max_lane_kbps;
+};
+
+struct samsung_mipi_dcphy {
+ struct device *dev;
+ struct clk *ref_clk;
+ struct clk *pclk;
+ struct regmap *regmap;
+ struct regmap *grf_regmap;
+ struct reset_control *m_phy_rst;
+ struct reset_control *s_phy_rst;
+ struct reset_control *apb_rst;
+ struct reset_control *grf_apb_rst;
+ unsigned int lanes;
+ struct phy *phy;
+ u8 type;
+
+ const struct samsung_mipi_dcphy_plat_data *pdata;
+ struct {
+ unsigned long long rate;
+ u8 prediv;
+ u16 fbdiv;
+ long dsm;
+ u8 scaler;
+
+ bool ssc_en;
+ u8 mfr;
+ u8 mrr;
+ } pll;
+};
+
+struct samsung_mipi_dphy_timing {
+ unsigned int max_lane_mbps;
+ u8 clk_prepare;
+ u8 clk_zero;
+ u8 clk_post;
+ u8 clk_trail_eot;
+ u8 hs_prepare;
+ u8 hs_zero;
+ u8 hs_trail_eot;
+ u8 lpx;
+ u8 hs_exit;
+ u8 hs_settle;
+};
+
+/*
+ * Timing values taken from rk3588 vendor kernel.
+ * Not documented in hw documentation.
+ */
+static const
+struct samsung_mipi_dphy_timing samsung_mipi_dphy_timing_table[] = {
+ {6500, 32, 117, 31, 28, 30, 56, 27, 24, 44, 37},
+ {6490, 32, 116, 31, 28, 30, 56, 27, 24, 44, 37},
+ {6480, 32, 116, 31, 28, 30, 56, 27, 24, 44, 37},
+ {6470, 32, 116, 31, 28, 30, 56, 27, 24, 44, 37},
+ {6460, 32, 116, 31, 28, 30, 56, 27, 24, 44, 37},
+ {6450, 32, 115, 31, 28, 30, 56, 27, 24, 44, 37},
+ {6440, 32, 115, 31, 28, 30, 56, 27, 24, 44, 37},
+ {6430, 31, 116, 31, 28, 30, 55, 27, 24, 44, 37},
+ {6420, 31, 116, 31, 28, 30, 55, 27, 24, 44, 37},
+ {6410, 31, 116, 31, 27, 30, 55, 27, 24, 44, 37},
+ {6400, 31, 115, 30, 27, 30, 55, 27, 23, 43, 36},
+ {6390, 31, 115, 30, 27, 30, 55, 27, 23, 43, 36},
+ {6380, 31, 115, 30, 27, 30, 55, 27, 23, 43, 36},
+ {6370, 31, 115, 30, 27, 30, 55, 26, 23, 43, 36},
+ {6360, 31, 114, 30, 27, 30, 54, 26, 23, 43, 36},
+ {6350, 31, 114, 30, 27, 30, 54, 26, 23, 43, 36},
+ {6340, 31, 114, 30, 27, 30, 54, 26, 23, 43, 36},
+ {6330, 31, 114, 30, 27, 30, 54, 26, 23, 43, 36},
+ {6320, 31, 113, 30, 27, 30, 54, 26, 23, 43, 36},
+ {6310, 31, 113, 30, 27, 30, 54, 26, 23, 43, 36},
+ {6300, 31, 113, 30, 27, 30, 54, 26, 23, 43, 36},
+ {6290, 31, 113, 30, 27, 29, 54, 26, 23, 43, 36},
+ {6280, 31, 112, 30, 27, 29, 54, 26, 23, 43, 36},
+ {6270, 31, 112, 30, 27, 29, 54, 26, 23, 43, 36},
+ {6260, 31, 112, 30, 27, 29, 54, 26, 23, 43, 36},
+ {6250, 31, 112, 30, 27, 29, 54, 26, 23, 42, 36},
+ {6240, 30, 113, 30, 27, 29, 54, 26, 23, 42, 36},
+ {6230, 30, 112, 30, 27, 29, 54, 26, 23, 42, 35},
+ {6220, 30, 112, 30, 27, 29, 53, 26, 23, 42, 35},
+ {6210, 30, 112, 30, 27, 29, 53, 26, 23, 42, 35},
+ {6200, 30, 112, 29, 27, 29, 53, 26, 23, 42, 35},
+ {6190, 30, 111, 29, 27, 29, 53, 26, 23, 42, 35},
+ {6180, 30, 111, 29, 27, 29, 53, 26, 23, 42, 35},
+ {6170, 30, 111, 29, 26, 29, 53, 26, 23, 42, 35},
+ {6160, 30, 111, 29, 26, 29, 53, 26, 23, 42, 35},
+ {6150, 30, 110, 29, 26, 29, 53, 26, 23, 42, 35},
+ {6140, 30, 110, 29, 26, 29, 52, 26, 23, 42, 35},
+ {6130, 30, 110, 29, 26, 29, 52, 25, 22, 42, 35},
+ {6120, 30, 110, 29, 26, 29, 52, 25, 22, 42, 35},
+ {6110, 30, 110, 29, 26, 29, 52, 25, 22, 42, 35},
+ {6100, 30, 109, 29, 26, 29, 52, 25, 22, 41, 35},
+ {6090, 30, 109, 29, 26, 29, 52, 25, 22, 41, 35},
+ {6080, 30, 109, 29, 26, 28, 53, 25, 22, 41, 35},
+ {6070, 30, 109, 29, 26, 28, 52, 25, 22, 41, 34},
+ {6060, 30, 108, 29, 26, 28, 52, 25, 22, 41, 34},
+ {6050, 30, 108, 29, 26, 28, 52, 25, 22, 41, 34},
+ {6040, 29, 109, 29, 26, 28, 52, 25, 22, 41, 34},
+ {6030, 29, 109, 29, 26, 28, 52, 25, 22, 41, 34},
+ {6020, 29, 108, 29, 26, 28, 52, 25, 22, 41, 34},
+ {6010, 29, 108, 29, 26, 28, 52, 25, 22, 41, 34},
+ {6000, 29, 108, 28, 26, 28, 51, 25, 22, 41, 34},
+ {5990, 29, 108, 28, 26, 28, 51, 25, 22, 41, 34},
+ {5980, 29, 107, 28, 26, 28, 51, 25, 22, 41, 34},
+ {5970, 29, 107, 28, 26, 28, 51, 25, 22, 41, 34},
+ {5960, 29, 107, 28, 26, 28, 51, 25, 22, 40, 34},
+ {5950, 29, 107, 28, 26, 28, 51, 25, 22, 40, 34},
+ {5940, 29, 107, 28, 25, 28, 51, 25, 22, 40, 34},
+ {5930, 29, 106, 28, 25, 28, 50, 25, 22, 40, 34},
+ {5920, 29, 106, 28, 25, 28, 50, 25, 22, 40, 34},
+ {5910, 29, 106, 28, 25, 28, 50, 25, 22, 40, 34},
+ {5900, 29, 106, 28, 25, 28, 50, 24, 22, 40, 33},
+ {5890, 29, 105, 28, 25, 28, 50, 24, 22, 40, 33},
+ {5880, 29, 105, 28, 25, 28, 50, 24, 22, 40, 33},
+ {5870, 29, 105, 28, 25, 27, 51, 24, 22, 40, 33},
+ {5860, 29, 105, 28, 25, 27, 51, 24, 21, 40, 33},
+ {5850, 29, 104, 28, 25, 27, 50, 24, 21, 40, 33},
+ {5840, 28, 105, 28, 25, 27, 50, 24, 21, 40, 33},
+ {5830, 28, 105, 28, 25, 27, 50, 24, 21, 40, 33},
+ {5820, 28, 105, 28, 25, 27, 50, 24, 21, 40, 33},
+ {5810, 28, 104, 28, 25, 27, 50, 24, 21, 39, 33},
+ {5800, 28, 104, 27, 25, 27, 50, 24, 21, 39, 33},
+ {5790, 28, 104, 27, 25, 27, 50, 24, 21, 39, 33},
+ {5780, 28, 104, 27, 25, 27, 49, 24, 21, 39, 33},
+ {5770, 28, 104, 27, 25, 27, 49, 24, 21, 39, 33},
+ {5760, 28, 103, 27, 25, 27, 49, 24, 21, 39, 33},
+ {5750, 28, 103, 27, 25, 27, 49, 24, 21, 39, 33},
+ {5740, 28, 103, 27, 25, 27, 49, 24, 21, 39, 33},
+ {5730, 28, 103, 27, 25, 27, 49, 24, 21, 39, 32},
+ {5720, 28, 102, 27, 25, 27, 49, 24, 21, 39, 32},
+ {5710, 28, 102, 27, 25, 27, 48, 24, 21, 39, 32},
+ {5700, 28, 102, 27, 24, 27, 48, 24, 21, 39, 32},
+ {5690, 28, 102, 27, 24, 27, 48, 24, 21, 39, 32},
+ {5680, 28, 101, 27, 24, 27, 48, 24, 21, 39, 32},
+ {5670, 28, 101, 27, 24, 27, 48, 23, 21, 38, 32},
+ {5660, 28, 101, 27, 24, 26, 49, 23, 21, 38, 32},
+ {5650, 28, 101, 27, 24, 26, 49, 23, 21, 38, 32},
+ {5640, 27, 101, 27, 24, 26, 48, 23, 21, 38, 32},
+ {5630, 27, 101, 27, 24, 26, 48, 23, 21, 38, 32},
+ {5620, 27, 101, 27, 24, 26, 48, 23, 21, 38, 32},
+ {5610, 27, 101, 27, 24, 26, 48, 23, 21, 38, 32},
+ {5600, 27, 101, 26, 24, 26, 48, 23, 20, 38, 32},
+ {5590, 27, 100, 26, 24, 26, 48, 23, 20, 38, 32},
+ {5580, 27, 100, 26, 24, 26, 48, 23, 20, 38, 32},
+ {5570, 27, 100, 26, 24, 26, 48, 23, 20, 38, 31},
+ {5560, 27, 100, 26, 24, 26, 47, 23, 20, 38, 31},
+ {5550, 27, 99, 26, 24, 26, 47, 23, 20, 38, 31},
+ {5540, 27, 99, 26, 24, 26, 47, 23, 20, 38, 31},
+ {5530, 27, 99, 26, 24, 26, 47, 23, 20, 38, 31},
+ {5520, 27, 99, 26, 24, 26, 47, 23, 20, 37, 31},
+ {5510, 27, 98, 26, 24, 26, 47, 23, 20, 37, 31},
+ {5500, 27, 98, 26, 24, 26, 47, 23, 20, 37, 31},
+ {5490, 27, 98, 26, 24, 26, 46, 23, 20, 37, 31},
+ {5480, 27, 98, 26, 24, 26, 46, 23, 20, 37, 31},
+ {5470, 27, 97, 26, 23, 26, 46, 23, 20, 37, 31},
+ {5460, 27, 97, 26, 23, 26, 46, 23, 20, 37, 31},
+ {5450, 27, 97, 26, 23, 25, 47, 23, 20, 37, 31},
+ {5440, 26, 98, 26, 23, 25, 47, 23, 20, 37, 31},
+ {5430, 26, 98, 26, 23, 25, 47, 22, 20, 37, 31},
+ {5420, 26, 97, 26, 23, 25, 46, 22, 20, 37, 31},
+ {5410, 26, 97, 26, 23, 25, 46, 22, 20, 37, 31},
+ {5400, 26, 97, 25, 23, 25, 46, 22, 20, 37, 30},
+ {5390, 26, 97, 25, 23, 25, 46, 22, 20, 37, 30},
+ {5380, 26, 96, 25, 23, 25, 46, 22, 20, 36, 30},
+ {5370, 26, 96, 25, 23, 25, 46, 22, 20, 36, 30},
+ {5360, 26, 96, 25, 23, 25, 46, 22, 20, 36, 30},
+ {5350, 26, 96, 25, 23, 25, 46, 22, 20, 36, 30},
+ {5340, 26, 95, 25, 23, 25, 45, 22, 20, 36, 30},
+ {5330, 26, 95, 25, 23, 25, 45, 22, 19, 36, 30},
+ {5320, 26, 95, 25, 23, 25, 45, 22, 19, 36, 30},
+ {5310, 26, 95, 25, 23, 25, 45, 22, 19, 36, 30},
+ {5300, 26, 95, 25, 23, 25, 45, 22, 19, 36, 30},
+ {5290, 26, 94, 25, 23, 25, 45, 22, 19, 36, 30},
+ {5280, 26, 94, 25, 23, 25, 45, 22, 19, 36, 30},
+ {5270, 26, 94, 25, 23, 25, 44, 22, 19, 36, 30},
+ {5260, 26, 94, 25, 23, 25, 44, 22, 19, 36, 30},
+ {5250, 25, 94, 25, 23, 24, 45, 22, 19, 36, 30},
+ {5240, 25, 94, 25, 23, 24, 45, 22, 19, 36, 29},
+ {5230, 25, 94, 25, 22, 24, 45, 22, 19, 35, 29},
+ {5220, 25, 94, 25, 22, 24, 45, 22, 19, 35, 29},
+ {5210, 25, 93, 25, 22, 24, 45, 22, 19, 35, 29},
+ {5200, 25, 93, 24, 22, 24, 44, 21, 19, 35, 29},
+ {5190, 25, 93, 24, 22, 24, 44, 21, 19, 35, 29},
+ {5180, 25, 93, 24, 22, 24, 44, 21, 19, 35, 29},
+ {5170, 25, 92, 24, 22, 24, 44, 21, 19, 35, 29},
+ {5160, 25, 92, 24, 22, 24, 44, 21, 19, 35, 29},
+ {5150, 25, 92, 24, 22, 24, 44, 21, 19, 35, 29},
+ {5140, 25, 92, 24, 22, 24, 44, 21, 19, 35, 29},
+ {5130, 25, 92, 24, 22, 24, 43, 21, 19, 35, 29},
+ {5120, 25, 91, 24, 22, 24, 43, 21, 19, 35, 29},
+ {5110, 25, 91, 24, 22, 24, 43, 21, 19, 35, 29},
+ {5100, 25, 91, 24, 22, 24, 43, 21, 19, 35, 29},
+ {5090, 25, 91, 24, 22, 24, 43, 21, 19, 34, 29},
+ {5080, 25, 90, 24, 22, 24, 43, 21, 19, 34, 29},
+ {5070, 25, 90, 24, 22, 24, 43, 21, 19, 34, 28},
+ {5060, 25, 90, 24, 22, 24, 43, 21, 18, 34, 28},
+ {5050, 24, 91, 24, 22, 24, 42, 21, 18, 34, 28},
+ {5040, 24, 90, 24, 22, 23, 43, 21, 18, 34, 28},
+ {5030, 24, 90, 24, 22, 23, 43, 21, 18, 34, 28},
+ {5020, 24, 90, 24, 22, 23, 43, 21, 18, 34, 28},
+ {5010, 24, 90, 24, 22, 23, 43, 21, 18, 34, 28},
+ {5000, 24, 89, 23, 21, 23, 43, 21, 18, 34, 28},
+ {4990, 24, 89, 23, 21, 23, 43, 21, 18, 34, 28},
+ {4980, 24, 89, 23, 21, 23, 42, 21, 18, 34, 28},
+ {4970, 24, 89, 23, 21, 23, 42, 21, 18, 34, 28},
+ {4960, 24, 89, 23, 21, 23, 42, 20, 18, 34, 28},
+ {4950, 24, 88, 23, 21, 23, 42, 20, 18, 34, 28},
+ {4940, 24, 88, 23, 21, 23, 42, 20, 18, 33, 28},
+ {4930, 24, 88, 23, 21, 23, 42, 20, 18, 33, 28},
+ {4920, 24, 88, 23, 21, 23, 42, 20, 18, 33, 28},
+ {4910, 24, 87, 23, 21, 23, 41, 20, 18, 33, 28},
+ {4900, 24, 87, 23, 21, 23, 41, 20, 18, 33, 27},
+ {4890, 24, 87, 23, 21, 23, 41, 20, 18, 33, 27},
+ {4880, 24, 87, 23, 21, 23, 41, 20, 18, 33, 27},
+ {4870, 24, 86, 23, 21, 23, 41, 20, 18, 33, 27},
+ {4860, 24, 86, 23, 21, 23, 41, 20, 18, 33, 27},
+ {4850, 23, 87, 23, 21, 23, 41, 20, 18, 33, 27},
+ {4840, 23, 87, 23, 21, 23, 40, 20, 18, 33, 27},
+ {4830, 23, 86, 23, 21, 22, 41, 20, 18, 33, 27},
+ {4820, 23, 86, 23, 21, 22, 41, 20, 18, 33, 27},
+ {4810, 23, 86, 23, 21, 22, 41, 20, 18, 33, 27},
+ {4800, 23, 86, 22, 21, 22, 41, 20, 17, 32, 27},
+ {4790, 23, 86, 22, 21, 22, 41, 20, 17, 32, 27},
+ {4780, 23, 85, 22, 21, 22, 41, 20, 17, 32, 27},
+ {4770, 23, 85, 22, 21, 22, 41, 20, 17, 32, 27},
+ {4760, 23, 85, 22, 20, 22, 40, 20, 17, 32, 27},
+ {4750, 23, 85, 22, 20, 22, 40, 20, 17, 32, 27},
+ {4740, 23, 84, 22, 20, 22, 40, 20, 17, 32, 26},
+ {4730, 23, 84, 22, 20, 22, 40, 19, 17, 32, 26},
+ {4720, 23, 84, 22, 20, 22, 40, 19, 17, 32, 26},
+ {4710, 23, 84, 22, 20, 22, 40, 19, 17, 32, 26},
+ {4700, 23, 83, 22, 20, 22, 40, 19, 17, 32, 26},
+ {4690, 23, 83, 22, 20, 22, 39, 19, 17, 32, 26},
+ {4680, 23, 83, 22, 20, 22, 39, 19, 17, 32, 26},
+ {4670, 23, 83, 22, 20, 22, 39, 19, 17, 32, 26},
+ {4660, 23, 82, 22, 20, 22, 39, 19, 17, 32, 26},
+ {4650, 22, 83, 22, 20, 22, 39, 19, 17, 31, 26},
+ {4640, 22, 83, 22, 20, 22, 39, 19, 17, 31, 26},
+ {4630, 22, 83, 22, 20, 22, 39, 19, 17, 31, 26},
+ {4620, 22, 83, 22, 20, 21, 39, 19, 17, 31, 26},
+ {4610, 22, 82, 22, 20, 21, 39, 19, 17, 31, 26},
+ {4600, 22, 82, 21, 20, 21, 39, 19, 17, 31, 26},
+ {4590, 22, 82, 21, 20, 21, 39, 19, 17, 31, 26},
+ {4580, 22, 82, 21, 20, 21, 39, 19, 17, 31, 26},
+ {4570, 22, 81, 21, 20, 21, 39, 19, 17, 31, 25},
+ {4560, 22, 81, 21, 20, 21, 39, 19, 17, 31, 25},
+ {4550, 22, 81, 21, 20, 21, 38, 19, 17, 31, 25},
+ {4540, 22, 81, 21, 20, 21, 38, 19, 17, 31, 25},
+ {4530, 22, 80, 21, 19, 21, 38, 19, 16, 31, 25},
+ {4520, 22, 80, 21, 19, 21, 38, 19, 16, 31, 25},
+ {4510, 22, 80, 21, 19, 21, 38, 19, 16, 31, 25},
+ {4500, 22, 80, 21, 19, 21, 38, 19, 16, 30, 25},
+ {4490, 22, 80, 21, 19, 21, 38, 18, 16, 30, 25},
+ {4480, 22, 79, 21, 19, 21, 38, 18, 16, 30, 25},
+ {4470, 22, 79, 21, 19, 21, 37, 18, 16, 30, 25},
+ {4460, 22, 79, 21, 19, 21, 37, 18, 16, 30, 25},
+ {4450, 21, 80, 21, 19, 21, 37, 18, 16, 30, 25},
+ {4440, 21, 79, 21, 19, 21, 37, 18, 16, 30, 25},
+ {4430, 21, 79, 21, 19, 21, 37, 18, 16, 30, 25},
+ {4420, 21, 79, 21, 19, 21, 37, 18, 16, 30, 25},
+ {4410, 21, 79, 21, 19, 20, 38, 18, 16, 30, 25},
+ {4400, 21, 78, 20, 19, 20, 37, 18, 16, 30, 24},
+ {4390, 21, 78, 20, 19, 20, 37, 18, 16, 30, 24},
+ {4380, 21, 78, 20, 19, 20, 37, 18, 16, 30, 24},
+ {4370, 21, 78, 20, 19, 20, 37, 18, 16, 30, 24},
+ {4360, 21, 77, 20, 19, 20, 37, 18, 16, 29, 24},
+ {4350, 21, 77, 20, 19, 20, 37, 18, 16, 29, 24},
+ {4340, 21, 77, 20, 19, 20, 37, 18, 16, 29, 24},
+ {4330, 21, 77, 20, 19, 20, 36, 18, 16, 29, 24},
+ {4320, 21, 77, 20, 19, 20, 36, 18, 16, 29, 24},
+ {4310, 21, 76, 20, 19, 20, 36, 18, 16, 29, 24},
+ {4300, 21, 76, 20, 18, 20, 36, 18, 16, 29, 24},
+ {4290, 21, 76, 20, 18, 20, 36, 18, 16, 29, 24},
+ {4280, 21, 76, 20, 18, 20, 36, 18, 16, 29, 24},
+ {4270, 21, 75, 20, 18, 20, 36, 18, 16, 29, 24},
+ {4260, 21, 75, 20, 18, 20, 35, 17, 15, 29, 24},
+ {4250, 20, 76, 20, 18, 20, 35, 17, 15, 29, 24},
+ {4240, 20, 76, 20, 18, 20, 35, 17, 15, 29, 23},
+ {4230, 20, 75, 20, 18, 20, 35, 17, 15, 29, 23},
+ {4220, 20, 75, 20, 18, 20, 35, 17, 15, 29, 23},
+ {4210, 20, 75, 20, 18, 20, 35, 17, 15, 28, 23},
+ {4200, 20, 75, 19, 18, 19, 36, 17, 15, 28, 23},
+ {4190, 20, 74, 19, 18, 19, 36, 17, 15, 28, 23},
+ {4180, 20, 74, 19, 18, 19, 35, 17, 15, 28, 23},
+ {4170, 20, 74, 19, 18, 19, 35, 17, 15, 28, 23},
+ {4160, 20, 74, 19, 18, 19, 35, 17, 15, 28, 23},
+ {4150, 20, 74, 19, 18, 19, 35, 17, 15, 28, 23},
+ {4140, 20, 73, 19, 18, 19, 35, 17, 15, 28, 23},
+ {4130, 20, 73, 19, 18, 19, 35, 17, 15, 28, 23},
+ {4120, 20, 73, 19, 18, 19, 35, 17, 15, 28, 23},
+ {4110, 20, 73, 19, 18, 19, 34, 17, 15, 28, 23},
+ {4100, 20, 72, 19, 18, 19, 34, 17, 15, 28, 23},
+ {4090, 20, 72, 19, 18, 19, 34, 17, 15, 28, 23},
+ {4080, 20, 72, 19, 18, 19, 34, 17, 15, 28, 23},
+ {4070, 20, 72, 19, 18, 19, 34, 17, 15, 27, 22},
+ {4060, 19, 72, 19, 17, 19, 34, 17, 15, 27, 22},
+ {4050, 19, 72, 19, 17, 19, 34, 17, 15, 27, 22},
+ {4040, 19, 72, 19, 17, 19, 33, 17, 15, 27, 22},
+ {4030, 19, 72, 19, 17, 19, 33, 17, 15, 27, 22},
+ {4020, 19, 71, 19, 17, 19, 33, 16, 15, 27, 22},
+ {4010, 19, 71, 19, 17, 19, 33, 16, 15, 27, 22},
+ {4000, 19, 71, 18, 17, 19, 33, 16, 14, 27, 22},
+ {3990, 19, 71, 18, 17, 18, 34, 16, 14, 27, 22},
+ {3980, 19, 71, 18, 17, 18, 34, 16, 14, 27, 22},
+ {3970, 19, 70, 18, 17, 18, 33, 16, 14, 27, 22},
+ {3960, 19, 70, 18, 17, 18, 33, 16, 14, 27, 22},
+ {3950, 19, 70, 18, 17, 18, 33, 16, 14, 27, 22},
+ {3940, 19, 70, 18, 17, 18, 33, 16, 14, 27, 22},
+ {3930, 19, 69, 18, 17, 18, 33, 16, 14, 27, 22},
+ {3920, 19, 69, 18, 17, 18, 33, 16, 14, 26, 22},
+ {3910, 19, 69, 18, 17, 18, 33, 16, 14, 26, 22},
+ {3900, 19, 69, 18, 17, 18, 33, 16, 14, 26, 21},
+ {3890, 19, 68, 18, 17, 18, 32, 16, 14, 26, 21},
+ {3880, 19, 68, 18, 17, 18, 32, 16, 14, 26, 21},
+ {3870, 19, 68, 18, 17, 18, 32, 16, 14, 26, 21},
+ {3860, 18, 69, 18, 17, 18, 32, 16, 14, 26, 21},
+ {3850, 18, 68, 18, 17, 18, 32, 16, 14, 26, 21},
+ {3840, 18, 68, 18, 17, 18, 32, 16, 14, 26, 21},
+ {3830, 18, 68, 18, 16, 18, 32, 16, 14, 26, 21},
+ {3820, 18, 68, 18, 16, 18, 31, 16, 14, 26, 21},
+ {3810, 18, 68, 18, 16, 18, 31, 16, 14, 26, 21},
+ {3800, 18, 67, 17, 16, 18, 31, 16, 14, 26, 21},
+ {3790, 18, 67, 17, 16, 17, 32, 15, 14, 26, 21},
+ {3780, 18, 67, 17, 16, 17, 32, 15, 14, 25, 21},
+ {3770, 18, 67, 17, 16, 17, 32, 15, 14, 25, 21},
+ {3760, 18, 66, 17, 16, 17, 32, 15, 14, 25, 21},
+ {3750, 18, 66, 17, 16, 17, 31, 15, 14, 25, 21},
+ {3740, 18, 66, 17, 16, 17, 31, 15, 14, 25, 20},
+ {3730, 18, 66, 17, 16, 17, 31, 15, 13, 25, 20},
+ {3720, 18, 65, 17, 16, 17, 31, 15, 13, 25, 20},
+ {3710, 18, 65, 17, 16, 17, 31, 15, 13, 25, 20},
+ {3700, 18, 65, 17, 16, 17, 31, 15, 13, 25, 20},
+ {3690, 18, 65, 17, 16, 17, 31, 15, 13, 25, 20},
+ {3680, 18, 64, 17, 16, 17, 31, 15, 13, 25, 20},
+ {3670, 18, 64, 17, 16, 17, 30, 15, 13, 25, 20},
+ {3660, 17, 65, 17, 16, 17, 30, 15, 13, 25, 20},
+ {3650, 17, 65, 17, 16, 17, 30, 15, 13, 25, 20},
+ {3640, 17, 65, 17, 16, 17, 30, 15, 13, 25, 20},
+ {3630, 17, 64, 17, 16, 17, 30, 15, 13, 24, 20},
+ {3620, 17, 64, 17, 16, 17, 30, 15, 13, 24, 20},
+ {3610, 17, 64, 17, 16, 17, 30, 15, 13, 24, 20},
+ {3600, 17, 64, 16, 16, 17, 29, 15, 13, 24, 20},
+ {3590, 17, 63, 16, 15, 17, 29, 15, 13, 24, 20},
+ {3580, 17, 63, 16, 15, 16, 30, 15, 13, 24, 20},
+ {3570, 17, 63, 16, 15, 16, 30, 15, 13, 24, 19},
+ {3560, 17, 63, 16, 15, 16, 30, 14, 13, 24, 19},
+ {3550, 17, 62, 16, 15, 16, 30, 14, 13, 24, 19},
+ {3540, 17, 62, 16, 15, 16, 30, 14, 13, 24, 19},
+ {3530, 17, 62, 16, 15, 16, 29, 14, 13, 24, 19},
+ {3520, 17, 62, 16, 15, 16, 29, 14, 13, 24, 19},
+ {3510, 17, 62, 16, 15, 16, 29, 14, 13, 24, 19},
+ {3500, 17, 61, 16, 15, 16, 29, 14, 13, 24, 19},
+ {3490, 17, 61, 16, 15, 16, 29, 14, 13, 23, 19},
+ {3480, 17, 61, 16, 15, 16, 29, 14, 13, 23, 19},
+ {3470, 17, 61, 16, 15, 16, 29, 14, 13, 23, 19},
+ {3460, 16, 61, 16, 15, 16, 28, 14, 12, 23, 19},
+ {3450, 16, 61, 16, 15, 16, 28, 14, 12, 23, 19},
+ {3440, 16, 61, 16, 15, 16, 28, 14, 12, 23, 19},
+ {3430, 16, 61, 16, 15, 16, 28, 14, 12, 23, 19},
+ {3420, 16, 60, 16, 15, 16, 28, 14, 12, 23, 19},
+ {3410, 16, 60, 16, 15, 16, 28, 14, 12, 23, 18},
+ {3400, 16, 60, 15, 15, 16, 28, 14, 12, 23, 18},
+ {3390, 16, 60, 15, 15, 16, 28, 14, 12, 23, 18},
+ {3380, 16, 59, 15, 15, 16, 27, 14, 12, 23, 18},
+ {3370, 16, 59, 15, 15, 15, 28, 14, 12, 23, 18},
+ {3360, 16, 59, 15, 14, 15, 28, 14, 12, 23, 18},
+ {3350, 16, 59, 15, 14, 15, 28, 14, 12, 23, 18},
+ {3340, 16, 59, 15, 14, 15, 28, 14, 12, 22, 18},
+ {3330, 16, 58, 15, 14, 15, 28, 14, 12, 22, 18},
+ {3320, 16, 58, 15, 14, 15, 28, 13, 12, 22, 18},
+ {3310, 16, 58, 15, 14, 15, 27, 13, 12, 22, 18},
+ {3300, 16, 58, 15, 14, 15, 27, 13, 12, 22, 18},
+ {3290, 16, 57, 15, 14, 15, 27, 13, 12, 22, 18},
+ {3280, 16, 57, 15, 14, 15, 27, 13, 12, 22, 18},
+ {3270, 16, 57, 15, 14, 15, 27, 13, 12, 22, 18},
+ {3260, 15, 58, 15, 14, 15, 27, 13, 12, 22, 18},
+ {3250, 15, 57, 15, 14, 15, 27, 13, 12, 22, 18},
+ {3240, 15, 57, 15, 14, 15, 26, 13, 12, 22, 17},
+ {3230, 15, 57, 15, 14, 15, 26, 13, 12, 22, 17},
+ {3220, 15, 57, 15, 14, 15, 26, 13, 12, 22, 17},
+ {3210, 15, 56, 15, 14, 15, 26, 13, 12, 22, 17},
+ {3200, 15, 56, 14, 14, 15, 26, 13, 11, 21, 17},
+ {3190, 15, 56, 14, 14, 15, 26, 13, 11, 21, 17},
+ {3180, 15, 56, 14, 14, 15, 26, 13, 11, 21, 17},
+ {3170, 15, 56, 14, 14, 15, 25, 13, 11, 21, 17},
+ {3160, 15, 55, 14, 14, 14, 26, 13, 11, 21, 17},
+ {3150, 15, 55, 14, 14, 14, 26, 13, 11, 21, 17},
+ {3140, 15, 55, 14, 14, 14, 26, 13, 11, 21, 17},
+ {3130, 15, 55, 14, 14, 14, 26, 13, 11, 21, 17},
+ {3120, 15, 54, 14, 13, 14, 26, 13, 11, 21, 17},
+ {3110, 15, 54, 14, 13, 14, 26, 13, 11, 21, 17},
+ {3100, 15, 54, 14, 13, 14, 26, 13, 11, 21, 17},
+ {3090, 15, 54, 14, 13, 14, 25, 12, 11, 21, 17},
+ {3080, 15, 53, 14, 13, 14, 25, 12, 11, 21, 17},
+ {3070, 14, 54, 14, 13, 14, 25, 12, 11, 21, 16},
+ {3060, 14, 54, 14, 13, 14, 25, 12, 11, 21, 16},
+ {3050, 14, 54, 14, 13, 14, 25, 12, 11, 20, 16},
+ {3040, 14, 53, 14, 13, 14, 25, 12, 11, 20, 16},
+ {3030, 14, 53, 14, 13, 14, 25, 12, 11, 20, 16},
+ {3020, 14, 53, 14, 13, 14, 24, 12, 11, 20, 16},
+ {3010, 14, 53, 14, 13, 14, 24, 12, 11, 20, 16},
+ {3000, 14, 53, 13, 13, 14, 24, 12, 11, 20, 16},
+ {2990, 14, 52, 13, 13, 14, 24, 12, 11, 20, 16},
+ {2980, 14, 52, 13, 13, 14, 24, 12, 11, 20, 16},
+ {2970, 14, 52, 13, 13, 14, 24, 12, 11, 20, 16},
+ {2960, 14, 52, 13, 13, 14, 24, 12, 11, 20, 16},
+ {2950, 14, 51, 13, 13, 13, 24, 12, 11, 20, 16},
+ {2940, 14, 51, 13, 13, 13, 24, 12, 11, 20, 16},
+ {2930, 14, 51, 13, 13, 13, 24, 12, 10, 20, 16},
+ {2920, 14, 51, 13, 13, 13, 24, 12, 10, 20, 16},
+ {2910, 14, 50, 13, 13, 13, 24, 12, 10, 20, 15},
+ {2900, 14, 50, 13, 13, 13, 24, 12, 10, 19, 15},
+ {2890, 14, 50, 13, 12, 13, 24, 12, 10, 19, 15},
+ {2880, 14, 50, 13, 12, 13, 23, 12, 10, 19, 15},
+ {2870, 13, 50, 13, 12, 13, 23, 12, 10, 19, 15},
+ {2860, 13, 50, 13, 12, 13, 23, 12, 10, 19, 15},
+ {2850, 13, 50, 13, 12, 13, 23, 11, 10, 19, 15},
+ {2840, 13, 50, 13, 12, 13, 23, 11, 10, 19, 15},
+ {2830, 13, 50, 13, 12, 13, 23, 11, 10, 19, 15},
+ {2820, 13, 49, 13, 12, 13, 23, 11, 10, 19, 15},
+ {2810, 13, 49, 13, 12, 13, 23, 11, 10, 19, 15},
+ {2800, 13, 49, 12, 12, 13, 22, 11, 10, 19, 15},
+ {2790, 13, 49, 12, 12, 13, 22, 11, 10, 19, 15},
+ {2780, 13, 48, 12, 12, 13, 22, 11, 10, 19, 15},
+ {2770, 13, 48, 12, 12, 13, 22, 11, 10, 19, 15},
+ {2760, 13, 48, 12, 12, 13, 22, 11, 10, 18, 15},
+ {2750, 13, 48, 12, 12, 13, 22, 11, 10, 18, 15},
+ {2740, 13, 47, 12, 12, 12, 23, 11, 10, 18, 14},
+ {2730, 13, 47, 12, 12, 12, 22, 11, 10, 18, 14},
+ {2720, 13, 47, 12, 12, 12, 22, 11, 10, 18, 14},
+ {2710, 13, 47, 12, 12, 12, 22, 11, 10, 18, 14},
+ {2700, 13, 47, 12, 12, 12, 22, 11, 10, 18, 14},
+ {2690, 13, 46, 12, 12, 12, 22, 11, 10, 18, 14},
+ {2680, 13, 46, 12, 12, 12, 22, 11, 10, 18, 14},
+ {2670, 12, 47, 12, 12, 12, 22, 11, 10, 18, 14},
+ {2660, 12, 47, 12, 12, 12, 21, 11, 9, 18, 14},
+ {2650, 12, 46, 12, 11, 12, 21, 11, 9, 18, 14},
+ {2640, 12, 46, 12, 11, 12, 21, 11, 9, 18, 14},
+ {2630, 12, 46, 12, 11, 12, 21, 11, 9, 18, 14},
+ {2620, 12, 46, 12, 11, 12, 21, 10, 9, 18, 14},
+ {2610, 12, 45, 12, 11, 12, 21, 10, 9, 17, 14},
+ {2600, 12, 45, 11, 11, 12, 21, 10, 9, 17, 14},
+ {2590, 12, 45, 11, 11, 12, 20, 10, 9, 17, 14},
+ {2580, 12, 45, 11, 11, 12, 20, 10, 9, 17, 14},
+ {2570, 12, 44, 11, 11, 12, 20, 10, 9, 17, 13},
+ {2560, 12, 44, 11, 11, 12, 20, 10, 9, 17, 13},
+ {2550, 12, 44, 11, 11, 12, 20, 10, 9, 17, 13},
+ {2540, 12, 44, 11, 11, 11, 21, 10, 9, 17, 13},
+ {2530, 12, 44, 11, 11, 11, 21, 10, 9, 17, 13},
+ {2520, 12, 43, 11, 11, 11, 21, 10, 9, 17, 13},
+ {2510, 12, 43, 11, 11, 11, 20, 10, 9, 17, 13},
+ {2500, 12, 43, 11, 11, 11, 20, 10, 9, 17, 13},
+ {2490, 12, 43, 11, 11, 11, 20, 10, 9, 17, 13},
+ {2480, 12, 42, 11, 11, 11, 20, 10, 9, 17, 13},
+ {2470, 11, 43, 11, 11, 11, 20, 10, 9, 16, 13},
+ {2460, 11, 43, 11, 11, 11, 20, 10, 9, 16, 13},
+ {2450, 11, 43, 11, 11, 11, 20, 10, 9, 16, 13},
+ {2440, 11, 42, 11, 11, 11, 19, 10, 9, 16, 13},
+ {2430, 11, 42, 11, 11, 11, 19, 10, 9, 16, 13},
+ {2420, 11, 42, 11, 10, 11, 19, 10, 9, 16, 13},
+ {2410, 11, 42, 11, 10, 11, 19, 10, 9, 16, 12},
+ {2400, 11, 41, 10, 10, 11, 19, 10, 8, 16, 12},
+ {2390, 11, 41, 10, 10, 11, 19, 10, 8, 16, 12},
+ {2380, 11, 41, 10, 10, 11, 19, 9, 8, 16, 12},
+ {2370, 11, 41, 10, 10, 11, 18, 9, 8, 16, 12},
+ {2360, 11, 41, 10, 10, 11, 18, 9, 8, 16, 12},
+ {2350, 11, 40, 10, 10, 11, 18, 9, 8, 16, 12},
+ {2340, 11, 40, 10, 10, 11, 18, 9, 8, 16, 12},
+ {2330, 11, 40, 10, 10, 10, 19, 9, 8, 16, 12},
+ {2320, 11, 40, 10, 10, 10, 19, 9, 8, 15, 12},
+ {2310, 11, 39, 10, 10, 10, 19, 9, 8, 15, 12},
+ {2300, 11, 39, 10, 10, 10, 18, 9, 8, 15, 12},
+ {2290, 11, 39, 10, 10, 10, 18, 9, 8, 15, 12},
+ {2280, 11, 39, 10, 10, 10, 18, 9, 8, 15, 12},
+ {2270, 10, 39, 10, 10, 10, 18, 9, 8, 15, 12},
+ {2260, 10, 39, 10, 10, 10, 18, 9, 8, 15, 12},
+ {2250, 10, 39, 10, 10, 10, 18, 9, 8, 15, 12},
+ {2240, 10, 39, 10, 10, 10, 18, 9, 8, 15, 11},
+ {2230, 10, 38, 10, 10, 10, 18, 9, 8, 15, 11},
+ {2220, 10, 38, 10, 10, 10, 17, 9, 8, 15, 11},
+ {2210, 10, 38, 10, 10, 10, 17, 9, 8, 15, 11},
+ {2200, 10, 38, 9, 10, 10, 17, 9, 8, 15, 11},
+ {2190, 10, 38, 9, 9, 10, 17, 9, 8, 15, 11},
+ {2180, 10, 37, 9, 9, 10, 17, 9, 8, 14, 11},
+ {2170, 10, 37, 9, 9, 10, 17, 9, 8, 14, 11},
+ {2160, 10, 37, 9, 9, 10, 17, 9, 8, 14, 11},
+ {2150, 10, 37, 9, 9, 10, 16, 8, 8, 14, 11},
+ {2140, 10, 36, 9, 9, 10, 16, 8, 8, 14, 11},
+ {2130, 10, 36, 9, 9, 10, 16, 8, 7, 14, 11},
+ {2120, 10, 36, 9, 9, 9, 17, 8, 7, 14, 11},
+ {2110, 10, 36, 9, 9, 9, 17, 8, 7, 14, 11},
+ {2100, 10, 35, 9, 9, 9, 17, 8, 7, 14, 11},
+ {2090, 10, 35, 9, 9, 9, 17, 8, 7, 14, 11},
+ {2080, 9, 36, 9, 9, 9, 16, 8, 7, 14, 11},
+ {2070, 9, 36, 9, 9, 9, 16, 8, 7, 14, 10},
+ {2060, 9, 35, 9, 9, 9, 16, 8, 7, 14, 10},
+ {2050, 9, 35, 9, 9, 9, 16, 8, 7, 14, 10},
+ {2040, 9, 35, 9, 9, 9, 16, 8, 7, 14, 10},
+ {2030, 9, 35, 9, 9, 9, 16, 8, 7, 13, 10},
+ {2020, 9, 35, 9, 9, 9, 16, 8, 7, 13, 10},
+ {2010, 9, 34, 9, 9, 9, 15, 8, 7, 13, 10},
+ {2000, 9, 34, 8, 9, 9, 15, 8, 7, 13, 10},
+ {1990, 9, 34, 8, 9, 9, 15, 8, 7, 13, 10},
+ {1980, 9, 34, 8, 9, 9, 15, 8, 7, 13, 10},
+ {1970, 9, 33, 8, 9, 9, 15, 8, 7, 13, 10},
+ {1960, 9, 33, 8, 9, 9, 15, 8, 7, 13, 10},
+ {1950, 9, 33, 8, 8, 9, 15, 8, 7, 13, 10},
+ {1940, 9, 33, 8, 8, 9, 15, 8, 7, 13, 10},
+ {1930, 9, 32, 8, 8, 9, 14, 8, 7, 13, 10},
+ {1920, 9, 32, 8, 8, 9, 14, 8, 7, 13, 10},
+ {1910, 9, 32, 8, 8, 8, 15, 7, 7, 13, 9},
+ {1900, 9, 32, 8, 8, 8, 15, 7, 7, 13, 9},
+ {1890, 9, 31, 8, 8, 8, 15, 7, 7, 12, 9},
+ {1880, 8, 32, 8, 8, 8, 15, 7, 7, 12, 9},
+ {1870, 8, 32, 8, 8, 8, 15, 7, 7, 12, 9},
+ {1860, 8, 32, 8, 8, 8, 14, 7, 6, 12, 9},
+ {1850, 8, 32, 8, 8, 8, 14, 7, 6, 12, 9},
+ {1840, 8, 31, 8, 8, 8, 14, 7, 6, 12, 9},
+ {1830, 8, 31, 8, 8, 8, 14, 7, 6, 12, 9},
+ {1820, 8, 31, 8, 8, 8, 14, 7, 6, 12, 9},
+ {1810, 8, 31, 8, 8, 8, 14, 7, 6, 12, 9},
+ {1800, 8, 30, 7, 8, 8, 14, 7, 6, 12, 9},
+ {1790, 8, 30, 7, 8, 8, 13, 7, 6, 12, 9},
+ {1780, 8, 30, 7, 8, 8, 13, 7, 6, 12, 9},
+ {1770, 8, 30, 7, 8, 8, 13, 7, 6, 12, 9},
+ {1760, 8, 29, 7, 8, 8, 13, 7, 6, 12, 9},
+ {1750, 8, 29, 7, 8, 8, 13, 7, 6, 12, 9},
+ {1740, 8, 29, 7, 8, 8, 13, 7, 6, 11, 8},
+ {1730, 8, 29, 7, 8, 8, 13, 7, 6, 11, 8},
+ {1720, 8, 29, 7, 7, 8, 13, 7, 6, 11, 8},
+ {1710, 8, 28, 7, 7, 8, 12, 7, 6, 11, 8},
+ {1700, 8, 28, 7, 7, 7, 13, 7, 6, 11, 8},
+ {1690, 8, 28, 7, 7, 7, 13, 7, 6, 11, 8},
+ {1680, 7, 29, 7, 7, 7, 13, 6, 6, 11, 8},
+ {1670, 7, 28, 7, 7, 7, 13, 6, 6, 11, 8},
+ {1660, 7, 28, 7, 7, 7, 13, 6, 6, 11, 8},
+ {1650, 7, 28, 7, 7, 7, 13, 6, 6, 11, 8},
+ {1640, 7, 28, 7, 7, 7, 12, 6, 6, 11, 8},
+ {1630, 7, 27, 7, 7, 7, 12, 6, 6, 11, 8},
+ {1620, 7, 27, 7, 7, 7, 12, 6, 6, 11, 8},
+ {1610, 7, 27, 7, 7, 7, 12, 6, 6, 11, 8},
+ {1600, 7, 27, 6, 7, 7, 12, 6, 5, 10, 8},
+ {1590, 7, 26, 6, 7, 7, 12, 6, 5, 10, 8},
+ {1580, 7, 26, 6, 7, 7, 12, 6, 5, 10, 7},
+ {1570, 7, 26, 6, 7, 7, 11, 6, 5, 10, 7},
+ {1560, 7, 26, 6, 7, 7, 11, 6, 5, 10, 7},
+ {1550, 7, 26, 6, 7, 7, 11, 6, 5, 10, 7},
+ {1540, 7, 25, 6, 7, 7, 11, 6, 5, 10, 7},
+ {1530, 7, 25, 6, 7, 7, 11, 6, 5, 10, 7},
+ {1520, 7, 25, 6, 7, 7, 11, 6, 5, 10, 7},
+ {1510, 7, 25, 6, 7, 7, 11, 6, 5, 10, 7},
+ {1500, 7, 24, 6, 7, 7, 10, 6, 5, 10, 7},
+ {1490, 59, 25, 6, 77, 59, 10, 70, 44, 9, 73},
+ {1480, 59, 24, 6, 76, 58, 10, 70, 44, 9, 73},
+ {1470, 58, 24, 6, 76, 58, 10, 69, 44, 9, 72},
+ {1460, 58, 24, 6, 76, 58, 10, 69, 43, 9, 72},
+ {1450, 58, 24, 6, 75, 57, 10, 68, 43, 9, 71},
+ {1440, 57, 24, 6, 75, 57, 10, 68, 43, 9, 71},
+ {1430, 57, 23, 6, 75, 57, 10, 68, 43, 8, 70},
+ {1420, 56, 23, 6, 74, 57, 9, 67, 43, 8, 70},
+ {1410, 56, 23, 6, 74, 57, 9, 67, 43, 8, 69},
+ {1400, 56, 23, 5, 74, 55, 9, 67, 41, 8, 69},
+ {1390, 55, 23, 5, 73, 55, 9, 66, 41, 8, 68},
+ {1380, 55, 23, 5, 73, 54, 9, 66, 41, 8, 68},
+ {1370, 54, 22, 5, 72, 54, 9, 66, 41, 8, 67},
+ {1360, 54, 22, 5, 72, 54, 9, 65, 40, 8, 67},
+ {1350, 54, 22, 5, 72, 53, 9, 65, 40, 8, 66},
+ {1340, 53, 22, 5, 71, 53, 9, 65, 40, 8, 66},
+ {1330, 53, 22, 5, 71, 53, 9, 64, 39, 8, 65},
+ {1320, 52, 22, 5, 71, 53, 8, 64, 40, 8, 65},
+ {1310, 52, 21, 5, 70, 53, 8, 64, 40, 8, 64},
+ {1300, 51, 21, 5, 70, 51, 8, 63, 38, 8, 64},
+ {1290, 51, 21, 5, 70, 51, 8, 63, 38, 7, 64},
+ {1280, 51, 21, 5, 69, 51, 8, 63, 38, 7, 63},
+ {1270, 50, 21, 5, 69, 50, 8, 62, 38, 7, 63},
+ {1260, 50, 20, 5, 69, 50, 8, 62, 37, 7, 62},
+ {1250, 49, 20, 5, 68, 49, 8, 62, 37, 7, 62},
+ {1240, 49, 20, 5, 68, 49, 8, 61, 37, 7, 61},
+ {1230, 49, 20, 5, 68, 49, 8, 61, 36, 7, 61},
+ {1220, 48, 20, 5, 67, 48, 8, 61, 36, 7, 60},
+ {1210, 48, 19, 5, 67, 48, 7, 60, 36, 7, 60},
+ {1200, 49, 19, 4, 67, 49, 7, 60, 36, 7, 59},
+ {1190, 48, 19, 4, 66, 48, 7, 60, 36, 7, 59},
+ {1180, 48, 19, 4, 66, 48, 7, 59, 36, 7, 58},
+ {1170, 46, 19, 4, 66, 46, 7, 59, 35, 7, 58},
+ {1160, 46, 18, 4, 65, 46, 7, 59, 34, 7, 57},
+ {1150, 45, 18, 4, 65, 46, 7, 58, 34, 7, 57},
+ {1140, 45, 18, 4, 65, 45, 7, 58, 34, 6, 56},
+ {1130, 45, 18, 4, 64, 45, 7, 58, 33, 6, 56},
+ {1120, 44, 18, 4, 64, 44, 7, 57, 33, 6, 55},
+ {1110, 44, 18, 4, 64, 44, 7, 57, 33, 6, 55},
+ {1100, 43, 17, 4, 63, 44, 6, 57, 32, 6, 54},
+ {1090, 43, 17, 4, 63, 44, 6, 56, 33, 6, 54},
+ {1080, 43, 17, 4, 63, 44, 6, 56, 33, 6, 53},
+ {1070, 42, 17, 4, 62, 44, 6, 56, 33, 6, 53},
+ {1060, 42, 17, 4, 62, 42, 6, 55, 31, 6, 52},
+ {1050, 41, 17, 4, 62, 42, 6, 55, 31, 6, 52},
+ {1040, 41, 16, 4, 61, 41, 6, 54, 31, 6, 52},
+ {1030, 41, 16, 4, 61, 41, 6, 54, 30, 6, 51},
+ {1020, 40, 16, 4, 61, 41, 6, 54, 30, 6, 51},
+ {1010, 40, 16, 4, 60, 40, 6, 53, 30, 6, 50},
+ {1000, 39, 16, 3, 60, 40, 6, 53, 29, 5, 50},
+ { 990, 39, 15, 3, 60, 39, 6, 53, 29, 5, 49},
+ { 980, 39, 15, 3, 59, 39, 5, 52, 29, 5, 49},
+ { 970, 38, 15, 3, 59, 39, 5, 52, 29, 5, 48},
+ { 960, 38, 15, 3, 59, 39, 5, 52, 29, 5, 48},
+ { 950, 37, 15, 3, 58, 39, 5, 51, 29, 5, 47},
+ { 940, 37, 14, 3, 58, 39, 5, 51, 29, 5, 47},
+ { 930, 37, 14, 3, 57, 37, 5, 51, 27, 5, 46},
+ { 920, 36, 14, 3, 57, 37, 5, 50, 27, 5, 46},
+ { 910, 36, 14, 3, 57, 36, 5, 50, 27, 5, 45},
+ { 900, 35, 14, 3, 56, 36, 5, 50, 26, 5, 45},
+ { 890, 35, 14, 3, 56, 36, 5, 49, 26, 5, 44},
+ { 880, 35, 13, 3, 56, 35, 5, 49, 26, 5, 44},
+ { 870, 34, 13, 3, 55, 35, 4, 49, 26, 5, 43},
+ { 860, 34, 13, 3, 55, 35, 4, 48, 25, 5, 43},
+ { 850, 33, 13, 3, 55, 35, 4, 48, 26, 4, 42},
+ { 840, 33, 13, 3, 54, 35, 4, 48, 26, 4, 42},
+ { 830, 33, 12, 3, 54, 33, 4, 47, 24, 4, 41},
+ { 820, 32, 12, 3, 54, 33, 4, 47, 24, 4, 41},
+ { 810, 32, 12, 3, 53, 33, 4, 47, 24, 4, 40},
+ { 800, 31, 12, 2, 53, 32, 4, 46, 23, 4, 40},
+ { 790, 31, 12, 2, 53, 32, 4, 46, 23, 4, 39},
+ { 780, 30, 12, 2, 52, 31, 4, 46, 23, 4, 39},
+ { 770, 30, 11, 2, 52, 31, 4, 45, 23, 4, 39},
+ { 760, 30, 11, 2, 52, 31, 3, 45, 22, 4, 38},
+ { 750, 29, 11, 2, 51, 30, 3, 45, 22, 4, 38},
+ { 740, 29, 11, 2, 51, 30, 3, 44, 22, 4, 37},
+ { 730, 28, 11, 2, 51, 31, 3, 44, 22, 4, 37},
+ { 720, 28, 10, 2, 50, 30, 3, 44, 22, 4, 36},
+ { 710, 28, 10, 2, 50, 30, 3, 43, 22, 4, 36},
+ { 700, 27, 10, 2, 50, 28, 3, 43, 20, 3, 35},
+ { 690, 27, 10, 2, 49, 28, 3, 43, 20, 3, 35},
+ { 680, 26, 10, 2, 49, 28, 3, 42, 20, 3, 34},
+ { 670, 26, 10, 2, 49, 27, 3, 42, 20, 3, 34},
+ { 660, 26, 9, 2, 48, 27, 3, 42, 19, 3, 33},
+ { 650, 25, 9, 2, 48, 26, 3, 41, 19, 3, 33},
+ { 640, 25, 9, 2, 48, 26, 2, 41, 19, 3, 32},
+ { 630, 24, 9, 2, 47, 26, 2, 40, 18, 3, 32},
+ { 620, 24, 9, 2, 47, 26, 2, 40, 19, 3, 31},
+ { 610, 24, 8, 2, 47, 26, 2, 40, 19, 3, 31},
+ { 600, 23, 8, 1, 46, 26, 2, 39, 18, 3, 30},
+ { 590, 23, 8, 1, 46, 24, 2, 39, 17, 3, 30},
+ { 580, 22, 8, 1, 46, 24, 2, 39, 17, 3, 29},
+ { 570, 22, 8, 1, 45, 23, 2, 38, 17, 3, 29},
+ { 560, 22, 7, 1, 45, 23, 2, 38, 16, 2, 28},
+ { 550, 21, 7, 1, 45, 23, 2, 38, 16, 2, 28},
+ { 540, 21, 7, 1, 44, 22, 2, 37, 16, 2, 27},
+ { 530, 20, 7, 1, 44, 22, 1, 37, 15, 2, 27},
+ { 520, 20, 7, 1, 43, 21, 1, 37, 15, 2, 27},
+ { 510, 20, 6, 1, 43, 21, 1, 36, 15, 2, 26},
+ { 500, 19, 6, 1, 43, 22, 1, 36, 15, 2, 26},
+ { 490, 19, 6, 1, 42, 21, 1, 36, 15, 2, 25},
+ { 480, 18, 6, 1, 42, 21, 1, 35, 15, 2, 25},
+ { 470, 18, 6, 1, 42, 21, 1, 35, 15, 2, 24},
+ { 460, 18, 6, 1, 41, 19, 1, 35, 13, 2, 24},
+ { 450, 17, 5, 1, 41, 19, 1, 34, 13, 2, 23},
+ { 440, 17, 5, 1, 41, 18, 1, 34, 13, 2, 23},
+ { 430, 16, 5, 1, 40, 18, 0, 34, 12, 2, 22},
+ { 420, 16, 5, 1, 40, 18, 0, 33, 12, 2, 22},
+ { 410, 16, 5, 1, 40, 17, 0, 33, 12, 1, 21},
+ { 400, 15, 5, 0, 39, 17, 0, 33, 11, 1, 21},
+ { 390, 15, 4, 0, 39, 17, 0, 32, 12, 1, 20},
+ { 380, 14, 4, 0, 39, 17, 0, 32, 12, 1, 20},
+ { 370, 14, 4, 0, 38, 17, 0, 32, 12, 1, 19},
+ { 360, 14, 4, 0, 38, 15, 0, 31, 10, 1, 19},
+ { 350, 13, 4, 0, 38, 15, 0, 31, 10, 1, 18},
+ { 340, 13, 3, 0, 37, 15, 0, 31, 10, 1, 18},
+ { 330, 12, 3, 0, 37, 14, 0, 30, 9, 1, 17},
+ { 320, 12, 3, 0, 37, 14, 0, 30, 9, 1, 17},
+ { 310, 12, 3, 0, 36, 13, 0, 30, 9, 1, 16},
+ { 300, 11, 3, 0, 36, 13, 0, 29, 8, 1, 16},
+ { 290, 11, 2, 0, 36, 13, 0, 29, 8, 1, 15},
+ { 280, 10, 2, 0, 35, 12, 0, 29, 8, 1, 15},
+ { 270, 10, 2, 0, 35, 12, 0, 28, 8, 0, 14},
+ { 260, 9, 2, 0, 35, 12, 0, 28, 8, 0, 14},
+ { 250, 9, 2, 0, 34, 12, 0, 28, 8, 0, 14},
+ { 240, 9, 2, 0, 34, 12, 0, 27, 8, 0, 13},
+ { 230, 8, 1, 0, 34, 10, 0, 27, 6, 0, 13},
+ { 220, 8, 1, 0, 33, 10, 0, 27, 6, 0, 12},
+ { 210, 7, 1, 0, 33, 10, 0, 26, 6, 0, 12},
+ { 200, 7, 1, 0, 33, 9, 0, 26, 5, 0, 11},
+ { 190, 7, 1, 0, 32, 9, 0, 25, 5, 0, 11},
+ { 180, 6, 1, 0, 32, 8, 0, 25, 5, 0, 10},
+ { 170, 6, 0, 0, 32, 8, 0, 25, 5, 0, 10},
+ { 160, 5, 0, 0, 31, 8, 0, 24, 4, 0, 9},
+ { 150, 5, 0, 0, 31, 8, 0, 24, 5, 0, 9},
+ { 140, 5, 0, 0, 31, 8, 0, 24, 5, 0, 8},
+ { 130, 4, 0, 0, 30, 6, 0, 23, 3, 0, 8},
+ { 120, 4, 0, 0, 30, 6, 0, 23, 3, 0, 7},
+ { 110, 3, 0, 0, 30, 6, 0, 23, 3, 0, 7},
+ { 100, 3, 0, 0, 29, 5, 0, 22, 2, 0, 6},
+ { 90, 3, 0, 0, 29, 5, 0, 22, 2, 0, 6},
+ { 80, 2, 0, 0, 28, 5, 0, 22, 2, 0, 5},
+};
+
+static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *samsung)
+{
+ regmap_write(samsung->regmap, BIAS_CON0, I_DEV_DIV_6 | I_RES_100_2UA);
+ regmap_write(samsung->regmap, BIAS_CON1, I_VBG_SEL_820MV | I_BGR_VREF_820MV |
+ I_LADDER_1_00V);
+ regmap_write(samsung->regmap, BIAS_CON2, REG_325M_325MV | REG_LP_400M_400MV |
+ REG_400M_400MV | REG_645M_645MV);
+
+ /* default output voltage select:
+ * dphy: 400mv
+ * cphy: 530mv
+ */
+ regmap_update_bits(samsung->regmap, BIAS_CON4,
+ I_MUX_SEL_MASK, I_MUX_400MV);
+}
+
+static void samsung_mipi_dphy_lane_enable(struct samsung_mipi_dcphy *samsung)
+{
+ regmap_write(samsung->regmap, DPHY_MC_GNR_CON1, T_PHY_READY(0x2000));
+ regmap_update_bits(samsung->regmap, DPHY_MC_GNR_CON0,
+ PHY_ENABLE, PHY_ENABLE);
+
+ switch (samsung->lanes) {
+ case 4:
+ regmap_write(samsung->regmap, DPHY_MD3_GNR_CON1,
+ T_PHY_READY(0x2000));
+ regmap_update_bits(samsung->regmap, DPHY_MD3_GNR_CON0,
+ PHY_ENABLE, PHY_ENABLE);
+ fallthrough;
+ case 3:
+ regmap_write(samsung->regmap, COMBO_MD2_GNR_CON1,
+ T_PHY_READY(0x2000));
+ regmap_update_bits(samsung->regmap, COMBO_MD2_GNR_CON0,
+ PHY_ENABLE, PHY_ENABLE);
+ fallthrough;
+ case 2:
+ regmap_write(samsung->regmap, COMBO_MD1_GNR_CON1,
+ T_PHY_READY(0x2000));
+ regmap_update_bits(samsung->regmap, COMBO_MD1_GNR_CON0,
+ PHY_ENABLE, PHY_ENABLE);
+ fallthrough;
+ case 1:
+ default:
+ regmap_write(samsung->regmap, COMBO_MD0_GNR_CON1,
+ T_PHY_READY(0x2000));
+ regmap_update_bits(samsung->regmap, COMBO_MD0_GNR_CON0,
+ PHY_ENABLE, PHY_ENABLE);
+ break;
+ }
+}
+
+static void samsung_mipi_dphy_lane_disable(struct samsung_mipi_dcphy *samsung)
+{
+ switch (samsung->lanes) {
+ case 4:
+ regmap_update_bits(samsung->regmap, DPHY_MD3_GNR_CON0,
+ PHY_ENABLE, 0);
+ fallthrough;
+ case 3:
+ regmap_update_bits(samsung->regmap, COMBO_MD2_GNR_CON0,
+ PHY_ENABLE, 0);
+ fallthrough;
+ case 2:
+ regmap_update_bits(samsung->regmap, COMBO_MD1_GNR_CON0,
+ PHY_ENABLE, 0);
+ fallthrough;
+ case 1:
+ default:
+ regmap_update_bits(samsung->regmap, COMBO_MD0_GNR_CON0,
+ PHY_ENABLE, 0);
+ break;
+ }
+
+ regmap_update_bits(samsung->regmap, DPHY_MC_GNR_CON0, PHY_ENABLE, 0);
+}
+
+static void samsung_mipi_dcphy_pll_configure(struct samsung_mipi_dcphy *samsung)
+{
+ regmap_update_bits(samsung->regmap, PLL_CON0, S_MASK | P_MASK,
+ S(samsung->pll.scaler) | P(samsung->pll.prediv));
+
+ if (samsung->pll.dsm < 0) {
+ u16 dsm_tmp;
+
+ /* Using opposite number subtraction to find complement */
+ dsm_tmp = abs(samsung->pll.dsm);
+ dsm_tmp = dsm_tmp - 1;
+ dsm_tmp ^= 0xffff;
+ regmap_write(samsung->regmap, PLL_CON1, dsm_tmp);
+ } else {
+ regmap_write(samsung->regmap, PLL_CON1, samsung->pll.dsm);
+ }
+
+ regmap_update_bits(samsung->regmap, PLL_CON2,
+ M_MASK, M(samsung->pll.fbdiv));
+
+ if (samsung->pll.ssc_en) {
+ regmap_write(samsung->regmap, PLL_CON3,
+ MRR(samsung->pll.mrr) | MFR(samsung->pll.mfr));
+ regmap_update_bits(samsung->regmap, PLL_CON4, SSCG_EN, SSCG_EN);
+ }
+
+ regmap_write(samsung->regmap, PLL_CON5, RESET_N_SEL | PLL_ENABLE_SEL);
+ regmap_write(samsung->regmap, PLL_CON7, PLL_LOCK_CNT(0xf000));
+ regmap_write(samsung->regmap, PLL_CON8, PLL_STB_CNT(0xf000));
+}
+
+static int samsung_mipi_dcphy_pll_enable(struct samsung_mipi_dcphy *samsung)
+{
+ u32 sts;
+ int ret;
+
+ regmap_update_bits(samsung->regmap, PLL_CON0, PLL_EN, PLL_EN);
+
+ ret = regmap_read_poll_timeout(samsung->regmap, PLL_STAT0,
+ sts, (sts & PLL_LOCK), 1000, 20000);
+ if (ret < 0)
+ dev_err(samsung->dev, "DC-PHY pll failed to lock\n");
+
+ return ret;
+}
+
+static void samsung_mipi_dcphy_pll_disable(struct samsung_mipi_dcphy *samsung)
+{
+ regmap_update_bits(samsung->regmap, PLL_CON0, PLL_EN, 0);
+}
+
+static const struct samsung_mipi_dphy_timing *
+samsung_mipi_dphy_get_timing(struct samsung_mipi_dcphy *samsung)
+{
+ const struct samsung_mipi_dphy_timing *timings;
+ unsigned int num_timings;
+ unsigned int lane_mbps = div64_ul(samsung->pll.rate, USEC_PER_SEC);
+ unsigned int i;
+
+ timings = samsung_mipi_dphy_timing_table;
+ num_timings = ARRAY_SIZE(samsung_mipi_dphy_timing_table);
+
+ for (i = num_timings; i > 1; i--)
+ if (lane_mbps <= timings[i - 1].max_lane_mbps)
+ break;
+
+ return &timings[i - 1];
+}
+
+static unsigned long
+samsung_mipi_dcphy_pll_round_rate(struct samsung_mipi_dcphy *samsung,
+ unsigned long prate, unsigned long rate,
+ u8 *prediv, u16 *fbdiv, int *dsm, u8 *scaler)
+{
+ u32 max_fout = samsung->pdata->dphy_tx_max_lane_kbps;
+ u64 best_freq = 0;
+ u64 fin, fvco, fout;
+ u8 min_prediv, max_prediv;
+ u8 _prediv, best_prediv = 1;
+ u16 _fbdiv, best_fbdiv = 1;
+ u8 _scaler, best_scaler = 0;
+ u32 min_delta = UINT_MAX;
+ long _dsm, best_dsm = 0;
+
+ if (!prate) {
+ dev_err(samsung->dev, "parent rate of PLL can not be zero\n");
+ return 0;
+ }
+
+ /*
+ * The PLL output frequency can be calculated using a simple formula:
+ * Fvco = ((m+k/65536) x 2 x Fin) / p
+ * Fout = ((m+k/65536) x 2 x Fin) / (p x 2^s)
+ */
+ fin = div64_ul(prate, MSEC_PER_SEC);
+
+ while (!best_freq) {
+ fout = div64_ul(rate, MSEC_PER_SEC);
+ if (fout > max_fout)
+ fout = max_fout;
+
+ /* 0 ≤ S[2:0] ≤ 6 */
+ for (_scaler = 0; _scaler < 7; _scaler++) {
+ fvco = fout << _scaler;
+
+ /*
+ * 2600MHz ≤ FVCO ≤ 6600MHz
+ */
+ if (fvco < 2600 * MSEC_PER_SEC || fvco > 6600 * MSEC_PER_SEC)
+ continue;
+
+ /* 6MHz ≤ Fref(Fin / p) ≤ 30MHz */
+ min_prediv = DIV_ROUND_UP_ULL(fin, 30 * MSEC_PER_SEC);
+ max_prediv = DIV_ROUND_CLOSEST_ULL(fin, 6 * MSEC_PER_SEC);
+
+ for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
+ u64 delta, tmp;
+
+ _fbdiv = DIV_ROUND_CLOSEST_ULL(fvco * _prediv, 2 * fin);
+
+ /* 64 ≤ M[9:0] ≤ 1023 */
+ if (_fbdiv < 64 || _fbdiv > 1023)
+ continue;
+
+ /* -32767 ≤ K[15:0] ≤ 32767 */
+ _dsm = ((_prediv * fvco) - (2 * _fbdiv * fin));
+ _dsm = DIV_ROUND_UP_ULL(_dsm << 15, fin);
+ if (abs(_dsm) > 32767)
+ continue;
+
+ tmp = DIV_ROUND_CLOSEST_ULL((_fbdiv * fin * 2 * 1000), _prediv);
+ tmp += DIV_ROUND_CLOSEST_ULL((_dsm * fin * 1000), _prediv << 15);
+
+ delta = abs(fvco * MSEC_PER_SEC - tmp);
+ if (delta < min_delta) {
+ best_prediv = _prediv;
+ best_fbdiv = _fbdiv;
+ best_dsm = _dsm;
+ best_scaler = _scaler;
+ min_delta = delta;
+ best_freq = DIV_ROUND_CLOSEST_ULL(tmp, 1000) * MSEC_PER_SEC;
+ }
+ }
+ }
+
+ rate += 100 * MSEC_PER_SEC;
+ }
+
+ *prediv = best_prediv;
+ *fbdiv = best_fbdiv;
+ *dsm = (int)best_dsm & 0xffff;
+ *scaler = best_scaler;
+ dev_dbg(samsung->dev, "p: %d, m: %d, dsm:%ld, scaler: %d\n",
+ best_prediv, best_fbdiv, best_dsm, best_scaler);
+
+ return best_freq >> best_scaler;
+}
+
+static void
+samsung_mipi_dphy_clk_lane_timing_init(struct samsung_mipi_dcphy *samsung)
+{
+ const struct samsung_mipi_dphy_timing *timing;
+ unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC);
+ u32 val, res_up, res_down;
+
+ timing = samsung_mipi_dphy_get_timing(samsung);
+ regmap_write(samsung->regmap, DPHY_MC_GNR_CON0, 0xf000);
+
+ /*
+ * The Drive-Strength / Voltage-Amplitude is adjusted by setting
+ * the Driver-Up Resistor and Driver-Down Resistor.
+ */
+ res_up = samsung->pdata->dphy_hs_drv_res_cfg->clk_hs_drv_up_ohm;
+ res_down = samsung->pdata->dphy_hs_drv_res_cfg->clk_hs_drv_down_ohm;
+ val = EDGE_CON(7) | EDGE_CON_DIR(0) | EDGE_CON_EN |
+ RES_UP(res_up) | RES_DN(res_down);
+ regmap_write(samsung->regmap, DPHY_MC_ANA_CON0, val);
+
+ if (lane_hs_rate >= 4500)
+ regmap_write(samsung->regmap, DPHY_MC_ANA_CON1, 0x0001);
+
+ val = 0;
+ /*
+ * Divide-by-2 Clock from Serial Clock. Use this when data rate is under
+ * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock
+ */
+ if (lane_hs_rate < 1500)
+ val = HSTX_CLK_SEL;
+
+ val |= T_LPX(timing->lpx);
+ /* T_LP_EXIT_SKEW/T_LP_ENTRY_SKEW unconfig */
+ regmap_write(samsung->regmap, DPHY_MC_TIME_CON0, val);
+
+ val = T_CLK_ZERO(timing->clk_zero) | T_CLK_PREPARE(timing->clk_prepare);
+ regmap_write(samsung->regmap, DPHY_MC_TIME_CON1, val);
+
+ val = T_HS_EXIT(timing->hs_exit) | T_CLK_TRAIL(timing->clk_trail_eot);
+ regmap_write(samsung->regmap, DPHY_MC_TIME_CON2, val);
+
+ val = T_CLK_POST(timing->clk_post);
+ regmap_write(samsung->regmap, DPHY_MC_TIME_CON3, val);
+
+ /* Escape Clock is 20.00MHz */
+ regmap_write(samsung->regmap, DPHY_MC_TIME_CON4, 0x1f4);
+
+ /*
+ * skew calibration should be off, if the operation data rate is
+ * under 1.5Gbps or equal to 1.5Gbps.
+ */
+ if (lane_hs_rate > 1500)
+ regmap_write(samsung->regmap, DPHY_MC_DESKEW_CON0, 0x9cb1);
+}
+
+static void
+samsung_mipi_dphy_data_lane_timing_init(struct samsung_mipi_dcphy *samsung)
+{
+ const struct samsung_mipi_dphy_timing *timing;
+ unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC);
+ u32 val, res_up, res_down;
+
+ timing = samsung_mipi_dphy_get_timing(samsung);
+
+ /*
+ * The Drive-Strength / Voltage-Amplitude is adjusted by adjusting the
+ * Driver-Up Resistor and Driver-Down Resistor.
+ */
+ res_up = samsung->pdata->dphy_hs_drv_res_cfg->data_hs_drv_up_ohm;
+ res_down = samsung->pdata->dphy_hs_drv_res_cfg->data_hs_drv_down_ohm;
+ val = EDGE_CON(7) | EDGE_CON_DIR(0) | EDGE_CON_EN |
+ RES_UP(res_up) | RES_DN(res_down);
+ regmap_write(samsung->regmap, COMBO_MD0_ANA_CON0, val);
+ regmap_write(samsung->regmap, COMBO_MD1_ANA_CON0, val);
+ regmap_write(samsung->regmap, COMBO_MD2_ANA_CON0, val);
+ regmap_write(samsung->regmap, DPHY_MD3_ANA_CON0, val);
+
+ if (lane_hs_rate >= 4500) {
+ regmap_write(samsung->regmap, COMBO_MD0_ANA_CON1, 0x0001);
+ regmap_write(samsung->regmap, COMBO_MD1_ANA_CON1, 0x0001);
+ regmap_write(samsung->regmap, COMBO_MD2_ANA_CON1, 0x0001);
+ regmap_write(samsung->regmap, DPHY_MD3_ANA_CON1, 0x0001);
+ }
+
+ val = 0;
+ /*
+ * Divide-by-2 Clock from Serial Clock. Use this when data rate is under
+ * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock
+ */
+ if (lane_hs_rate < 1500)
+ val = HSTX_CLK_SEL;
+
+ val |= T_LPX(timing->lpx);
+ /* T_LP_EXIT_SKEW/T_LP_ENTRY_SKEW unconfig */
+ regmap_write(samsung->regmap, COMBO_MD0_TIME_CON0, val);
+ regmap_write(samsung->regmap, COMBO_MD1_TIME_CON0, val);
+ regmap_write(samsung->regmap, COMBO_MD2_TIME_CON0, val);
+ regmap_write(samsung->regmap, DPHY_MD3_TIME_CON0, val);
+
+ val = T_HS_ZERO(timing->hs_zero) | T_HS_PREPARE(timing->hs_prepare);
+ regmap_write(samsung->regmap, COMBO_MD0_TIME_CON1, val);
+ regmap_write(samsung->regmap, COMBO_MD1_TIME_CON1, val);
+ regmap_write(samsung->regmap, COMBO_MD2_TIME_CON1, val);
+ regmap_write(samsung->regmap, DPHY_MD3_TIME_CON1, val);
+
+ val = T_HS_EXIT(timing->hs_exit) | T_HS_TRAIL(timing->hs_trail_eot);
+ regmap_write(samsung->regmap, COMBO_MD0_TIME_CON2, val);
+ regmap_write(samsung->regmap, COMBO_MD1_TIME_CON2, val);
+ regmap_write(samsung->regmap, COMBO_MD2_TIME_CON2, val);
+ regmap_write(samsung->regmap, DPHY_MD3_TIME_CON2, val);
+
+ /* TTA-GET/TTA-GO Timing Counter register use default value */
+ val = T_TA_GET(0x3) | T_TA_GO(0x0);
+ regmap_write(samsung->regmap, COMBO_MD0_TIME_CON3, val);
+ regmap_write(samsung->regmap, COMBO_MD1_TIME_CON3, val);
+ regmap_write(samsung->regmap, COMBO_MD2_TIME_CON3, val);
+ regmap_write(samsung->regmap, DPHY_MD3_TIME_CON3, val);
+
+ /* Escape Clock is 20.00MHz */
+ regmap_write(samsung->regmap, COMBO_MD0_TIME_CON4, 0x1f4);
+ regmap_write(samsung->regmap, COMBO_MD1_TIME_CON4, 0x1f4);
+ regmap_write(samsung->regmap, COMBO_MD2_TIME_CON4, 0x1f4);
+ regmap_write(samsung->regmap, DPHY_MD3_TIME_CON4, 0x1f4);
+}
+
+static int samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
+{
+ int ret;
+
+ reset_control_assert(samsung->m_phy_rst);
+
+ samsung_mipi_dcphy_bias_block_enable(samsung);
+ samsung_mipi_dcphy_pll_configure(samsung);
+ samsung_mipi_dphy_clk_lane_timing_init(samsung);
+ samsung_mipi_dphy_data_lane_timing_init(samsung);
+ ret = samsung_mipi_dcphy_pll_enable(samsung);
+ if (ret < 0)
+ return ret;
+
+ samsung_mipi_dphy_lane_enable(samsung);
+
+ reset_control_deassert(samsung->m_phy_rst);
+
+ /* The TSKEWCAL maximum is 100 µsec
+ * at initial calibration.
+ */
+ usleep_range(100, 110);
+
+ return 0;
+}
+
+static int samsung_mipi_dcphy_power_on(struct phy *phy)
+{
+ struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
+
+ reset_control_assert(samsung->apb_rst);
+ udelay(1);
+ reset_control_deassert(samsung->apb_rst);
+
+ switch (samsung->type) {
+ case PHY_TYPE_DPHY:
+ return samsung_mipi_dphy_power_on(samsung);
+ default:
+ /* CPHY part to be implemented later */
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int samsung_mipi_dcphy_power_off(struct phy *phy)
+{
+ struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
+
+ switch (samsung->type) {
+ case PHY_TYPE_DPHY:
+ samsung_mipi_dphy_lane_disable(samsung);
+ break;
+ default:
+ /* CPHY part to be implemented later */
+ return -EOPNOTSUPP;
+ }
+
+ samsung_mipi_dcphy_pll_disable(samsung);
+
+ return 0;
+}
+
+static int
+samsung_mipi_dcphy_pll_ssc_modulation_calc(struct samsung_mipi_dcphy *samsung,
+ u8 *mfr, u8 *mrr)
+{
+ unsigned long fin = div64_ul(clk_get_rate(samsung->ref_clk), MSEC_PER_SEC);
+ u16 prediv = samsung->pll.prediv;
+ u16 fbdiv = samsung->pll.fbdiv;
+ u16 min_mfr, max_mfr;
+ u16 _mfr, best_mfr = 0;
+ u16 mr, _mrr, best_mrr = 0;
+
+ /* 20KHz ≤ MF ≤ 150KHz */
+ max_mfr = DIV_ROUND_UP(fin, (20 * prediv) << 5);
+ min_mfr = div64_ul(fin, ((150 * prediv) << 5));
+ /*0 ≤ mfr ≤ 255 */
+ if (max_mfr > 256)
+ max_mfr = 256;
+
+ for (_mfr = min_mfr; _mfr < max_mfr; _mfr++) {
+ /* 1 ≤ mrr ≤ 31 */
+ for (_mrr = 1; _mrr < 32; _mrr++) {
+ mr = DIV_ROUND_UP(_mfr * _mrr * 100, fbdiv << 6);
+ /* 0 ≤ MR ≤ 5% */
+ if (mr > 5)
+ continue;
+
+ if (_mfr * _mrr < 513) {
+ best_mfr = _mfr;
+ best_mrr = _mrr;
+ break;
+ }
+ }
+ }
+
+ if (best_mrr) {
+ *mfr = best_mfr & 0xff;
+ *mrr = best_mrr & 0x3f;
+ } else {
+ dev_err(samsung->dev, "failed to calc ssc parameter mfr and mrr\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void
+samsung_mipi_dcphy_pll_calc_rate(struct samsung_mipi_dcphy *samsung,
+ unsigned long long rate)
+{
+ unsigned long prate = clk_get_rate(samsung->ref_clk);
+ unsigned long fout;
+ u8 scaler = 0, mfr = 0, mrr = 0;
+ u16 fbdiv = 0;
+ u8 prediv = 1;
+ int dsm = 0;
+ int ret;
+
+ fout = samsung_mipi_dcphy_pll_round_rate(samsung, prate, rate,
+ &prediv, &fbdiv, &dsm,
+ &scaler);
+
+ dev_dbg(samsung->dev, "%s: fin=%lu, req_rate=%llu\n",
+ __func__, prate, rate);
+ dev_dbg(samsung->dev, "%s: fout=%lu, prediv=%u, fbdiv=%u\n",
+ __func__, fout, prediv, fbdiv);
+
+ samsung->pll.prediv = prediv;
+ samsung->pll.fbdiv = fbdiv;
+ samsung->pll.dsm = dsm;
+ samsung->pll.scaler = scaler;
+ samsung->pll.rate = fout;
+
+ /*
+ * All DPHY 2.0 compliant Transmitters shall support SSC operating above
+ * 2.5 Gbps
+ */
+ if (fout > 2500000000LL) {
+ ret = samsung_mipi_dcphy_pll_ssc_modulation_calc(samsung,
+ &mfr, &mrr);
+ if (!ret) {
+ samsung->pll.ssc_en = true;
+ samsung->pll.mfr = mfr;
+ samsung->pll.mrr = mrr;
+ }
+ }
+}
+
+static int samsung_mipi_dcphy_configure(struct phy *phy,
+ union phy_configure_opts *opts)
+{
+ struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
+ unsigned long long target_rate = opts->mipi_dphy.hs_clk_rate;
+
+ samsung->lanes = opts->mipi_dphy.lanes > 4 ? 4 : opts->mipi_dphy.lanes;
+
+ samsung_mipi_dcphy_pll_calc_rate(samsung, target_rate);
+ opts->mipi_dphy.hs_clk_rate = samsung->pll.rate;
+
+ return 0;
+}
+
+static int samsung_mipi_dcphy_init(struct phy *phy)
+{
+ struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
+
+ return pm_runtime_resume_and_get(samsung->dev);
+}
+
+static int samsung_mipi_dcphy_exit(struct phy *phy)
+{
+ struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
+
+ return pm_runtime_put(samsung->dev);
+}
+
+static const struct phy_ops samsung_mipi_dcphy_ops = {
+ .configure = samsung_mipi_dcphy_configure,
+ .power_on = samsung_mipi_dcphy_power_on,
+ .power_off = samsung_mipi_dcphy_power_off,
+ .init = samsung_mipi_dcphy_init,
+ .exit = samsung_mipi_dcphy_exit,
+ .owner = THIS_MODULE,
+};
+
+static const struct regmap_config samsung_mipi_dcphy_regmap_config = {
+ .name = "dcphy",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x10000,
+};
+
+static struct phy *samsung_mipi_dcphy_xlate(struct device *dev,
+ const struct of_phandle_args *args)
+{
+ struct samsung_mipi_dcphy *samsung = dev_get_drvdata(dev);
+
+ if (args->args_count != 1) {
+ dev_err(dev, "invalid number of arguments\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (samsung->type != PHY_NONE && samsung->type != args->args[0])
+ dev_warn(dev, "phy type select %d overwriting type %d\n",
+ args->args[0], samsung->type);
+
+ samsung->type = args->args[0];
+
+ return samsung->phy;
+}
+
+static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct samsung_mipi_dcphy *samsung;
+ struct phy_provider *phy_provider;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ samsung = devm_kzalloc(dev, sizeof(*samsung), GFP_KERNEL);
+ if (!samsung)
+ return -ENOMEM;
+
+ samsung->dev = dev;
+ samsung->pdata = device_get_match_data(dev);
+ platform_set_drvdata(pdev, samsung);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ samsung->regmap = devm_regmap_init_mmio(dev, regs,
+ &samsung_mipi_dcphy_regmap_config);
+ if (IS_ERR(samsung->regmap))
+ return dev_err_probe(dev, PTR_ERR(samsung->regmap), "Failed to init regmap\n");
+
+ samsung->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(samsung->grf_regmap))
+ return dev_err_probe(dev, PTR_ERR(samsung->grf_regmap),
+ "Unable to get rockchip,grf\n");
+
+ samsung->ref_clk = devm_clk_get(dev, "ref");
+ if (IS_ERR(samsung->ref_clk))
+ return dev_err_probe(dev, PTR_ERR(samsung->ref_clk),
+ "Failed to get reference clock\n");
+
+ samsung->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(samsung->pclk))
+ return dev_err_probe(dev, PTR_ERR(samsung->pclk), "Failed to get pclk\n");
+
+ samsung->m_phy_rst = devm_reset_control_get(dev, "m_phy");
+ if (IS_ERR(samsung->m_phy_rst))
+ return dev_err_probe(dev, PTR_ERR(samsung->m_phy_rst),
+ "Failed to get system m_phy_rst control\n");
+
+ samsung->s_phy_rst = devm_reset_control_get(dev, "s_phy");
+ if (IS_ERR(samsung->s_phy_rst))
+ return dev_err_probe(dev, PTR_ERR(samsung->s_phy_rst),
+ "Failed to get system s_phy_rst control\n");
+
+ samsung->apb_rst = devm_reset_control_get(dev, "apb");
+ if (IS_ERR(samsung->apb_rst))
+ return dev_err_probe(dev, PTR_ERR(samsung->apb_rst),
+ "Failed to get system apb_rst control\n");
+
+ samsung->grf_apb_rst = devm_reset_control_get(dev, "grf");
+ if (IS_ERR(samsung->grf_apb_rst))
+ return dev_err_probe(dev, PTR_ERR(samsung->grf_apb_rst),
+ "Failed to get system grf_apb_rst control\n");
+
+ samsung->phy = devm_phy_create(dev, NULL, &samsung_mipi_dcphy_ops);
+ if (IS_ERR(samsung->phy))
+ return dev_err_probe(dev, PTR_ERR(samsung->phy), "Failed to create MIPI DC-PHY\n");
+
+ phy_set_drvdata(samsung->phy, samsung);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
+
+ phy_provider = devm_of_phy_provider_register(dev, samsung_mipi_dcphy_xlate);
+ if (IS_ERR(phy_provider))
+ return dev_err_probe(dev, PTR_ERR(phy_provider),
+ "Failed to register phy provider\n");
+
+ return 0;
+}
+
+static __maybe_unused int samsung_mipi_dcphy_runtime_suspend(struct device *dev)
+{
+ struct samsung_mipi_dcphy *samsung = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(samsung->ref_clk);
+ clk_disable_unprepare(samsung->pclk);
+
+ return 0;
+}
+
+static __maybe_unused int samsung_mipi_dcphy_runtime_resume(struct device *dev)
+{
+ struct samsung_mipi_dcphy *samsung = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(samsung->pclk);
+ if (ret) {
+ dev_err(samsung->dev, "Failed to enable pclk, %d\n", ret);
+ return ret;
+ }
+
+ clk_prepare_enable(samsung->ref_clk);
+ if (ret) {
+ dev_err(samsung->dev, "Failed to enable reference clock, %d\n", ret);
+ clk_disable_unprepare(samsung->pclk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops samsung_mipi_dcphy_pm_ops = {
+ SET_RUNTIME_PM_OPS(samsung_mipi_dcphy_runtime_suspend,
+ samsung_mipi_dcphy_runtime_resume, NULL)
+};
+
+static const struct hs_drv_res_cfg rk3576_dphy_hs_drv_res_cfg = {
+ .clk_hs_drv_up_ohm = STRENGTH_52_OHM,
+ .clk_hs_drv_down_ohm = STRENGTH_52_OHM,
+ .data_hs_drv_up_ohm = STRENGTH_39_OHM,
+ .data_hs_drv_down_ohm = STRENGTH_39_OHM,
+};
+
+static const struct hs_drv_res_cfg rk3588_dphy_hs_drv_res_cfg = {
+ .clk_hs_drv_up_ohm = STRENGTH_34_OHM,
+ .clk_hs_drv_down_ohm = STRENGTH_34_OHM,
+ .data_hs_drv_up_ohm = STRENGTH_43_OHM,
+ .data_hs_drv_down_ohm = STRENGTH_43_OHM,
+};
+
+static const struct samsung_mipi_dcphy_plat_data rk3576_samsung_mipi_dcphy_plat_data = {
+ .dphy_hs_drv_res_cfg = &rk3576_dphy_hs_drv_res_cfg,
+ .dphy_tx_max_lane_kbps = 2500000L,
+};
+
+static const struct samsung_mipi_dcphy_plat_data rk3588_samsung_mipi_dcphy_plat_data = {
+ .dphy_hs_drv_res_cfg = &rk3588_dphy_hs_drv_res_cfg,
+ .dphy_tx_max_lane_kbps = 4500000L,
+};
+
+static const struct of_device_id samsung_mipi_dcphy_of_match[] = {
+ {
+ .compatible = "rockchip,rk3576-mipi-dcphy",
+ .data = &rk3576_samsung_mipi_dcphy_plat_data,
+ }, {
+ .compatible = "rockchip,rk3588-mipi-dcphy",
+ .data = &rk3588_samsung_mipi_dcphy_plat_data,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, samsung_mipi_dcphy_of_match);
+
+static struct platform_driver samsung_mipi_dcphy_driver = {
+ .driver = {
+ .name = "samsung-mipi-dcphy",
+ .of_match_table = samsung_mipi_dcphy_of_match,
+ .pm = &samsung_mipi_dcphy_pm_ops,
+ },
+ .probe = samsung_mipi_dcphy_probe,
+};
+module_platform_driver(samsung_mipi_dcphy_driver);
+
+MODULE_AUTHOR("Guochun Huang <hero.huang@rock-chips.com>");
+MODULE_DESCRIPTION("Samsung MIPI DCPHY Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 0965b9d4f9cf..fe7c05748356 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -25,6 +25,7 @@
#define HDPTX_I_PLL_EN BIT(7)
#define HDPTX_I_BIAS_EN BIT(6)
#define HDPTX_I_BGR_EN BIT(5)
+#define HDPTX_MODE_SEL BIT(0)
#define GRF_HDPTX_STATUS 0x80
#define HDPTX_O_PLL_LOCK_DONE BIT(3)
#define HDPTX_O_PHY_CLK_RDY BIT(2)
@@ -44,66 +45,130 @@
#define LANE_REG(n) HDTPX_REG(n, 0300, 062d)
/* CMN_REG(0008) */
+#define OVRD_LCPLL_EN_MASK BIT(7)
#define LCPLL_EN_MASK BIT(6)
#define LCPLL_LCVCO_MODE_EN_MASK BIT(4)
/* CMN_REG(001e) */
#define LCPLL_PI_EN_MASK BIT(5)
#define LCPLL_100M_CLK_EN_MASK BIT(0)
/* CMN_REG(0025) */
-#define LCPLL_PMS_IQDIV_RSTN BIT(4)
+#define LCPLL_PMS_IQDIV_RSTN_MASK BIT(4)
/* CMN_REG(0028) */
-#define LCPLL_SDC_FRAC_EN BIT(2)
-#define LCPLL_SDC_FRAC_RSTN BIT(0)
+#define LCPLL_SDC_FRAC_EN_MASK BIT(2)
+#define LCPLL_SDC_FRAC_RSTN_MASK BIT(0)
/* CMN_REG(002d) */
#define LCPLL_SDC_N_MASK GENMASK(3, 1)
/* CMN_REG(002e) */
#define LCPLL_SDC_NUMBERATOR_MASK GENMASK(5, 0)
/* CMN_REG(002f) */
#define LCPLL_SDC_DENOMINATOR_MASK GENMASK(7, 2)
-#define LCPLL_SDC_NDIV_RSTN BIT(0)
+#define LCPLL_SDC_NDIV_RSTN_MASK BIT(0)
+/* CMN_REG(003c) */
+#define ANA_LCPLL_RESERVED7_MASK BIT(7)
/* CMN_REG(003d) */
-#define ROPLL_LCVCO_EN BIT(4)
+#define OVRD_ROPLL_EN_MASK BIT(7)
+#define ROPLL_EN_MASK BIT(6)
+#define ROPLL_LCVCO_EN_MASK BIT(4)
+/* CMN_REG(0046) */
+#define ROPLL_ANA_CPP_CTRL_COARSE_MASK GENMASK(7, 4)
+#define ROPLL_ANA_CPP_CTRL_FINE_MASK GENMASK(3, 0)
+/* CMN_REG(0047) */
+#define ROPLL_ANA_LPF_C_SEL_COARSE_MASK GENMASK(5, 3)
+#define ROPLL_ANA_LPF_C_SEL_FINE_MASK GENMASK(2, 0)
/* CMN_REG(004e) */
-#define ROPLL_PI_EN BIT(5)
+#define ROPLL_PI_EN_MASK BIT(5)
+/* CMN_REG(0051) */
+#define ROPLL_PMS_MDIV_MASK GENMASK(7, 0)
+/* CMN_REG(0055) */
+#define ROPLL_PMS_MDIV_AFC_MASK GENMASK(7, 0)
+/* CMN_REG(0059) */
+#define ANA_ROPLL_PMS_PDIV_MASK GENMASK(7, 4)
+#define ANA_ROPLL_PMS_REFDIV_MASK GENMASK(3, 0)
+/* CMN_REG(005a) */
+#define ROPLL_PMS_SDIV_RBR_MASK GENMASK(7, 4)
+#define ROPLL_PMS_SDIV_HBR_MASK GENMASK(3, 0)
+/* CMN_REG(005b) */
+#define ROPLL_PMS_SDIV_HBR2_MASK GENMASK(7, 4)
/* CMN_REG(005c) */
-#define ROPLL_PMS_IQDIV_RSTN BIT(5)
+#define ROPLL_PMS_IQDIV_RSTN_MASK BIT(5)
/* CMN_REG(005e) */
#define ROPLL_SDM_EN_MASK BIT(6)
-#define ROPLL_SDM_FRAC_EN_RBR BIT(3)
-#define ROPLL_SDM_FRAC_EN_HBR BIT(2)
-#define ROPLL_SDM_FRAC_EN_HBR2 BIT(1)
-#define ROPLL_SDM_FRAC_EN_HBR3 BIT(0)
+#define OVRD_ROPLL_SDM_RSTN_MASK BIT(5)
+#define ROPLL_SDM_RSTN_MASK BIT(4)
+#define ROPLL_SDC_FRAC_EN_RBR_MASK BIT(3)
+#define ROPLL_SDC_FRAC_EN_HBR_MASK BIT(2)
+#define ROPLL_SDC_FRAC_EN_HBR2_MASK BIT(1)
+#define ROPLL_SDM_FRAC_EN_HBR3_MASK BIT(0)
+/* CMN_REG(005f) */
+#define OVRD_ROPLL_SDC_RSTN_MASK BIT(5)
+#define ROPLL_SDC_RSTN_MASK BIT(4)
+/* CMN_REG(0060) */
+#define ROPLL_SDM_DENOMINATOR_MASK GENMASK(7, 0)
/* CMN_REG(0064) */
#define ROPLL_SDM_NUM_SIGN_RBR_MASK BIT(3)
+#define ROPLL_SDM_NUM_SIGN_HBR_MASK BIT(2)
+#define ROPLL_SDM_NUM_SIGN_HBR2_MASK BIT(1)
+/* CMN_REG(0065) */
+#define ROPLL_SDM_NUM_MASK GENMASK(7, 0)
/* CMN_REG(0069) */
#define ROPLL_SDC_N_RBR_MASK GENMASK(2, 0)
+/* CMN_REG(006a) */
+#define ROPLL_SDC_N_HBR_MASK GENMASK(5, 3)
+#define ROPLL_SDC_N_HBR2_MASK GENMASK(2, 0)
+/* CMN_REG(006b) */
+#define ROPLL_SDC_N_HBR3_MASK GENMASK(3, 1)
+/* CMN_REG(006c) */
+#define ROPLL_SDC_NUM_MASK GENMASK(5, 0)
+/* cmn_reg0070 */
+#define ROPLL_SDC_DENO_MASK GENMASK(5, 0)
/* CMN_REG(0074) */
-#define ROPLL_SDC_NDIV_RSTN BIT(2)
-#define ROPLL_SSC_EN BIT(0)
+#define OVRD_ROPLL_SDC_NDIV_RSTN_MASK BIT(3)
+#define ROPLL_SDC_NDIV_RSTN_MASK BIT(2)
+#define OVRD_ROPLL_SSC_EN_MASK BIT(1)
+#define ROPLL_SSC_EN_MASK BIT(0)
+/* CMN_REG(0075) */
+#define ANA_ROPLL_SSC_FM_DEVIATION_MASK GENMASK(5, 0)
+/* CMN_REG(0076) */
+#define ANA_ROPLL_SSC_FM_FREQ_MASK GENMASK(6, 2)
+/* CMN_REG(0077) */
+#define ANA_ROPLL_SSC_CLK_DIV_SEL_MASK GENMASK(6, 3)
/* CMN_REG(0081) */
-#define OVRD_PLL_CD_CLK_EN BIT(8)
-#define PLL_CD_HSCLK_EAST_EN BIT(0)
+#define OVRD_PLL_CD_CLK_EN_MASK BIT(8)
+#define ANA_PLL_CD_TX_SER_RATE_SEL_MASK BIT(3)
+#define ANA_PLL_CD_HSCLK_WEST_EN_MASK BIT(1)
+#define ANA_PLL_CD_HSCLK_EAST_EN_MASK BIT(0)
+/* CMN_REG(0082) */
+#define ANA_PLL_CD_VREG_GAIN_CTRL_MASK GENMASK(3, 0)
+/* CMN_REG(0083) */
+#define ANA_PLL_CD_VREG_ICTRL_MASK GENMASK(6, 5)
+/* CMN_REG(0084) */
+#define PLL_LCRO_CLK_SEL_MASK BIT(5)
+/* CMN_REG(0085) */
+#define ANA_PLL_SYNC_LOSS_DET_MODE_MASK GENMASK(1, 0)
/* CMN_REG(0086) */
#define PLL_PCG_POSTDIV_SEL_MASK GENMASK(7, 4)
#define PLL_PCG_CLK_SEL_MASK GENMASK(3, 1)
-#define PLL_PCG_CLK_EN BIT(0)
+#define PLL_PCG_CLK_EN_MASK BIT(0)
/* CMN_REG(0087) */
-#define PLL_FRL_MODE_EN BIT(3)
-#define PLL_TX_HS_CLK_EN BIT(2)
+#define ANA_PLL_FRL_MODE_EN_MASK BIT(3)
+#define ANA_PLL_TX_HS_CLK_EN_MASK BIT(2)
/* CMN_REG(0089) */
-#define LCPLL_ALONE_MODE BIT(1)
+#define LCPLL_ALONE_MODE_MASK BIT(1)
+/* CMN_REG(0095) */
+#define DP_TX_LINK_BW_MASK GENMASK(1, 0)
/* CMN_REG(0097) */
-#define DIG_CLK_SEL BIT(1)
-#define ROPLL_REF BIT(1)
-#define LCPLL_REF 0
+#define DIG_CLK_SEL_MASK BIT(1)
+#define LCPLL_REF BIT(1)
+#define ROPLL_REF 0
/* CMN_REG(0099) */
-#define CMN_ROPLL_ALONE_MODE BIT(2)
+#define SSC_EN_MASK GENMASK(7, 6)
+#define CMN_ROPLL_ALONE_MODE_MASK BIT(2)
#define ROPLL_ALONE_MODE BIT(2)
/* CMN_REG(009a) */
-#define HS_SPEED_SEL BIT(0)
+#define HS_SPEED_SEL_MASK BIT(0)
#define DIV_10_CLOCK BIT(0)
/* CMN_REG(009b) */
-#define IS_SPEED_SEL BIT(4)
+#define LS_SPEED_SEL_MASK BIT(4)
#define LINK_SYMBOL_CLOCK BIT(4)
#define LINK_SYMBOL_CLOCK1_2 0
@@ -118,6 +183,8 @@
/* SB_REG(0104) */
#define OVRD_SB_EN_MASK BIT(5)
#define SB_EN_MASK BIT(4)
+#define OVRD_SB_AUX_EN_MASK BIT(1)
+#define SB_AUX_EN_MASK BIT(0)
/* SB_REG(0105) */
#define OVRD_SB_EARC_CMDC_EN_MASK BIT(6)
#define SB_EARC_CMDC_EN_MASK BIT(5)
@@ -126,6 +193,8 @@
#define ANA_SB_TX_LLVL_PROG_MASK GENMASK(6, 4)
/* SB_REG(0109) */
#define ANA_SB_DMRX_AFC_DIV_RATIO_MASK GENMASK(2, 0)
+/* SB_REG(010d) */
+#define ANA_SB_DMRX_LPBK_DATA_MASK BIT(4)
/* SB_REG(010f) */
#define OVRD_SB_VREG_EN_MASK BIT(7)
#define SB_VREG_EN_MASK BIT(6)
@@ -133,6 +202,7 @@
#define SB_VREG_LPF_BYPASS_MASK BIT(4)
#define ANA_SB_VREG_GAIN_CTRL_MASK GENMASK(3, 0)
/* SB_REG(0110) */
+#define ANA_SB_VREG_OUT_SEL_MASK BIT(1)
#define ANA_SB_VREG_REF_SEL_MASK BIT(0)
/* SB_REG(0113) */
#define SB_RX_RCAL_OPT_CODE_MASK GENMASK(5, 4)
@@ -147,13 +217,24 @@
#define AFC_RSTN_DELAY_TIME_MASK GENMASK(6, 4)
/* SB_REG(0117) */
#define FAST_PULSE_TIME_MASK GENMASK(3, 0)
+/* SB_REG(0118) */
+#define SB_TG_EARC_DMRX_RECVRD_CLK_CNT_MASK GENMASK(7, 0)
+/* SB_REG(011a) */
+#define SB_TG_CNT_RUN_NO_7_0_MASK GENMASK(7, 0)
/* SB_REG(011b) */
#define SB_EARC_SIG_DET_BYPASS_MASK BIT(4)
#define SB_AFC_TOL_MASK GENMASK(3, 0)
+/* SB_REG(011c) */
+#define SB_AFC_STB_NUM_MASK GENMASK(3, 0)
+/* SB_REG(011d) */
+#define SB_TG_OSC_CNT_MIN_MASK GENMASK(7, 0)
+/* SB_REG(011e) */
+#define SB_TG_OSC_CNT_MAX_MASK GENMASK(7, 0)
/* SB_REG(011f) */
#define SB_PWM_AFC_CTRL_MASK GENMASK(7, 2)
#define SB_RCAL_RSTN_MASK BIT(1)
/* SB_REG(0120) */
+#define SB_AUX_EN_IN_MASK BIT(7)
#define SB_EARC_EN_MASK BIT(1)
#define SB_EARC_AFC_EN_MASK BIT(2)
/* SB_REG(0123) */
@@ -161,39 +242,92 @@
#define SB_READY_MASK BIT(4)
/* LNTOP_REG(0200) */
-#define PROTOCOL_SEL BIT(2)
+#define PROTOCOL_SEL_MASK BIT(2)
#define HDMI_MODE BIT(2)
#define HDMI_TMDS_FRL_SEL BIT(1)
/* LNTOP_REG(0206) */
-#define DATA_BUS_SEL BIT(0)
+#define DATA_BUS_WIDTH_MASK GENMASK(2, 1)
+#define DATA_BUS_WIDTH_SEL_MASK BIT(0)
#define DATA_BUS_36_40 BIT(0)
/* LNTOP_REG(0207) */
-#define LANE_EN 0xf
+#define LANE_EN_MASK 0xf
#define ALL_LANE_EN 0xf
+/* LANE_REG(0301) */
+#define OVRD_LN_TX_DRV_EI_EN_MASK BIT(7)
+#define LN_TX_DRV_EI_EN_MASK BIT(6)
+/* LANE_REG(0303) */
+#define OVRD_LN_TX_DRV_LVL_CTRL_MASK BIT(5)
+#define LN_TX_DRV_LVL_CTRL_MASK GENMASK(4, 0)
+/* LANE_REG(0304) */
+#define OVRD_LN_TX_DRV_POST_LVL_CTRL_MASK BIT(4)
+#define LN_TX_DRV_POST_LVL_CTRL_MASK GENMASK(3, 0)
+/* LANE_REG(0305) */
+#define OVRD_LN_TX_DRV_PRE_LVL_CTRL_MASK BIT(6)
+#define LN_TX_DRV_PRE_LVL_CTRL_MASK GENMASK(5, 2)
+/* LANE_REG(0306) */
+#define LN_ANA_TX_DRV_IDRV_IDN_CTRL_MASK GENMASK(7, 5)
+#define LN_ANA_TX_DRV_IDRV_IUP_CTRL_MASK GENMASK(4, 2)
+#define LN_ANA_TX_DRV_ACCDRV_EN_MASK BIT(0)
+/* LANE_REG(0307) */
+#define LN_ANA_TX_DRV_ACCDRV_POL_SEL_MASK BIT(6)
+#define LN_ANA_TX_DRV_ACCDRV_CTRL_MASK GENMASK(5, 3)
+/* LANE_REG(030a) */
+#define LN_ANA_TX_JEQ_EN_MASK BIT(4)
+#define LN_TX_JEQ_EVEN_CTRL_RBR_MASK GENMASK(3, 0)
+/* LANE_REG(030b) */
+#define LN_TX_JEQ_EVEN_CTRL_HBR_MASK GENMASK(7, 4)
+#define LN_TX_JEQ_EVEN_CTRL_HBR2_MASK GENMASK(3, 0)
+/* LANE_REG(030c) */
+#define LN_TX_JEQ_ODD_CTRL_RBR_MASK GENMASK(3, 0)
+/* LANE_REG(030d) */
+#define LN_TX_JEQ_ODD_CTRL_HBR_MASK GENMASK(7, 4)
+#define LN_TX_JEQ_ODD_CTRL_HBR2_MASK GENMASK(3, 0)
+/* LANE_REG(0310) */
+#define LN_ANA_TX_SYNC_LOSS_DET_MODE_MASK GENMASK(1, 0)
+/* LANE_REG(0311) */
+#define LN_TX_SER_40BIT_EN_RBR_MASK BIT(3)
+#define LN_TX_SER_40BIT_EN_HBR_MASK BIT(2)
+#define LN_TX_SER_40BIT_EN_HBR2_MASK BIT(1)
/* LANE_REG(0312) */
-#define LN0_TX_SER_RATE_SEL_RBR BIT(5)
-#define LN0_TX_SER_RATE_SEL_HBR BIT(4)
-#define LN0_TX_SER_RATE_SEL_HBR2 BIT(3)
-#define LN0_TX_SER_RATE_SEL_HBR3 BIT(2)
+#define LN0_TX_SER_RATE_SEL_RBR_MASK BIT(5)
+#define LN0_TX_SER_RATE_SEL_HBR_MASK BIT(4)
+#define LN0_TX_SER_RATE_SEL_HBR2_MASK BIT(3)
+#define LN0_TX_SER_RATE_SEL_HBR3_MASK BIT(2)
+/* LANE_REG(0316) */
+#define LN_ANA_TX_SER_VREG_GAIN_CTRL_MASK GENMASK(3, 0)
+/* LANE_REG(031B) */
+#define LN_ANA_TX_RESERVED_MASK GENMASK(7, 0)
+/* LANE_REG(031e) */
+#define LN_POLARITY_INV_MASK BIT(2)
+#define LN_LANE_MODE_MASK BIT(1)
+
/* LANE_REG(0412) */
-#define LN1_TX_SER_RATE_SEL_RBR BIT(5)
-#define LN1_TX_SER_RATE_SEL_HBR BIT(4)
-#define LN1_TX_SER_RATE_SEL_HBR2 BIT(3)
-#define LN1_TX_SER_RATE_SEL_HBR3 BIT(2)
+#define LN1_TX_SER_RATE_SEL_RBR_MASK BIT(5)
+#define LN1_TX_SER_RATE_SEL_HBR_MASK BIT(4)
+#define LN1_TX_SER_RATE_SEL_HBR2_MASK BIT(3)
+#define LN1_TX_SER_RATE_SEL_HBR3_MASK BIT(2)
+
/* LANE_REG(0512) */
-#define LN2_TX_SER_RATE_SEL_RBR BIT(5)
-#define LN2_TX_SER_RATE_SEL_HBR BIT(4)
-#define LN2_TX_SER_RATE_SEL_HBR2 BIT(3)
-#define LN2_TX_SER_RATE_SEL_HBR3 BIT(2)
+#define LN2_TX_SER_RATE_SEL_RBR_MASK BIT(5)
+#define LN2_TX_SER_RATE_SEL_HBR_MASK BIT(4)
+#define LN2_TX_SER_RATE_SEL_HBR2_MASK BIT(3)
+#define LN2_TX_SER_RATE_SEL_HBR3_MASK BIT(2)
+
/* LANE_REG(0612) */
-#define LN3_TX_SER_RATE_SEL_RBR BIT(5)
-#define LN3_TX_SER_RATE_SEL_HBR BIT(4)
-#define LN3_TX_SER_RATE_SEL_HBR2 BIT(3)
-#define LN3_TX_SER_RATE_SEL_HBR3 BIT(2)
+#define LN3_TX_SER_RATE_SEL_RBR_MASK BIT(5)
+#define LN3_TX_SER_RATE_SEL_HBR_MASK BIT(4)
+#define LN3_TX_SER_RATE_SEL_HBR2_MASK BIT(3)
+#define LN3_TX_SER_RATE_SEL_HBR3_MASK BIT(2)
#define HDMI20_MAX_RATE 600000000
+enum dp_link_rate {
+ DP_BW_RBR,
+ DP_BW_HBR,
+ DP_BW_HBR2,
+};
+
struct lcpll_config {
u32 bit_rate;
u8 lcvco_mode_en;
@@ -255,6 +389,19 @@ struct ropll_config {
u8 cd_tx_ser_rate_sel;
};
+struct tx_drv_ctrl {
+ u8 tx_drv_lvl_ctrl;
+ u8 tx_drv_post_lvl_ctrl;
+ u8 ana_tx_drv_idrv_idn_ctrl;
+ u8 ana_tx_drv_idrv_iup_ctrl;
+ u8 ana_tx_drv_accdrv_en;
+ u8 ana_tx_drv_accdrv_ctrl;
+ u8 tx_drv_pre_lvl_ctrl;
+ u8 ana_tx_jeq_en;
+ u8 tx_jeq_even_ctrl;
+ u8 tx_jeq_odd_ctrl;
+};
+
enum rk_hdptx_reset {
RST_APB = 0,
RST_INIT,
@@ -263,11 +410,22 @@ enum rk_hdptx_reset {
RST_MAX
};
+#define MAX_HDPTX_PHY_NUM 2
+
+struct rk_hdptx_phy_cfg {
+ unsigned int num_phys;
+ unsigned int phy_ids[MAX_HDPTX_PHY_NUM];
+};
+
struct rk_hdptx_phy {
struct device *dev;
struct regmap *regmap;
struct regmap *grf;
+ /* PHY const config */
+ const struct rk_hdptx_phy_cfg *cfgs;
+ int phy_id;
+
struct phy *phy;
struct phy_config *phy_cfg;
struct clk_bulk_data *clks;
@@ -279,6 +437,10 @@ struct rk_hdptx_phy {
unsigned long rate;
atomic_t usage_count;
+
+ /* used for dp mode */
+ unsigned int link_rate;
+ unsigned int lanes;
};
static const struct ropll_config ropll_tmds_cfg[] = {
@@ -557,16 +719,100 @@ static const struct reg_sequence rk_hdtpx_tmds_lane_init_seq[] = {
REG_SEQ0(LANE_REG(0606), 0x1c),
};
+static struct tx_drv_ctrl tx_drv_ctrl_rbr[4][4] = {
+ /* voltage swing 0, pre-emphasis 0->3 */
+ {
+ { 0x2, 0x0, 0x4, 0x6, 0x0, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ { 0x4, 0x3, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0x7, 0x6, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0xd, 0xc, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 1, pre-emphasis 0->2 */
+ {
+ { 0x4, 0x0, 0x4, 0x6, 0x0, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ { 0x9, 0x5, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0xc, 0x8, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 2, pre-emphasis 0->1 */
+ {
+ { 0x8, 0x0, 0x4, 0x6, 0x0, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ { 0xc, 0x5, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 3, pre-emphasis 0 */
+ {
+ { 0xb, 0x0, 0x7, 0x7, 0x1, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ }
+};
+
+static struct tx_drv_ctrl tx_drv_ctrl_hbr[4][4] = {
+ /* voltage swing 0, pre-emphasis 0->3 */
+ {
+ { 0x2, 0x0, 0x4, 0x6, 0x0, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ { 0x5, 0x4, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0x9, 0x8, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0xd, 0xc, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 1, pre-emphasis 0->2 */
+ {
+ { 0x6, 0x1, 0x4, 0x6, 0x0, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ { 0xa, 0x6, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0xc, 0x8, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 2, pre-emphasis 0->1 */
+ {
+ { 0x9, 0x1, 0x4, 0x6, 0x0, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ { 0xd, 0x6, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 3, pre-emphasis 0 */
+ {
+ { 0xc, 0x1, 0x7, 0x7, 0x1, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ }
+};
+
+static struct tx_drv_ctrl tx_drv_ctrl_hbr2[4][4] = {
+ /* voltage swing 0, pre-emphasis 0->3 */
+ {
+ { 0x2, 0x1, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0x5, 0x4, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0x9, 0x8, 0x4, 0x6, 0x1, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0xd, 0xc, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 1, pre-emphasis 0->2 */
+ {
+ { 0x6, 0x1, 0x4, 0x6, 0x0, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ { 0xb, 0x7, 0x4, 0x6, 0x0, 0x4, 0x0, 0x1, 0x7, 0x7 },
+ { 0xd, 0x9, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 2, pre-emphasis 0->1 */
+ {
+ { 0x8, 0x1, 0x4, 0x6, 0x0, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ { 0xc, 0x6, 0x7, 0x7, 0x1, 0x7, 0x0, 0x1, 0x7, 0x7 },
+ },
+
+ /* voltage swing 3, pre-emphasis 0 */
+ {
+ { 0xb, 0x0, 0x7, 0x7, 0x1, 0x4, 0x1, 0x1, 0x7, 0x7 },
+ }
+};
+
static bool rk_hdptx_phy_is_rw_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
- case 0x0000 ... 0x029c:
- case 0x0400 ... 0x04a4:
- case 0x0800 ... 0x08a4:
- case 0x0c00 ... 0x0cb4:
- case 0x1000 ... 0x10b4:
- case 0x1400 ... 0x14b4:
- case 0x1800 ... 0x18b4:
+ case 0x0000 ... 0x029c: /* CMN Register */
+ case 0x0400 ... 0x04a4: /* Sideband Register */
+ case 0x0800 ... 0x08a4: /* Lane Top Register */
+ case 0x0c00 ... 0x0cb4: /* Lane 0 Register */
+ case 0x1000 ... 0x10b4: /* Lane 1 Register */
+ case 0x1400 ... 0x14b4: /* Lane 2 Register */
+ case 0x1800 ... 0x18b4: /* Lane 3 Register */
return true;
}
@@ -813,8 +1059,8 @@ static int rk_hdptx_ropll_tmds_cmn_config(struct rk_hdptx_phy *hdptx,
regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_POSTDIV_SEL_MASK,
FIELD_PREP(PLL_PCG_POSTDIV_SEL_MASK, cfg->pms_sdiv));
- regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_EN,
- PLL_PCG_CLK_EN);
+ regmap_update_bits(hdptx->regmap, CMN_REG(0086), PLL_PCG_CLK_EN_MASK,
+ FIELD_PREP(PLL_PCG_CLK_EN_MASK, 0x1));
return rk_hdptx_post_enable_pll(hdptx);
}
@@ -843,9 +1089,45 @@ static int rk_hdptx_ropll_tmds_mode_config(struct rk_hdptx_phy *hdptx,
return rk_hdptx_post_enable_lane(hdptx);
}
+static void rk_hdptx_dp_reset(struct rk_hdptx_phy *hdptx)
+{
+ reset_control_assert(hdptx->rsts[RST_LANE].rstc);
+ reset_control_assert(hdptx->rsts[RST_CMN].rstc);
+ reset_control_assert(hdptx->rsts[RST_INIT].rstc);
+
+ reset_control_assert(hdptx->rsts[RST_APB].rstc);
+ udelay(10);
+ reset_control_deassert(hdptx->rsts[RST_APB].rstc);
+
+ regmap_update_bits(hdptx->regmap, LANE_REG(0301),
+ OVRD_LN_TX_DRV_EI_EN_MASK | LN_TX_DRV_EI_EN_MASK,
+ FIELD_PREP(OVRD_LN_TX_DRV_EI_EN_MASK, 1) |
+ FIELD_PREP(LN_TX_DRV_EI_EN_MASK, 0));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0401),
+ OVRD_LN_TX_DRV_EI_EN_MASK | LN_TX_DRV_EI_EN_MASK,
+ FIELD_PREP(OVRD_LN_TX_DRV_EI_EN_MASK, 1) |
+ FIELD_PREP(LN_TX_DRV_EI_EN_MASK, 0));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0501),
+ OVRD_LN_TX_DRV_EI_EN_MASK | LN_TX_DRV_EI_EN_MASK,
+ FIELD_PREP(OVRD_LN_TX_DRV_EI_EN_MASK, 1) |
+ FIELD_PREP(LN_TX_DRV_EI_EN_MASK, 0));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0601),
+ OVRD_LN_TX_DRV_EI_EN_MASK | LN_TX_DRV_EI_EN_MASK,
+ FIELD_PREP(OVRD_LN_TX_DRV_EI_EN_MASK, 1) |
+ FIELD_PREP(LN_TX_DRV_EI_EN_MASK, 0));
+
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x0));
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_I_BIAS_EN << 16 | FIELD_PREP(HDPTX_I_BIAS_EN, 0x0));
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_I_BGR_EN << 16 | FIELD_PREP(HDPTX_I_BGR_EN, 0x0));
+}
+
static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx,
unsigned int rate)
{
+ enum phy_mode mode = phy_get_mode(hdptx->phy);
u32 status;
int ret;
@@ -859,10 +1141,14 @@ static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx,
if (status & HDPTX_O_PLL_LOCK_DONE)
dev_warn(hdptx->dev, "PLL locked by unknown consumer!\n");
- if (rate) {
- ret = rk_hdptx_ropll_tmds_cmn_config(hdptx, rate);
- if (ret)
- goto dec_usage;
+ if (mode == PHY_MODE_DP) {
+ rk_hdptx_dp_reset(hdptx);
+ } else {
+ if (rate) {
+ ret = rk_hdptx_ropll_tmds_cmn_config(hdptx, rate);
+ if (ret)
+ goto dec_usage;
+ }
}
return 0;
@@ -874,6 +1160,7 @@ dec_usage:
static int rk_hdptx_phy_consumer_put(struct rk_hdptx_phy *hdptx, bool force)
{
+ enum phy_mode mode = phy_get_mode(hdptx->phy);
u32 status;
int ret;
@@ -887,8 +1174,12 @@ static int rk_hdptx_phy_consumer_put(struct rk_hdptx_phy *hdptx, bool force)
} else {
ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &status);
if (!ret) {
- if (status & HDPTX_O_PLL_LOCK_DONE)
- rk_hdptx_phy_disable(hdptx);
+ if (status & HDPTX_O_PLL_LOCK_DONE) {
+ if (mode == PHY_MODE_DP)
+ rk_hdptx_dp_reset(hdptx);
+ else
+ rk_hdptx_phy_disable(hdptx);
+ }
return 0;
} else if (force) {
return 0;
@@ -899,11 +1190,262 @@ static int rk_hdptx_phy_consumer_put(struct rk_hdptx_phy *hdptx, bool force)
return ret;
}
+static void rk_hdptx_dp_pll_init(struct rk_hdptx_phy *hdptx)
+{
+ regmap_update_bits(hdptx->regmap, CMN_REG(003c), ANA_LCPLL_RESERVED7_MASK,
+ FIELD_PREP(ANA_LCPLL_RESERVED7_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0046),
+ ROPLL_ANA_CPP_CTRL_COARSE_MASK | ROPLL_ANA_CPP_CTRL_FINE_MASK,
+ FIELD_PREP(ROPLL_ANA_CPP_CTRL_COARSE_MASK, 0xe) |
+ FIELD_PREP(ROPLL_ANA_CPP_CTRL_FINE_MASK, 0xe));
+ regmap_update_bits(hdptx->regmap, CMN_REG(0047),
+ ROPLL_ANA_LPF_C_SEL_COARSE_MASK |
+ ROPLL_ANA_LPF_C_SEL_FINE_MASK,
+ FIELD_PREP(ROPLL_ANA_LPF_C_SEL_COARSE_MASK, 0x4) |
+ FIELD_PREP(ROPLL_ANA_LPF_C_SEL_FINE_MASK, 0x4));
+
+ regmap_write(hdptx->regmap, CMN_REG(0051), FIELD_PREP(ROPLL_PMS_MDIV_MASK, 0x87));
+ regmap_write(hdptx->regmap, CMN_REG(0052), FIELD_PREP(ROPLL_PMS_MDIV_MASK, 0x71));
+ regmap_write(hdptx->regmap, CMN_REG(0053), FIELD_PREP(ROPLL_PMS_MDIV_MASK, 0x71));
+
+ regmap_write(hdptx->regmap, CMN_REG(0055),
+ FIELD_PREP(ROPLL_PMS_MDIV_AFC_MASK, 0x87));
+ regmap_write(hdptx->regmap, CMN_REG(0056),
+ FIELD_PREP(ROPLL_PMS_MDIV_AFC_MASK, 0x71));
+ regmap_write(hdptx->regmap, CMN_REG(0057),
+ FIELD_PREP(ROPLL_PMS_MDIV_AFC_MASK, 0x71));
+
+ regmap_write(hdptx->regmap, CMN_REG(0059),
+ FIELD_PREP(ANA_ROPLL_PMS_PDIV_MASK, 0x1) |
+ FIELD_PREP(ANA_ROPLL_PMS_REFDIV_MASK, 0x1));
+ regmap_write(hdptx->regmap, CMN_REG(005a),
+ FIELD_PREP(ROPLL_PMS_SDIV_RBR_MASK, 0x3) |
+ FIELD_PREP(ROPLL_PMS_SDIV_HBR_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(005b), ROPLL_PMS_SDIV_HBR2_MASK,
+ FIELD_PREP(ROPLL_PMS_SDIV_HBR2_MASK, 0x0));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(005e), ROPLL_SDM_EN_MASK,
+ FIELD_PREP(ROPLL_SDM_EN_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(005e),
+ OVRD_ROPLL_SDM_RSTN_MASK | ROPLL_SDM_RSTN_MASK,
+ FIELD_PREP(OVRD_ROPLL_SDM_RSTN_MASK, 0x1) |
+ FIELD_PREP(ROPLL_SDM_RSTN_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(005e), ROPLL_SDC_FRAC_EN_RBR_MASK,
+ FIELD_PREP(ROPLL_SDC_FRAC_EN_RBR_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(005e), ROPLL_SDC_FRAC_EN_HBR_MASK,
+ FIELD_PREP(ROPLL_SDC_FRAC_EN_HBR_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(005e), ROPLL_SDC_FRAC_EN_HBR2_MASK,
+ FIELD_PREP(ROPLL_SDC_FRAC_EN_HBR2_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(005f),
+ OVRD_ROPLL_SDC_RSTN_MASK | ROPLL_SDC_RSTN_MASK,
+ FIELD_PREP(OVRD_ROPLL_SDC_RSTN_MASK, 0x1) |
+ FIELD_PREP(ROPLL_SDC_RSTN_MASK, 0x1));
+ regmap_write(hdptx->regmap, CMN_REG(0060),
+ FIELD_PREP(ROPLL_SDM_DENOMINATOR_MASK, 0x21));
+ regmap_write(hdptx->regmap, CMN_REG(0061),
+ FIELD_PREP(ROPLL_SDM_DENOMINATOR_MASK, 0x27));
+ regmap_write(hdptx->regmap, CMN_REG(0062),
+ FIELD_PREP(ROPLL_SDM_DENOMINATOR_MASK, 0x27));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0064),
+ ROPLL_SDM_NUM_SIGN_RBR_MASK |
+ ROPLL_SDM_NUM_SIGN_HBR_MASK |
+ ROPLL_SDM_NUM_SIGN_HBR2_MASK,
+ FIELD_PREP(ROPLL_SDM_NUM_SIGN_RBR_MASK, 0x0) |
+ FIELD_PREP(ROPLL_SDM_NUM_SIGN_HBR_MASK, 0x1) |
+ FIELD_PREP(ROPLL_SDM_NUM_SIGN_HBR2_MASK, 0x1));
+ regmap_write(hdptx->regmap, CMN_REG(0065),
+ FIELD_PREP(ROPLL_SDM_NUM_MASK, 0x0));
+ regmap_write(hdptx->regmap, CMN_REG(0066),
+ FIELD_PREP(ROPLL_SDM_NUM_MASK, 0xd));
+ regmap_write(hdptx->regmap, CMN_REG(0067),
+ FIELD_PREP(ROPLL_SDM_NUM_MASK, 0xd));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0069), ROPLL_SDC_N_RBR_MASK,
+ FIELD_PREP(ROPLL_SDC_N_RBR_MASK, 0x2));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(006a),
+ ROPLL_SDC_N_HBR_MASK | ROPLL_SDC_N_HBR2_MASK,
+ FIELD_PREP(ROPLL_SDC_N_HBR_MASK, 0x1) |
+ FIELD_PREP(ROPLL_SDC_N_HBR2_MASK, 0x1));
+
+ regmap_write(hdptx->regmap, CMN_REG(006c),
+ FIELD_PREP(ROPLL_SDC_NUM_MASK, 0x3));
+ regmap_write(hdptx->regmap, CMN_REG(006d),
+ FIELD_PREP(ROPLL_SDC_NUM_MASK, 0x7));
+ regmap_write(hdptx->regmap, CMN_REG(006e),
+ FIELD_PREP(ROPLL_SDC_NUM_MASK, 0x7));
+
+ regmap_write(hdptx->regmap, CMN_REG(0070),
+ FIELD_PREP(ROPLL_SDC_DENO_MASK, 0x8));
+ regmap_write(hdptx->regmap, CMN_REG(0071),
+ FIELD_PREP(ROPLL_SDC_DENO_MASK, 0x18));
+ regmap_write(hdptx->regmap, CMN_REG(0072),
+ FIELD_PREP(ROPLL_SDC_DENO_MASK, 0x18));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0074),
+ OVRD_ROPLL_SDC_NDIV_RSTN_MASK | ROPLL_SDC_NDIV_RSTN_MASK,
+ FIELD_PREP(OVRD_ROPLL_SDC_NDIV_RSTN_MASK, 0x1) |
+ FIELD_PREP(ROPLL_SDC_NDIV_RSTN_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0077), ANA_ROPLL_SSC_CLK_DIV_SEL_MASK,
+ FIELD_PREP(ANA_ROPLL_SSC_CLK_DIV_SEL_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0081), ANA_PLL_CD_TX_SER_RATE_SEL_MASK,
+ FIELD_PREP(ANA_PLL_CD_TX_SER_RATE_SEL_MASK, 0x0));
+ regmap_update_bits(hdptx->regmap, CMN_REG(0081),
+ ANA_PLL_CD_HSCLK_EAST_EN_MASK | ANA_PLL_CD_HSCLK_WEST_EN_MASK,
+ FIELD_PREP(ANA_PLL_CD_HSCLK_EAST_EN_MASK, 0x1) |
+ FIELD_PREP(ANA_PLL_CD_HSCLK_WEST_EN_MASK, 0x0));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0082), ANA_PLL_CD_VREG_GAIN_CTRL_MASK,
+ FIELD_PREP(ANA_PLL_CD_VREG_GAIN_CTRL_MASK, 0x4));
+ regmap_update_bits(hdptx->regmap, CMN_REG(0083), ANA_PLL_CD_VREG_ICTRL_MASK,
+ FIELD_PREP(ANA_PLL_CD_VREG_ICTRL_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(0084), PLL_LCRO_CLK_SEL_MASK,
+ FIELD_PREP(PLL_LCRO_CLK_SEL_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(0085), ANA_PLL_SYNC_LOSS_DET_MODE_MASK,
+ FIELD_PREP(ANA_PLL_SYNC_LOSS_DET_MODE_MASK, 0x3));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0087), ANA_PLL_TX_HS_CLK_EN_MASK,
+ FIELD_PREP(ANA_PLL_TX_HS_CLK_EN_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0097), DIG_CLK_SEL_MASK,
+ FIELD_PREP(DIG_CLK_SEL_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0099), CMN_ROPLL_ALONE_MODE_MASK,
+ FIELD_PREP(CMN_ROPLL_ALONE_MODE_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(009a), HS_SPEED_SEL_MASK,
+ FIELD_PREP(HS_SPEED_SEL_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, CMN_REG(009b), LS_SPEED_SEL_MASK,
+ FIELD_PREP(LS_SPEED_SEL_MASK, 0x1));
+}
+
+static int rk_hdptx_dp_aux_init(struct rk_hdptx_phy *hdptx)
+{
+ u32 status;
+ int ret;
+
+ regmap_update_bits(hdptx->regmap, SB_REG(0102), ANA_SB_RXTERM_OFFSP_MASK,
+ FIELD_PREP(ANA_SB_RXTERM_OFFSP_MASK, 0x3));
+ regmap_update_bits(hdptx->regmap, SB_REG(0103), ANA_SB_RXTERM_OFFSN_MASK,
+ FIELD_PREP(ANA_SB_RXTERM_OFFSN_MASK, 0x3));
+ regmap_update_bits(hdptx->regmap, SB_REG(0104), SB_AUX_EN_MASK,
+ FIELD_PREP(SB_AUX_EN_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, SB_REG(0105), ANA_SB_TX_HLVL_PROG_MASK,
+ FIELD_PREP(ANA_SB_TX_HLVL_PROG_MASK, 0x7));
+ regmap_update_bits(hdptx->regmap, SB_REG(0106), ANA_SB_TX_LLVL_PROG_MASK,
+ FIELD_PREP(ANA_SB_TX_LLVL_PROG_MASK, 0x7));
+
+ regmap_update_bits(hdptx->regmap, SB_REG(010d), ANA_SB_DMRX_LPBK_DATA_MASK,
+ FIELD_PREP(ANA_SB_DMRX_LPBK_DATA_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, SB_REG(010f), ANA_SB_VREG_GAIN_CTRL_MASK,
+ FIELD_PREP(ANA_SB_VREG_GAIN_CTRL_MASK, 0x0));
+ regmap_update_bits(hdptx->regmap, SB_REG(0110),
+ ANA_SB_VREG_OUT_SEL_MASK | ANA_SB_VREG_REF_SEL_MASK,
+ FIELD_PREP(ANA_SB_VREG_OUT_SEL_MASK, 0x1) |
+ FIELD_PREP(ANA_SB_VREG_REF_SEL_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, SB_REG(0113),
+ SB_RX_RCAL_OPT_CODE_MASK | SB_RX_RTERM_CTRL_MASK,
+ FIELD_PREP(SB_RX_RCAL_OPT_CODE_MASK, 0x1) |
+ FIELD_PREP(SB_RX_RTERM_CTRL_MASK, 0x3));
+ regmap_update_bits(hdptx->regmap, SB_REG(0114),
+ SB_TG_SB_EN_DELAY_TIME_MASK | SB_TG_RXTERM_EN_DELAY_TIME_MASK,
+ FIELD_PREP(SB_TG_SB_EN_DELAY_TIME_MASK, 0x2) |
+ FIELD_PREP(SB_TG_RXTERM_EN_DELAY_TIME_MASK, 0x2));
+ regmap_update_bits(hdptx->regmap, SB_REG(0115),
+ SB_READY_DELAY_TIME_MASK | SB_TG_OSC_EN_DELAY_TIME_MASK,
+ FIELD_PREP(SB_READY_DELAY_TIME_MASK, 0x2) |
+ FIELD_PREP(SB_TG_OSC_EN_DELAY_TIME_MASK, 0x2));
+ regmap_update_bits(hdptx->regmap, SB_REG(0116),
+ AFC_RSTN_DELAY_TIME_MASK,
+ FIELD_PREP(AFC_RSTN_DELAY_TIME_MASK, 0x2));
+ regmap_update_bits(hdptx->regmap, SB_REG(0117),
+ FAST_PULSE_TIME_MASK,
+ FIELD_PREP(FAST_PULSE_TIME_MASK, 0x4));
+ regmap_update_bits(hdptx->regmap, SB_REG(0118),
+ SB_TG_EARC_DMRX_RECVRD_CLK_CNT_MASK,
+ FIELD_PREP(SB_TG_EARC_DMRX_RECVRD_CLK_CNT_MASK, 0xa));
+
+ regmap_update_bits(hdptx->regmap, SB_REG(011a), SB_TG_CNT_RUN_NO_7_0_MASK,
+ FIELD_PREP(SB_TG_CNT_RUN_NO_7_0_MASK, 0x3));
+ regmap_update_bits(hdptx->regmap, SB_REG(011b),
+ SB_EARC_SIG_DET_BYPASS_MASK | SB_AFC_TOL_MASK,
+ FIELD_PREP(SB_EARC_SIG_DET_BYPASS_MASK, 0x1) |
+ FIELD_PREP(SB_AFC_TOL_MASK, 0x3));
+ regmap_update_bits(hdptx->regmap, SB_REG(011c), SB_AFC_STB_NUM_MASK,
+ FIELD_PREP(SB_AFC_STB_NUM_MASK, 0x4));
+ regmap_update_bits(hdptx->regmap, SB_REG(011d), SB_TG_OSC_CNT_MIN_MASK,
+ FIELD_PREP(SB_TG_OSC_CNT_MIN_MASK, 0x67));
+ regmap_update_bits(hdptx->regmap, SB_REG(011e), SB_TG_OSC_CNT_MAX_MASK,
+ FIELD_PREP(SB_TG_OSC_CNT_MAX_MASK, 0x6a));
+ regmap_update_bits(hdptx->regmap, SB_REG(011f), SB_PWM_AFC_CTRL_MASK,
+ FIELD_PREP(SB_PWM_AFC_CTRL_MASK, 0x5));
+ regmap_update_bits(hdptx->regmap, SB_REG(011f), SB_RCAL_RSTN_MASK,
+ FIELD_PREP(SB_RCAL_RSTN_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, SB_REG(0120), SB_AUX_EN_IN_MASK,
+ FIELD_PREP(SB_AUX_EN_IN_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, SB_REG(0102), OVRD_SB_RXTERM_EN_MASK,
+ FIELD_PREP(OVRD_SB_RXTERM_EN_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, SB_REG(0103), OVRD_SB_RX_RESCAL_DONE_MASK,
+ FIELD_PREP(OVRD_SB_RX_RESCAL_DONE_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, SB_REG(0104), OVRD_SB_EN_MASK,
+ FIELD_PREP(OVRD_SB_EN_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, SB_REG(0104), OVRD_SB_AUX_EN_MASK,
+ FIELD_PREP(OVRD_SB_AUX_EN_MASK, 0x1));
+
+ regmap_update_bits(hdptx->regmap, SB_REG(010f), OVRD_SB_VREG_EN_MASK,
+ FIELD_PREP(OVRD_SB_VREG_EN_MASK, 0x1));
+
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_I_BGR_EN << 16 | FIELD_PREP(HDPTX_I_BGR_EN, 0x1));
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_I_BIAS_EN << 16 | FIELD_PREP(HDPTX_I_BIAS_EN, 0x1));
+ usleep_range(20, 25);
+
+ reset_control_deassert(hdptx->rsts[RST_INIT].rstc);
+ usleep_range(20, 25);
+ reset_control_deassert(hdptx->rsts[RST_CMN].rstc);
+ usleep_range(20, 25);
+
+ regmap_update_bits(hdptx->regmap, SB_REG(0103), OVRD_SB_RX_RESCAL_DONE_MASK,
+ FIELD_PREP(OVRD_SB_RX_RESCAL_DONE_MASK, 0x1));
+ usleep_range(100, 110);
+ regmap_update_bits(hdptx->regmap, SB_REG(0104), SB_EN_MASK,
+ FIELD_PREP(SB_EN_MASK, 0x1));
+ usleep_range(100, 110);
+ regmap_update_bits(hdptx->regmap, SB_REG(0102), SB_RXTERM_EN_MASK,
+ FIELD_PREP(SB_RXTERM_EN_MASK, 0x1));
+ usleep_range(20, 25);
+ regmap_update_bits(hdptx->regmap, SB_REG(010f), SB_VREG_EN_MASK,
+ FIELD_PREP(SB_VREG_EN_MASK, 0x1));
+ usleep_range(20, 25);
+ regmap_update_bits(hdptx->regmap, SB_REG(0104), SB_AUX_EN_MASK,
+ FIELD_PREP(SB_AUX_EN_MASK, 0x1));
+ usleep_range(100, 110);
+
+ ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS,
+ status, FIELD_GET(HDPTX_O_SB_RDY, status),
+ 50, 1000);
+ if (ret) {
+ dev_err(hdptx->dev, "Failed to get phy sb ready: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int rk_hdptx_phy_power_on(struct phy *phy)
{
struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy);
int bus_width = phy_get_bus_width(hdptx->phy);
- int ret;
+ enum phy_mode mode = phy_get_mode(phy);
+ int ret, lane;
/*
* FIXME: Temporary workaround to pass pixel_clk_rate
@@ -919,9 +1461,37 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
if (ret)
return ret;
- ret = rk_hdptx_ropll_tmds_mode_config(hdptx, rate);
- if (ret)
- rk_hdptx_phy_consumer_put(hdptx, true);
+ if (mode == PHY_MODE_DP) {
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x1));
+
+ for (lane = 0; lane < 4; lane++) {
+ regmap_update_bits(hdptx->regmap, LANE_REG(031e) + 0x400 * lane,
+ LN_POLARITY_INV_MASK | LN_LANE_MODE_MASK,
+ FIELD_PREP(LN_POLARITY_INV_MASK, 0) |
+ FIELD_PREP(LN_LANE_MODE_MASK, 1));
+ }
+
+ regmap_update_bits(hdptx->regmap, LNTOP_REG(0200), PROTOCOL_SEL_MASK,
+ FIELD_PREP(PROTOCOL_SEL_MASK, 0x0));
+ regmap_update_bits(hdptx->regmap, LNTOP_REG(0206), DATA_BUS_WIDTH_MASK,
+ FIELD_PREP(DATA_BUS_WIDTH_MASK, 0x1));
+ regmap_update_bits(hdptx->regmap, LNTOP_REG(0206), DATA_BUS_WIDTH_SEL_MASK,
+ FIELD_PREP(DATA_BUS_WIDTH_SEL_MASK, 0x0));
+
+ rk_hdptx_dp_pll_init(hdptx);
+
+ ret = rk_hdptx_dp_aux_init(hdptx);
+ if (ret)
+ rk_hdptx_phy_consumer_put(hdptx, true);
+ } else {
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
+
+ ret = rk_hdptx_ropll_tmds_mode_config(hdptx, rate);
+ if (ret)
+ rk_hdptx_phy_consumer_put(hdptx, true);
+ }
return ret;
}
@@ -933,9 +1503,308 @@ static int rk_hdptx_phy_power_off(struct phy *phy)
return rk_hdptx_phy_consumer_put(hdptx, false);
}
+static int rk_hdptx_phy_verify_config(struct rk_hdptx_phy *hdptx,
+ struct phy_configure_opts_dp *dp)
+{
+ int i;
+
+ if (dp->set_rate) {
+ switch (dp->link_rate) {
+ case 1620:
+ case 2700:
+ case 5400:
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if (dp->set_lanes) {
+ switch (dp->lanes) {
+ case 1:
+ case 2:
+ case 4:
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if (dp->set_voltages) {
+ for (i = 0; i < hdptx->lanes; i++) {
+ if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+ return -EINVAL;
+
+ if (dp->voltage[i] + dp->pre[i] > 3)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int rk_hdptx_phy_set_rate(struct rk_hdptx_phy *hdptx,
+ struct phy_configure_opts_dp *dp)
+{
+ u32 bw, status;
+ int ret;
+
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x0));
+
+ switch (dp->link_rate) {
+ case 1620:
+ bw = DP_BW_RBR;
+ break;
+ case 2700:
+ bw = DP_BW_HBR;
+ break;
+ case 5400:
+ bw = DP_BW_HBR2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ hdptx->link_rate = dp->link_rate;
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0008), OVRD_LCPLL_EN_MASK | LCPLL_EN_MASK,
+ FIELD_PREP(OVRD_LCPLL_EN_MASK, 0x1) |
+ FIELD_PREP(LCPLL_EN_MASK, 0x0));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(003d), OVRD_ROPLL_EN_MASK | ROPLL_EN_MASK,
+ FIELD_PREP(OVRD_ROPLL_EN_MASK, 0x1) |
+ FIELD_PREP(ROPLL_EN_MASK, 0x1));
+
+ if (dp->ssc) {
+ regmap_update_bits(hdptx->regmap, CMN_REG(0074),
+ OVRD_ROPLL_SSC_EN_MASK | ROPLL_SSC_EN_MASK,
+ FIELD_PREP(OVRD_ROPLL_SSC_EN_MASK, 0x1) |
+ FIELD_PREP(ROPLL_SSC_EN_MASK, 0x1));
+ regmap_write(hdptx->regmap, CMN_REG(0075),
+ FIELD_PREP(ANA_ROPLL_SSC_FM_DEVIATION_MASK, 0xc));
+ regmap_update_bits(hdptx->regmap, CMN_REG(0076),
+ ANA_ROPLL_SSC_FM_FREQ_MASK,
+ FIELD_PREP(ANA_ROPLL_SSC_FM_FREQ_MASK, 0x1f));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0099), SSC_EN_MASK,
+ FIELD_PREP(SSC_EN_MASK, 0x2));
+ } else {
+ regmap_update_bits(hdptx->regmap, CMN_REG(0074),
+ OVRD_ROPLL_SSC_EN_MASK | ROPLL_SSC_EN_MASK,
+ FIELD_PREP(OVRD_ROPLL_SSC_EN_MASK, 0x1) |
+ FIELD_PREP(ROPLL_SSC_EN_MASK, 0x0));
+ regmap_write(hdptx->regmap, CMN_REG(0075),
+ FIELD_PREP(ANA_ROPLL_SSC_FM_DEVIATION_MASK, 0x20));
+ regmap_update_bits(hdptx->regmap, CMN_REG(0076),
+ ANA_ROPLL_SSC_FM_FREQ_MASK,
+ FIELD_PREP(ANA_ROPLL_SSC_FM_FREQ_MASK, 0xc));
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0099), SSC_EN_MASK,
+ FIELD_PREP(SSC_EN_MASK, 0x0));
+ }
+
+ regmap_update_bits(hdptx->regmap, CMN_REG(0095), DP_TX_LINK_BW_MASK,
+ FIELD_PREP(DP_TX_LINK_BW_MASK, bw));
+
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x1));
+
+ ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS,
+ status, FIELD_GET(HDPTX_O_PLL_LOCK_DONE, status),
+ 50, 1000);
+ if (ret) {
+ dev_err(hdptx->dev, "Failed to get phy pll lock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rk_hdptx_phy_set_lanes(struct rk_hdptx_phy *hdptx,
+ struct phy_configure_opts_dp *dp)
+{
+ hdptx->lanes = dp->lanes;
+
+ regmap_update_bits(hdptx->regmap, LNTOP_REG(0207), LANE_EN_MASK,
+ FIELD_PREP(LANE_EN_MASK, GENMASK(hdptx->lanes - 1, 0)));
+
+ return 0;
+}
+
+static void rk_hdptx_phy_set_voltage(struct rk_hdptx_phy *hdptx,
+ struct phy_configure_opts_dp *dp,
+ u8 lane)
+{
+ const struct tx_drv_ctrl *ctrl;
+ u32 offset = lane * 0x400;
+
+ switch (hdptx->link_rate) {
+ case 1620:
+ ctrl = &tx_drv_ctrl_rbr[dp->voltage[lane]][dp->pre[lane]];
+ regmap_update_bits(hdptx->regmap, LANE_REG(030a) + offset,
+ LN_TX_JEQ_EVEN_CTRL_RBR_MASK,
+ FIELD_PREP(LN_TX_JEQ_EVEN_CTRL_RBR_MASK,
+ ctrl->tx_jeq_even_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(030c) + offset,
+ LN_TX_JEQ_ODD_CTRL_RBR_MASK,
+ FIELD_PREP(LN_TX_JEQ_ODD_CTRL_RBR_MASK,
+ ctrl->tx_jeq_odd_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0311) + offset,
+ LN_TX_SER_40BIT_EN_RBR_MASK,
+ FIELD_PREP(LN_TX_SER_40BIT_EN_RBR_MASK, 0x1));
+ break;
+ case 2700:
+ ctrl = &tx_drv_ctrl_hbr[dp->voltage[lane]][dp->pre[lane]];
+ regmap_update_bits(hdptx->regmap, LANE_REG(030b) + offset,
+ LN_TX_JEQ_EVEN_CTRL_HBR_MASK,
+ FIELD_PREP(LN_TX_JEQ_EVEN_CTRL_HBR_MASK,
+ ctrl->tx_jeq_even_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(030d) + offset,
+ LN_TX_JEQ_ODD_CTRL_HBR_MASK,
+ FIELD_PREP(LN_TX_JEQ_ODD_CTRL_HBR_MASK,
+ ctrl->tx_jeq_odd_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0311) + offset,
+ LN_TX_SER_40BIT_EN_HBR_MASK,
+ FIELD_PREP(LN_TX_SER_40BIT_EN_HBR_MASK, 0x1));
+ break;
+ case 5400:
+ default:
+ ctrl = &tx_drv_ctrl_hbr2[dp->voltage[lane]][dp->pre[lane]];
+ regmap_update_bits(hdptx->regmap, LANE_REG(030b) + offset,
+ LN_TX_JEQ_EVEN_CTRL_HBR2_MASK,
+ FIELD_PREP(LN_TX_JEQ_EVEN_CTRL_HBR2_MASK,
+ ctrl->tx_jeq_even_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(030d) + offset,
+ LN_TX_JEQ_ODD_CTRL_HBR2_MASK,
+ FIELD_PREP(LN_TX_JEQ_ODD_CTRL_HBR2_MASK,
+ ctrl->tx_jeq_odd_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0311) + offset,
+ LN_TX_SER_40BIT_EN_HBR2_MASK,
+ FIELD_PREP(LN_TX_SER_40BIT_EN_HBR2_MASK, 0x1));
+ break;
+ }
+
+ regmap_update_bits(hdptx->regmap, LANE_REG(0303) + offset,
+ OVRD_LN_TX_DRV_LVL_CTRL_MASK | LN_TX_DRV_LVL_CTRL_MASK,
+ FIELD_PREP(OVRD_LN_TX_DRV_LVL_CTRL_MASK, 0x1) |
+ FIELD_PREP(LN_TX_DRV_LVL_CTRL_MASK,
+ ctrl->tx_drv_lvl_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0304) + offset,
+ OVRD_LN_TX_DRV_POST_LVL_CTRL_MASK |
+ LN_TX_DRV_POST_LVL_CTRL_MASK,
+ FIELD_PREP(OVRD_LN_TX_DRV_POST_LVL_CTRL_MASK, 0x1) |
+ FIELD_PREP(LN_TX_DRV_POST_LVL_CTRL_MASK,
+ ctrl->tx_drv_post_lvl_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0305) + offset,
+ OVRD_LN_TX_DRV_PRE_LVL_CTRL_MASK |
+ LN_TX_DRV_PRE_LVL_CTRL_MASK,
+ FIELD_PREP(OVRD_LN_TX_DRV_PRE_LVL_CTRL_MASK, 0x1) |
+ FIELD_PREP(LN_TX_DRV_PRE_LVL_CTRL_MASK,
+ ctrl->tx_drv_pre_lvl_ctrl));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0306) + offset,
+ LN_ANA_TX_DRV_IDRV_IDN_CTRL_MASK |
+ LN_ANA_TX_DRV_IDRV_IUP_CTRL_MASK |
+ LN_ANA_TX_DRV_ACCDRV_EN_MASK,
+ FIELD_PREP(LN_ANA_TX_DRV_IDRV_IDN_CTRL_MASK,
+ ctrl->ana_tx_drv_idrv_idn_ctrl) |
+ FIELD_PREP(LN_ANA_TX_DRV_IDRV_IUP_CTRL_MASK,
+ ctrl->ana_tx_drv_idrv_iup_ctrl) |
+ FIELD_PREP(LN_ANA_TX_DRV_ACCDRV_EN_MASK,
+ ctrl->ana_tx_drv_accdrv_en));
+ regmap_update_bits(hdptx->regmap, LANE_REG(0307) + offset,
+ LN_ANA_TX_DRV_ACCDRV_POL_SEL_MASK |
+ LN_ANA_TX_DRV_ACCDRV_CTRL_MASK,
+ FIELD_PREP(LN_ANA_TX_DRV_ACCDRV_POL_SEL_MASK, 0x1) |
+ FIELD_PREP(LN_ANA_TX_DRV_ACCDRV_CTRL_MASK,
+ ctrl->ana_tx_drv_accdrv_ctrl));
+
+ regmap_update_bits(hdptx->regmap, LANE_REG(030a) + offset,
+ LN_ANA_TX_JEQ_EN_MASK,
+ FIELD_PREP(LN_ANA_TX_JEQ_EN_MASK, ctrl->ana_tx_jeq_en));
+
+ regmap_update_bits(hdptx->regmap, LANE_REG(0310) + offset,
+ LN_ANA_TX_SYNC_LOSS_DET_MODE_MASK,
+ FIELD_PREP(LN_ANA_TX_SYNC_LOSS_DET_MODE_MASK, 0x3));
+
+ regmap_update_bits(hdptx->regmap, LANE_REG(0316) + offset,
+ LN_ANA_TX_SER_VREG_GAIN_CTRL_MASK,
+ FIELD_PREP(LN_ANA_TX_SER_VREG_GAIN_CTRL_MASK, 0x2));
+
+ regmap_update_bits(hdptx->regmap, LANE_REG(031b) + offset,
+ LN_ANA_TX_RESERVED_MASK,
+ FIELD_PREP(LN_ANA_TX_RESERVED_MASK, 0x1));
+}
+
+static int rk_hdptx_phy_set_voltages(struct rk_hdptx_phy *hdptx,
+ struct phy_configure_opts_dp *dp)
+{
+ u8 lane;
+ u32 status;
+ int ret;
+
+ for (lane = 0; lane < hdptx->lanes; lane++)
+ rk_hdptx_phy_set_voltage(hdptx, dp, lane);
+
+ reset_control_deassert(hdptx->rsts[RST_LANE].rstc);
+
+ ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS,
+ status, FIELD_GET(HDPTX_O_PHY_RDY, status),
+ 50, 5000);
+ if (ret) {
+ dev_err(hdptx->dev, "Failed to get phy ready: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct rk_hdptx_phy *hdptx = phy_get_drvdata(phy);
+ enum phy_mode mode = phy_get_mode(phy);
+ int ret;
+
+ if (mode != PHY_MODE_DP)
+ return 0;
+
+ ret = rk_hdptx_phy_verify_config(hdptx, &opts->dp);
+ if (ret) {
+ dev_err(hdptx->dev, "invalid params for phy configure\n");
+ return ret;
+ }
+
+ if (opts->dp.set_rate) {
+ ret = rk_hdptx_phy_set_rate(hdptx, &opts->dp);
+ if (ret) {
+ dev_err(hdptx->dev, "failed to set rate: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (opts->dp.set_lanes) {
+ ret = rk_hdptx_phy_set_lanes(hdptx, &opts->dp);
+ if (ret) {
+ dev_err(hdptx->dev, "failed to set lanes: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (opts->dp.set_voltages) {
+ ret = rk_hdptx_phy_set_voltages(hdptx, &opts->dp);
+ if (ret) {
+ dev_err(hdptx->dev, "failed to set voltages: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static const struct phy_ops rk_hdptx_phy_ops = {
.power_on = rk_hdptx_phy_power_on,
.power_off = rk_hdptx_phy_power_off,
+ .configure = rk_hdptx_phy_configure,
.owner = THIS_MODULE,
};
@@ -1007,15 +1876,14 @@ static int rk_hdptx_phy_clk_register(struct rk_hdptx_phy *hdptx)
struct device *dev = hdptx->dev;
const char *name, *pname;
struct clk *refclk;
- int ret, id;
+ int ret;
refclk = devm_clk_get(dev, "ref");
if (IS_ERR(refclk))
return dev_err_probe(dev, PTR_ERR(refclk),
"Failed to get ref clock\n");
- id = of_alias_get_id(dev->of_node, "hdptxphy");
- name = id > 0 ? "clk_hdmiphy_pixel1" : "clk_hdmiphy_pixel0";
+ name = hdptx->phy_id > 0 ? "clk_hdmiphy_pixel1" : "clk_hdmiphy_pixel0";
pname = __clk_get_name(refclk);
hdptx->hw.init = CLK_HW_INIT(name, pname, &hdptx_phy_clk_ops,
@@ -1058,8 +1926,9 @@ static int rk_hdptx_phy_probe(struct platform_device *pdev)
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct rk_hdptx_phy *hdptx;
+ struct resource *res;
void __iomem *regs;
- int ret;
+ int ret, id;
hdptx = devm_kzalloc(dev, sizeof(*hdptx), GFP_KERNEL);
if (!hdptx)
@@ -1067,11 +1936,27 @@ static int rk_hdptx_phy_probe(struct platform_device *pdev)
hdptx->dev = dev;
- regs = devm_platform_ioremap_resource(pdev, 0);
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return dev_err_probe(dev, PTR_ERR(regs),
"Failed to ioremap resource\n");
+ hdptx->cfgs = device_get_match_data(dev);
+ if (!hdptx->cfgs)
+ return dev_err_probe(dev, -EINVAL, "missing match data\n");
+
+ /* find the phy-id from the io address */
+ hdptx->phy_id = -ENODEV;
+ for (id = 0; id < hdptx->cfgs->num_phys; id++) {
+ if (res->start == hdptx->cfgs->phy_ids[id]) {
+ hdptx->phy_id = id;
+ break;
+ }
+ }
+
+ if (hdptx->phy_id < 0)
+ return dev_err_probe(dev, -ENODEV, "no matching device found\n");
+
ret = devm_clk_bulk_get_all(dev, &hdptx->clks);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get clocks\n");
@@ -1132,8 +2017,30 @@ static const struct dev_pm_ops rk_hdptx_phy_pm_ops = {
rk_hdptx_phy_runtime_resume, NULL)
};
+static const struct rk_hdptx_phy_cfg rk3576_hdptx_phy_cfgs = {
+ .num_phys = 1,
+ .phy_ids = {
+ 0x2b000000,
+ },
+};
+
+static const struct rk_hdptx_phy_cfg rk3588_hdptx_phy_cfgs = {
+ .num_phys = 2,
+ .phy_ids = {
+ 0xfed60000,
+ 0xfed70000,
+ },
+};
+
static const struct of_device_id rk_hdptx_phy_of_match[] = {
- { .compatible = "rockchip,rk3588-hdptx-phy", },
+ {
+ .compatible = "rockchip,rk3576-hdptx-phy",
+ .data = &rk3576_hdptx_phy_cfgs
+ },
+ {
+ .compatible = "rockchip,rk3588-hdptx-phy",
+ .data = &rk3588_hdptx_phy_cfgs
+ },
{}
};
MODULE_DEVICE_TABLE(of, rk_hdptx_phy_of_match);
@@ -1150,5 +2057,6 @@ module_platform_driver(rk_hdptx_phy_driver);
MODULE_AUTHOR("Algea Cao <algea.cao@rock-chips.com>");
MODULE_AUTHOR("Cristian Ciocaltea <cristian.ciocaltea@collabora.com>");
+MODULE_AUTHOR("Damon Ding <damon.ding@rock-chips.com>");
MODULE_DESCRIPTION("Samsung HDMI/eDP Transmitter Combo PHY Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 5b1e8a3806ed..c066cc0a7b4f 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -187,6 +187,8 @@ struct rk_udphy {
u32 dp_aux_din_sel;
bool dp_sink_hpd_sel;
bool dp_sink_hpd_cfg;
+ unsigned int link_rate;
+ unsigned int lanes;
u8 bw;
int id;
@@ -978,7 +980,7 @@ static int rk_udphy_parse_dt(struct rk_udphy *udphy)
if (device_property_present(dev, "maximum-speed")) {
maximum_speed = usb_get_maximum_speed(dev);
- udphy->hs = maximum_speed <= USB_SPEED_HIGH ? true : false;
+ udphy->hs = maximum_speed <= USB_SPEED_HIGH;
}
ret = rk_udphy_clk_init(udphy, dev);
@@ -1045,7 +1047,6 @@ static int rk_udphy_dp_phy_init(struct phy *phy)
mutex_lock(&udphy->mutex);
udphy->dp_in_use = true;
- rk_udphy_dp_hpd_event_trigger(udphy, udphy->dp_sink_hpd_cfg);
mutex_unlock(&udphy->mutex);
@@ -1103,15 +1104,19 @@ static int rk_udphy_dp_phy_power_off(struct phy *phy)
return 0;
}
-static int rk_udphy_dp_phy_verify_link_rate(unsigned int link_rate)
+/*
+ * Verify link rate
+ */
+static int rk_udphy_dp_phy_verify_link_rate(struct rk_udphy *udphy,
+ struct phy_configure_opts_dp *dp)
{
- switch (link_rate) {
+ switch (dp->link_rate) {
case 1620:
case 2700:
case 5400:
case 8100:
+ udphy->link_rate = dp->link_rate;
break;
-
default:
return -EINVAL;
}
@@ -1119,45 +1124,44 @@ static int rk_udphy_dp_phy_verify_link_rate(unsigned int link_rate)
return 0;
}
-static int rk_udphy_dp_phy_verify_config(struct rk_udphy *udphy,
- struct phy_configure_opts_dp *dp)
+static int rk_udphy_dp_phy_verify_lanes(struct rk_udphy *udphy,
+ struct phy_configure_opts_dp *dp)
{
- int i, ret;
-
- /* If changing link rate was required, verify it's supported. */
- ret = rk_udphy_dp_phy_verify_link_rate(dp->link_rate);
- if (ret)
- return ret;
-
- /* Verify lane count. */
switch (dp->lanes) {
case 1:
case 2:
case 4:
/* valid lane count. */
+ udphy->lanes = dp->lanes;
break;
default:
return -EINVAL;
}
- /*
- * If changing voltages is required, check swing and pre-emphasis
- * levels, per-lane.
- */
- if (dp->set_voltages) {
- /* Lane count verified previously. */
- for (i = 0; i < dp->lanes; i++) {
- if (dp->voltage[i] > 3 || dp->pre[i] > 3)
- return -EINVAL;
+ return 0;
+}
- /*
- * Sum of voltage swing and pre-emphasis levels cannot
- * exceed 3.
- */
- if (dp->voltage[i] + dp->pre[i] > 3)
- return -EINVAL;
- }
+/*
+ * If changing voltages is required, check swing and pre-emphasis
+ * levels, per-lane.
+ */
+static int rk_udphy_dp_phy_verify_voltages(struct rk_udphy *udphy,
+ struct phy_configure_opts_dp *dp)
+{
+ int i;
+
+ /* Lane count verified previously. */
+ for (i = 0; i < udphy->lanes; i++) {
+ if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+ return -EINVAL;
+
+ /*
+ * Sum of voltage swing and pre-emphasis levels cannot
+ * exceed 3.
+ */
+ if (dp->voltage[i] + dp->pre[i] > 3)
+ return -EINVAL;
}
return 0;
@@ -1197,9 +1201,23 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
u32 i, val, lane;
int ret;
- ret = rk_udphy_dp_phy_verify_config(udphy, dp);
- if (ret)
- return ret;
+ if (dp->set_rate) {
+ ret = rk_udphy_dp_phy_verify_link_rate(udphy, dp);
+ if (ret)
+ return ret;
+ }
+
+ if (dp->set_lanes) {
+ ret = rk_udphy_dp_phy_verify_lanes(udphy, dp);
+ if (ret)
+ return ret;
+ }
+
+ if (dp->set_voltages) {
+ ret = rk_udphy_dp_phy_verify_voltages(udphy, dp);
+ if (ret)
+ return ret;
+ }
if (dp->set_rate) {
regmap_update_bits(udphy->pma_regmap, CMN_DP_RSTN_OFFSET,
@@ -1244,9 +1262,9 @@ static int rk_udphy_dp_phy_configure(struct phy *phy,
}
if (dp->set_voltages) {
- for (i = 0; i < dp->lanes; i++) {
+ for (i = 0; i < udphy->lanes; i++) {
lane = udphy->dp_lane_sel[i];
- switch (dp->link_rate) {
+ switch (udphy->link_rate) {
case 1620:
case 2700:
regmap_update_bits(udphy->pma_regmap,
diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig
index e2330b0894d6..6566100441d6 100644
--- a/drivers/phy/samsung/Kconfig
+++ b/drivers/phy/samsung/Kconfig
@@ -81,6 +81,7 @@ config PHY_EXYNOS5_USBDRD
tristate "Exynos5 SoC series USB DRD PHY driver"
depends on (ARCH_EXYNOS && OF) || COMPILE_TEST
depends on HAS_IOMEM
+ depends on TYPEC || !TYPEC
depends on USB_DWC3_EXYNOS
select GENERIC_PHY
select MFD_SYSCON
diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile
index fea1f96d0e43..342682638a87 100644
--- a/drivers/phy/samsung/Makefile
+++ b/drivers/phy/samsung/Makefile
@@ -7,6 +7,7 @@ phy-exynos-ufs-y += phy-gs101-ufs.o
phy-exynos-ufs-y += phy-samsung-ufs.o
phy-exynos-ufs-y += phy-exynos7-ufs.o
phy-exynos-ufs-y += phy-exynosautov9-ufs.o
+phy-exynos-ufs-y += phy-exynosautov920-ufs.o
phy-exynos-ufs-y += phy-fsd-ufs.o
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
phy-exynos-usb2-y += phy-samsung-usb2.o
diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c
index 46b8f6987c62..817fddee0392 100644
--- a/drivers/phy/samsung/phy-exynos5-usbdrd.c
+++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c
@@ -23,6 +23,8 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/typec_mux.h>
/* Exynos USB PHY registers */
#define EXYNOS5_FSEL_9MHZ6 0x0
@@ -209,6 +211,10 @@
#define EXYNOS9_PMA_USBDP_CMN_REG00B8 0x02e0
#define CMN_REG00B8_LANE_MUX_SEL_DP GENMASK(3, 0)
+#define CMN_REG00B8_LANE_MUX_SEL_DP_LANE3 BIT(3)
+#define CMN_REG00B8_LANE_MUX_SEL_DP_LANE2 BIT(2)
+#define CMN_REG00B8_LANE_MUX_SEL_DP_LANE1 BIT(1)
+#define CMN_REG00B8_LANE_MUX_SEL_DP_LANE0 BIT(0)
#define EXYNOS9_PMA_USBDP_CMN_REG01C0 0x0700
#define CMN_REG01C0_ANA_LCPLL_LOCK_DONE BIT(7)
@@ -383,11 +389,14 @@ struct exynos5_usbdrd_phy_drvdata {
* @clks: clocks for register access
* @core_clks: core clocks for phy (ref, pipe3, utmi+, ITP, etc. as required)
* @drv_data: pointer to SoC level driver data structure
+ * @phy_mutex: mutex protecting phy_init/exit & TCPC callbacks
* @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
* instances each with its 'phy' and 'phy_cfg'.
* @extrefclk: frequency select settings when using 'separate
* reference clocks' for SS and HS operations
* @regulators: regulators for phy
+ * @sw: TypeC orientation switch handle
+ * @orientation: TypeC connector orientation - normal or flipped
*/
struct exynos5_usbdrd_phy {
struct device *dev;
@@ -397,6 +406,7 @@ struct exynos5_usbdrd_phy {
struct clk_bulk_data *clks;
struct clk_bulk_data *core_clks;
const struct exynos5_usbdrd_phy_drvdata *drv_data;
+ struct mutex phy_mutex;
struct phy_usb_instance {
struct phy *phy;
u32 index;
@@ -406,6 +416,9 @@ struct exynos5_usbdrd_phy {
} phys[EXYNOS5_DRDPHYS_NUM];
u32 extrefclk;
struct regulator_bulk_data *regulators;
+
+ struct typec_switch_dev *sw;
+ enum typec_orientation orientation;
};
static inline
@@ -647,22 +660,38 @@ exynos5_usbdrd_usbdp_g2_v4_pma_lane_mux_sel(struct exynos5_usbdrd_phy *phy_drd)
/* lane configuration: USB on all lanes */
reg = readl(regs_base + EXYNOS9_PMA_USBDP_CMN_REG00B8);
reg &= ~CMN_REG00B8_LANE_MUX_SEL_DP;
- writel(reg, regs_base + EXYNOS9_PMA_USBDP_CMN_REG00B8);
-
/*
- * FIXME: below code supports one connector orientation only. It needs
- * updating once we can receive connector events.
+ * USB on lanes 0 & 1 in normal mode, or 2 & 3 if reversed, DP on the
+ * other ones.
*/
+ reg |= FIELD_PREP(CMN_REG00B8_LANE_MUX_SEL_DP,
+ ((phy_drd->orientation == TYPEC_ORIENTATION_NORMAL)
+ ? (CMN_REG00B8_LANE_MUX_SEL_DP_LANE3
+ | CMN_REG00B8_LANE_MUX_SEL_DP_LANE2)
+ : (CMN_REG00B8_LANE_MUX_SEL_DP_LANE1
+ | CMN_REG00B8_LANE_MUX_SEL_DP_LANE0)));
+ writel(reg, regs_base + EXYNOS9_PMA_USBDP_CMN_REG00B8);
+
/* override of TX receiver detector and comparator: lane 1 */
reg = readl(regs_base + EXYNOS9_PMA_USBDP_TRSV_REG0413);
- reg &= ~TRSV_REG0413_OVRD_LN1_TX_RXD_COMP_EN;
- reg &= ~TRSV_REG0413_OVRD_LN1_TX_RXD_EN;
+ if (phy_drd->orientation == TYPEC_ORIENTATION_NORMAL) {
+ reg &= ~TRSV_REG0413_OVRD_LN1_TX_RXD_COMP_EN;
+ reg &= ~TRSV_REG0413_OVRD_LN1_TX_RXD_EN;
+ } else {
+ reg |= TRSV_REG0413_OVRD_LN1_TX_RXD_COMP_EN;
+ reg |= TRSV_REG0413_OVRD_LN1_TX_RXD_EN;
+ }
writel(reg, regs_base + EXYNOS9_PMA_USBDP_TRSV_REG0413);
/* lane 3 */
reg = readl(regs_base + EXYNOS9_PMA_USBDP_TRSV_REG0813);
- reg |= TRSV_REG0813_OVRD_LN3_TX_RXD_COMP_EN;
- reg |= TRSV_REG0813_OVRD_LN3_TX_RXD_EN;
+ if (phy_drd->orientation == TYPEC_ORIENTATION_NORMAL) {
+ reg |= TRSV_REG0813_OVRD_LN3_TX_RXD_COMP_EN;
+ reg |= TRSV_REG0813_OVRD_LN3_TX_RXD_EN;
+ } else {
+ reg &= ~TRSV_REG0813_OVRD_LN3_TX_RXD_COMP_EN;
+ reg &= ~TRSV_REG0813_OVRD_LN3_TX_RXD_EN;
+ }
writel(reg, regs_base + EXYNOS9_PMA_USBDP_TRSV_REG0813);
}
@@ -700,21 +729,18 @@ exynos5_usbdrd_usbdp_g2_v4_pma_check_cdr_lock(struct exynos5_usbdrd_phy *phy_drd
int err;
err = readl_poll_timeout(
- phy_drd->reg_pma + EXYNOS9_PMA_USBDP_TRSV_REG03C3,
- reg, (reg & locked) == locked, sleep_us, timeout_us);
- if (!err)
- return;
-
- dev_err(phy_drd->dev,
- "timed out waiting for CDR lock (l0): %#.8x, retrying\n", reg);
-
- /* based on cable orientation, this might be on the other phy port */
- err = readl_poll_timeout(
- phy_drd->reg_pma + EXYNOS9_PMA_USBDP_TRSV_REG07C3,
+ /* lane depends on cable orientation */
+ (phy_drd->reg_pma
+ + ((phy_drd->orientation == TYPEC_ORIENTATION_NORMAL)
+ ? EXYNOS9_PMA_USBDP_TRSV_REG03C3
+ : EXYNOS9_PMA_USBDP_TRSV_REG07C3)),
reg, (reg & locked) == locked, sleep_us, timeout_us);
if (err)
dev_err(phy_drd->dev,
- "timed out waiting for CDR lock (l2): %#.8x\n", reg);
+ "timed out waiting for CDR(l%d) lock: %#.8x\n",
+ ((phy_drd->orientation == TYPEC_ORIENTATION_NORMAL)
+ ? 0
+ : 2), reg);
}
static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
@@ -1111,13 +1137,15 @@ static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
reg |= LINKCTRL_BUS_FILTER_BYPASS(0xf);
writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL);
- reg = readl(regs_base + EXYNOS850_DRD_UTMI);
- reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID;
- writel(reg, regs_base + EXYNOS850_DRD_UTMI);
+ if (!phy_drd->sw) {
+ reg = readl(regs_base + EXYNOS850_DRD_UTMI);
+ reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID;
+ writel(reg, regs_base + EXYNOS850_DRD_UTMI);
- reg = readl(regs_base + EXYNOS850_DRD_HSP);
- reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL;
- writel(reg, regs_base + EXYNOS850_DRD_HSP);
+ reg = readl(regs_base + EXYNOS850_DRD_HSP);
+ reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL;
+ writel(reg, regs_base + EXYNOS850_DRD_HSP);
+ }
reg = readl(regs_base + EXYNOS850_DRD_SSPPLLCTL);
reg &= ~SSPPLLCTL_FSEL;
@@ -1184,7 +1212,8 @@ static int exynos850_usbdrd_phy_init(struct phy *phy)
return ret;
/* UTMI or PIPE3 specific init */
- inst->phy_cfg->phy_init(phy_drd);
+ scoped_guard(mutex, &phy_drd->phy_mutex)
+ inst->phy_cfg->phy_init(phy_drd);
clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks);
@@ -1203,6 +1232,8 @@ static int exynos850_usbdrd_phy_exit(struct phy *phy)
if (ret)
return ret;
+ guard(mutex)(&phy_drd->phy_mutex);
+
/* Set PHY clock and control HS PHY */
reg = readl(regs_base + EXYNOS850_DRD_UTMI);
reg &= ~(UTMI_DP_PULLDOWN | UTMI_DM_PULLDOWN);
@@ -1374,6 +1405,87 @@ static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
return 0;
}
+static int exynos5_usbdrd_orien_sw_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct exynos5_usbdrd_phy *phy_drd = typec_switch_get_drvdata(sw);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks);
+ if (ret) {
+ dev_err(phy_drd->dev, "Failed to enable PHY clocks(s)\n");
+ return ret;
+ }
+
+ scoped_guard(mutex, &phy_drd->phy_mutex) {
+ void __iomem * const regs_base = phy_drd->reg_phy;
+ unsigned int reg;
+
+ if (orientation == TYPEC_ORIENTATION_NONE) {
+ reg = readl(regs_base + EXYNOS850_DRD_UTMI);
+ reg &= ~(UTMI_FORCE_VBUSVALID | UTMI_FORCE_BVALID);
+ writel(reg, regs_base + EXYNOS850_DRD_UTMI);
+
+ reg = readl(regs_base + EXYNOS850_DRD_HSP);
+ reg |= HSP_VBUSVLDEXTSEL;
+ reg &= ~HSP_VBUSVLDEXT;
+ writel(reg, regs_base + EXYNOS850_DRD_HSP);
+ } else {
+ reg = readl(regs_base + EXYNOS850_DRD_UTMI);
+ reg |= UTMI_FORCE_VBUSVALID | UTMI_FORCE_BVALID;
+ writel(reg, regs_base + EXYNOS850_DRD_UTMI);
+
+ reg = readl(regs_base + EXYNOS850_DRD_HSP);
+ reg |= HSP_VBUSVLDEXTSEL | HSP_VBUSVLDEXT;
+ writel(reg, regs_base + EXYNOS850_DRD_HSP);
+ }
+
+ phy_drd->orientation = orientation;
+ }
+
+ clk_bulk_disable(phy_drd->drv_data->n_clks, phy_drd->clks);
+
+ return 0;
+}
+
+static void exynos5_usbdrd_orien_switch_unregister(void *data)
+{
+ struct exynos5_usbdrd_phy *phy_drd = data;
+
+ typec_switch_unregister(phy_drd->sw);
+}
+
+static int exynos5_usbdrd_setup_notifiers(struct exynos5_usbdrd_phy *phy_drd)
+{
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_TYPEC))
+ return 0;
+
+ if (device_property_present(phy_drd->dev, "orientation-switch")) {
+ struct typec_switch_desc sw_desc = { };
+
+ sw_desc.drvdata = phy_drd;
+ sw_desc.fwnode = dev_fwnode(phy_drd->dev);
+ sw_desc.set = exynos5_usbdrd_orien_sw_set;
+
+ phy_drd->sw = typec_switch_register(phy_drd->dev, &sw_desc);
+ if (IS_ERR(phy_drd->sw))
+ return dev_err_probe(phy_drd->dev,
+ PTR_ERR(phy_drd->sw),
+ "Failed to register TypeC orientation switch\n");
+
+ ret = devm_add_action_or_reset(phy_drd->dev,
+ exynos5_usbdrd_orien_switch_unregister,
+ phy_drd);
+ if (ret)
+ return dev_err_probe(phy_drd->dev, ret,
+ "Failed to register TypeC orientation devm action\n");
+ }
+
+ return 0;
+}
+
static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
{
.id = EXYNOS5_DRDPHY_UTMI,
@@ -1513,8 +1625,11 @@ static const struct exynos5_usbdrd_phy_tuning gs101_tunes_pipe3_preinit[] = {
PHY_TUNING_ENTRY_PMA(0x09e0, -1, 0x00),
PHY_TUNING_ENTRY_PMA(0x09e4, -1, 0x36),
PHY_TUNING_ENTRY_PMA(0x1e7c, -1, 0x06),
- PHY_TUNING_ENTRY_PMA(0x1e90, -1, 0x00),
- PHY_TUNING_ENTRY_PMA(0x1e94, -1, 0x36),
+ PHY_TUNING_ENTRY_PMA(0x19e0, -1, 0x00),
+ PHY_TUNING_ENTRY_PMA(0x19e4, -1, 0x36),
+ /* fix bootloader bug */
+ PHY_TUNING_ENTRY_PMA(0x1e90, -1, 0x02),
+ PHY_TUNING_ENTRY_PMA(0x1e94, -1, 0x0b),
/* improve LVCC */
PHY_TUNING_ENTRY_PMA(0x08f0, -1, 0x30),
PHY_TUNING_ENTRY_PMA(0x18f0, -1, 0x30),
@@ -1698,6 +1813,10 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
return -EINVAL;
phy_drd->drv_data = drv_data;
+ ret = devm_mutex_init(dev, &phy_drd->phy_mutex);
+ if (ret)
+ return ret;
+
if (of_property_present(dev->of_node, "reg-names")) {
void __iomem *reg;
@@ -1728,10 +1847,9 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,pmu-syscon");
- if (IS_ERR(reg_pmu)) {
- dev_err(dev, "Failed to lookup PMU regmap\n");
- return PTR_ERR(reg_pmu);
- }
+ if (IS_ERR(reg_pmu))
+ return dev_err_probe(dev, PTR_ERR(reg_pmu),
+ "Failed to lookup PMU regmap\n");
/*
* Exynos5420 SoC has multiple channels for USB 3.0 PHY, with
@@ -1757,15 +1875,18 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "failed to get regulators\n");
+ ret = exynos5_usbdrd_setup_notifiers(phy_drd);
+ if (ret)
+ return ret;
+
dev_vdbg(dev, "Creating usbdrd_phy phy\n");
for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
struct phy *phy = devm_phy_create(dev, NULL, drv_data->phy_ops);
- if (IS_ERR(phy)) {
- dev_err(dev, "Failed to create usbdrd_phy phy\n");
- return PTR_ERR(phy);
- }
+ if (IS_ERR(phy))
+ return dev_err_probe(dev, PTR_ERR(phy),
+ "Failed to create usbdrd_phy phy\n");
phy_drd->phys[i].phy = phy;
phy_drd->phys[i].index = i;
@@ -1789,10 +1910,9 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev,
exynos5_usbdrd_phy_xlate);
- if (IS_ERR(phy_provider)) {
- dev_err(phy_drd->dev, "Failed to register phy provider\n");
- return PTR_ERR(phy_provider);
- }
+ if (IS_ERR(phy_provider))
+ return dev_err_probe(phy_drd->dev, PTR_ERR(phy_provider),
+ "Failed to register phy provider\n");
return 0;
}
diff --git a/drivers/phy/samsung/phy-exynosautov920-ufs.c b/drivers/phy/samsung/phy-exynosautov920-ufs.c
new file mode 100644
index 000000000000..21ef79c42f95
--- /dev/null
+++ b/drivers/phy/samsung/phy-exynosautov920-ufs.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver data for Samsung ExynosAuto v920 SoC
+ *
+ * Copyright (C) 2024 Samsung Electronics Co., Ltd.
+ */
+
+#include "phy-samsung-ufs.h"
+
+#define EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL 0x708
+#define EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1
+#define EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0)
+#define EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e
+
+#define EXYNOSAUTOV920_CDR_LOCK_OFFSET 0xce4
+
+#define PHY_EXYNOSAUTOV920_LANE_OFFSET 0x200
+#define PHY_TRSV_REG_CFG_AUTOV920(o, v, d) \
+ PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_EXYNOSAUTOV920_LANE_OFFSET)
+
+/* Calibration for phy initialization */
+static const struct samsung_ufs_phy_cfg exynosautov920_pre_init_cfg[] = {
+ PHY_COMN_REG_CFG(0x29, 0x22, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x43, 0x10, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x3c, 0x14, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x46, 0x48, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x04, 0x95, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x06, 0x30, PWR_MODE_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV920(0x200, 0x00, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x201, 0x06, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x202, 0x06, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x203, 0x0a, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x204, 0x00, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x205, 0x10, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x207, 0x0c, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2e1, 0xc0, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x22d, 0xf8, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x234, 0x60, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x238, 0x13, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x239, 0x48, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x23a, 0x01, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x23b, 0x29, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x23c, 0x2a, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x23d, 0x01, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x23e, 0x14, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x23f, 0x13, PWR_MODE_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV920(0x240, 0x4a, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x243, 0x40, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x244, 0x02, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x25d, 0x00, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x25e, 0x3f, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x25f, 0xff, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x26f, 0xf0, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x273, 0x33, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x274, 0x50, PWR_MODE_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV920(0x284, 0x02, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x285, 0x02, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2a2, 0x04, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x27d, 0x01, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2fa, 0x01, PWR_MODE_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV920(0x286, 0x03, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x287, 0x03, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x288, 0x03, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x289, 0x03, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2b3, 0x04, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2b6, 0x0b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2b7, 0x0b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2b8, 0x0b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2b9, 0x0b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2ba, 0x0b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2bb, 0x06, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2bc, 0x06, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2bd, 0x06, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x2be, 0x06, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x34b, 0x01, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x34c, 0x24, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x34d, 0x23, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x34e, 0x45, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x34f, 0x00, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x350, 0x31, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x351, 0x00, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x352, 0x02, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x353, 0x00, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x354, 0x01, PWR_MODE_ANY),
+
+ PHY_COMN_REG_CFG(0x43, 0x18, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x43, 0x00, PWR_MODE_ANY),
+
+ END_UFS_PHY_CFG,
+};
+
+/* Calibration for HS mode series A/B */
+static const struct samsung_ufs_phy_cfg exynosautov920_pre_pwr_hs_cfg[] = {
+ PHY_TRSV_REG_CFG_AUTOV920(0x369, 0x11, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x246, 0x03, PWR_MODE_ANY),
+
+ END_UFS_PHY_CFG,
+};
+
+static const struct samsung_ufs_phy_cfg exynosautov920_post_pwr_hs_cfg[] = {
+ END_UFS_PHY_CFG,
+};
+
+#define DELAY_IN_US 40
+#define RETRY_CNT 100
+#define EXYNOSAUTOV920_CDR_LOCK_MASK 0x8
+
+int exynosautov920_ufs_phy_wait_cdr_lock(struct phy *phy, u8 lane)
+{
+ struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
+ u32 reg, i;
+
+ struct samsung_ufs_phy_cfg cfg[4] = {
+ PHY_TRSV_REG_CFG_AUTOV920(0x222, 0x10, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x222, 0x18, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV920(0x246, 0x01, PWR_MODE_ANY),
+ END_UFS_PHY_CFG,
+ };
+
+ for (i = 0; i < RETRY_CNT; i++) {
+ udelay(DELAY_IN_US);
+
+ reg = readl(ufs_phy->reg_pma + EXYNOSAUTOV920_CDR_LOCK_OFFSET +
+ (PHY_APB_ADDR(PHY_EXYNOSAUTOV920_LANE_OFFSET) * lane));
+
+ if ((reg & EXYNOSAUTOV920_CDR_LOCK_MASK)
+ == EXYNOSAUTOV920_CDR_LOCK_MASK) {
+ samsung_ufs_phy_config(ufs_phy, &cfg[2], lane);
+ return 0;
+ }
+
+ udelay(DELAY_IN_US);
+
+ /* Disable and enable CDR */
+ samsung_ufs_phy_config(ufs_phy, &cfg[0], lane);
+ samsung_ufs_phy_config(ufs_phy, &cfg[1], lane);
+ }
+
+ dev_err(ufs_phy->dev, "failed to get phy cdr lock\n");
+ return -ETIMEDOUT;
+}
+
+static const struct samsung_ufs_phy_cfg *exynosautov920_ufs_phy_cfgs[CFG_TAG_MAX] = {
+ [CFG_PRE_INIT] = exynosautov920_pre_init_cfg,
+ [CFG_PRE_PWR_HS] = exynosautov920_pre_pwr_hs_cfg,
+ [CFG_POST_PWR_HS] = exynosautov920_post_pwr_hs_cfg,
+};
+
+static const char * const exynosautov920_ufs_phy_clks[] = {
+ "ref_clk",
+};
+
+const struct samsung_ufs_phy_drvdata exynosautov920_ufs_phy = {
+ .cfgs = exynosautov920_ufs_phy_cfgs,
+ .isol = {
+ .offset = EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL,
+ .mask = EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL_MASK,
+ .en = EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL_EN,
+ },
+ .clk_list = exynosautov920_ufs_phy_clks,
+ .num_clks = ARRAY_SIZE(exynosautov920_ufs_phy_clks),
+ .cdr_lock_status_offset = EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
+ .wait_for_cdr = exynosautov920_ufs_phy_wait_cdr_lock,
+};
diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c
index 8e9ccd39f97e..f3cbe6b17b23 100644
--- a/drivers/phy/samsung/phy-samsung-ufs.c
+++ b/drivers/phy/samsung/phy-samsung-ufs.c
@@ -28,9 +28,9 @@
#define PHY_DEF_LANE_CNT 1
-static void samsung_ufs_phy_config(struct samsung_ufs_phy *phy,
- const struct samsung_ufs_phy_cfg *cfg,
- u8 lane)
+void samsung_ufs_phy_config(struct samsung_ufs_phy *phy,
+ const struct samsung_ufs_phy_cfg *cfg,
+ u8 lane)
{
enum {LANE_0, LANE_1}; /* lane index */
@@ -324,6 +324,9 @@ static const struct of_device_id samsung_ufs_phy_match[] = {
.compatible = "samsung,exynosautov9-ufs-phy",
.data = &exynosautov9_ufs_phy,
}, {
+ .compatible = "samsung,exynosautov920-ufs-phy",
+ .data = &exynosautov920_ufs_phy,
+ }, {
.compatible = "tesla,fsd-ufs-phy",
.data = &fsd_ufs_phy,
},
diff --git a/drivers/phy/samsung/phy-samsung-ufs.h b/drivers/phy/samsung/phy-samsung-ufs.h
index 9b7deef6e10f..a28f148081d1 100644
--- a/drivers/phy/samsung/phy-samsung-ufs.h
+++ b/drivers/phy/samsung/phy-samsung-ufs.h
@@ -143,9 +143,13 @@ static inline void samsung_ufs_phy_ctrl_isol(
}
int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy, u8 lane);
+int exynosautov920_ufs_phy_wait_cdr_lock(struct phy *phy, u8 lane);
+void samsung_ufs_phy_config(struct samsung_ufs_phy *phy,
+ const struct samsung_ufs_phy_cfg *cfg, u8 lane);
extern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy;
extern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy;
+extern const struct samsung_ufs_phy_drvdata exynosautov920_ufs_phy;
extern const struct samsung_ufs_phy_drvdata fsd_ufs_phy;
extern const struct samsung_ufs_phy_drvdata tensor_gs101_ufs_phy;
diff --git a/drivers/phy/st/phy-stih407-usb.c b/drivers/phy/st/phy-stih407-usb.c
index a4ae2cca7f63..ebb1d0858aa3 100644
--- a/drivers/phy/st/phy-stih407-usb.c
+++ b/drivers/phy/st/phy-stih407-usb.c
@@ -18,8 +18,8 @@
#include <linux/mfd/syscon.h>
#include <linux/phy/phy.h>
-#define PHYPARAM_REG 1
-#define PHYCTRL_REG 2
+#define PHYPARAM_REG 0
+#define PHYCTRL_REG 1
/* Default PHY_SEL and REFCLKSEL configuration */
#define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6
@@ -91,8 +91,8 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct phy_provider *phy_provider;
+ unsigned int syscon_args[2];
struct phy *phy;
- int ret;
phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
if (!phy_dev)
@@ -116,25 +116,15 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev)
/* Reset port by default: only deassert it in phy init */
reset_control_assert(phy_dev->rstport);
- phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ phy_dev->regmap = syscon_regmap_lookup_by_phandle_args(np, "st,syscfg",
+ 2, syscon_args);
if (IS_ERR(phy_dev->regmap)) {
dev_err(dev, "No syscfg phandle specified\n");
return PTR_ERR(phy_dev->regmap);
}
- ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG,
- &phy_dev->param);
- if (ret) {
- dev_err(dev, "can't get phyparam offset (%d)\n", ret);
- return ret;
- }
-
- ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG,
- &phy_dev->ctrl);
- if (ret) {
- dev_err(dev, "can't get phyctrl offset (%d)\n", ret);
- return ret;
- }
+ phy_dev->param = syscon_args[PHYPARAM_REG];
+ phy_dev->ctrl = syscon_args[PHYCTRL_REG];
phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data);
if (IS_ERR(phy)) {