diff --git a/config/arm64/default b/config/arm64/default index aa51aab..21b2122 100644 --- a/config/arm64/default +++ b/config/arm64/default @@ -494,7 +494,7 @@ CONFIG_PCIE_IPROC_PLATFORM=m CONFIG_PCIE_IPROC_MSI=y CONFIG_PCI_HOST_THUNDER_PEM=y CONFIG_PCI_HOST_THUNDER_ECAM=y -CONFIG_PCIE_ROCKCHIP=y +# CONFIG_PCIE_ROCKCHIP is not set # # PCI Endpoint @@ -3697,7 +3697,8 @@ CONFIG_GPIO_PISOSR=m # CONFIG_GPIO_VIPERBOARD=m # CONFIG_W1 is not set -# CONFIG_POWER_AVS is not set +CONFIG_POWER_AVS=y +CONFIG_ROCKCHIP_IODOMAIN=m CONFIG_POWER_RESET=y CONFIG_POWER_RESET_GPIO=y CONFIG_POWER_RESET_GPIO_RESTART=y @@ -4098,7 +4099,7 @@ CONFIG_MFD_RTSX_PCI=m # CONFIG_MFD_RT5033 is not set CONFIG_MFD_RTSX_USB=m # CONFIG_MFD_RC5T583 is not set -# CONFIG_MFD_RK808 is not set +CONFIG_MFD_RK808=m # CONFIG_MFD_RN5T618 is not set # CONFIG_MFD_SEC_CORE is not set # CONFIG_MFD_SI476X_CORE is not set @@ -4180,6 +4181,7 @@ CONFIG_REGULATOR_LTC3676=m CONFIG_REGULATOR_PWM=m CONFIG_REGULATOR_QCOM_RPM=m CONFIG_REGULATOR_QCOM_SMD_RPM=m +CONFIG_REGULATOR_RK808=m # CONFIG_REGULATOR_TPS51632 is not set # CONFIG_REGULATOR_TPS62360 is not set # CONFIG_REGULATOR_TPS65023 is not set @@ -4774,6 +4776,13 @@ CONFIG_NOUVEAU_DEBUG_DEFAULT=3 CONFIG_DRM_NOUVEAU_BACKLIGHT=y # CONFIG_DRM_VGEM is not set # CONFIG_DRM_EXYNOS is not set +CONFIG_DRM_ROCKCHIP=m +CONFIG_ROCKCHIP_ANALOGIX_DP=y +CONFIG_ROCKCHIP_CDN_DP=y +CONFIG_ROCKCHIP_DW_HDMI=y +CONFIG_ROCKCHIP_DW_MIPI_DSI=y +CONFIG_ROCKCHIP_INNO_HDMI=y +CONFIG_ROCKCHIP_LVDS=y CONFIG_DRM_UDL=m CONFIG_DRM_AST=m CONFIG_DRM_MGAG200=m @@ -4825,6 +4834,7 @@ CONFIG_DRM_SIL_SII8620=m CONFIG_DRM_SII902X=m CONFIG_DRM_TOSHIBA_TC358767=m CONFIG_DRM_TI_TFP410=m +CONFIG_DRM_ANALOGIX_DP=m CONFIG_DRM_I2C_ADV7511=m # CONFIG_DRM_I2C_ADV7511_AUDIO is not set CONFIG_DRM_I2C_ADV7533=y @@ -5868,6 +5878,7 @@ CONFIG_RTC_DRV_DS1374_WDT=y CONFIG_RTC_DRV_DS1672=m # CONFIG_RTC_DRV_HYM8563 is not set CONFIG_RTC_DRV_MAX6900=m +CONFIG_RTC_DRV_RK808=m CONFIG_RTC_DRV_RS5C372=m CONFIG_RTC_DRV_ISL1208=m # CONFIG_RTC_DRV_ISL12022 is not set @@ -6126,6 +6137,7 @@ CONFIG_COMMON_CLK=y CONFIG_COMMON_CLK_VERSATILE=y CONFIG_CLK_SP810=y CONFIG_CLK_VEXPRESS_OSC=y +CONFIG_COMMON_CLK_RK808=m CONFIG_COMMON_CLK_HI655X=m CONFIG_COMMON_CLK_SCPI=m # CONFIG_COMMON_CLK_SI5351 is not set @@ -6238,6 +6250,7 @@ CONFIG_IOMMU_IO_PGTABLE_ARMV7S=y CONFIG_IOMMU_IOVA=y CONFIG_OF_IOMMU=y CONFIG_IOMMU_DMA=y +CONFIG_ROCKCHIP_IOMMU=y # CONFIG_TEGRA_IOMMU_SMMU is not set CONFIG_EXYNOS_IOMMU=y # CONFIG_EXYNOS_IOMMU_DEBUG is not set @@ -6404,41 +6417,41 @@ CONFIG_RESET_TEGRA_BPMP=y # PHY Subsystem # CONFIG_GENERIC_PHY=y +CONFIG_PHY_MT65XX_USB3=m +CONFIG_PHY_XGENE=m +CONFIG_PHY_SUN4I_USB=m +CONFIG_PHY_SUN9I_USB=m +CONFIG_PHY_MESON8B_USB2=m +CONFIG_BCM_KONA_USB2_PHY=m CONFIG_PHY_BCM_NS_USB2=m CONFIG_PHY_BCM_NS_USB3=m -CONFIG_PHY_BERLIN_USB=m +CONFIG_PHY_NS2_PCIE=m +CONFIG_PHY_BRCM_SATA=m +CONFIG_PHY_HI6220_USB=m CONFIG_PHY_BERLIN_SATA=m -CONFIG_PHY_EXYNOS_MIPI_VIDEO=m +CONFIG_PHY_BERLIN_USB=m CONFIG_PHY_PXA_28NM_HSIC=m CONFIG_PHY_PXA_28NM_USB2=m -CONFIG_PHY_EXYNOS_DP_VIDEO=m -CONFIG_BCM_KONA_USB2_PHY=m -CONFIG_PHY_MT65XX_USB3=m -CONFIG_PHY_HI6220_USB=m -CONFIG_PHY_SUN4I_USB=m -CONFIG_PHY_SUN9I_USB=m -# CONFIG_PHY_SAMSUNG_USB2 is not set -# CONFIG_PHY_EXYNOS5_USBDRD is not set -CONFIG_PHY_EXYNOS_PCIE=y CONFIG_PHY_QCOM_APQ8064_SATA=m CONFIG_PHY_QCOM_IPQ806X_SATA=m -CONFIG_PHY_ROCKCHIP_USB=m -CONFIG_PHY_ROCKCHIP_INNO_USB2=m -CONFIG_PHY_ROCKCHIP_EMMC=m -CONFIG_PHY_ROCKCHIP_DP=m -CONFIG_PHY_ROCKCHIP_PCIE=m -CONFIG_PHY_ROCKCHIP_TYPEC=m -CONFIG_PHY_XGENE=m CONFIG_PHY_QCOM_QMP=m CONFIG_PHY_QCOM_QUSB2=m CONFIG_PHY_QCOM_UFS=m CONFIG_PHY_QCOM_USB_HS=m CONFIG_PHY_QCOM_USB_HSIC=m -# CONFIG_PHY_TUSB1210 is not set -CONFIG_PHY_BRCM_SATA=m +CONFIG_PHY_ROCKCHIP_DP=m +CONFIG_PHY_ROCKCHIP_EMMC=m +CONFIG_PHY_ROCKCHIP_INNO_USB2=m +CONFIG_PHY_ROCKCHIP_PCIE=m +CONFIG_PHY_ROCKCHIP_TYPEC=m +CONFIG_PHY_ROCKCHIP_USB=m +CONFIG_PHY_EXYNOS_DP_VIDEO=m +CONFIG_PHY_EXYNOS_MIPI_VIDEO=m +CONFIG_PHY_EXYNOS_PCIE=y +# CONFIG_PHY_SAMSUNG_USB2 is not set +# CONFIG_PHY_EXYNOS5_USBDRD is not set CONFIG_PHY_TEGRA_XUSB=m -CONFIG_PHY_NS2_PCIE=m -CONFIG_PHY_MESON8B_USB2=m +# CONFIG_PHY_TUSB1210 is not set # CONFIG_POWERCAP is not set # CONFIG_MCB is not set diff --git a/patches.drivers/0001-PCI-rockchip-Factor-out-rockchip_pcie_get_phys.patch b/patches.drivers/0001-PCI-rockchip-Factor-out-rockchip_pcie_get_phys.patch new file mode 100644 index 0000000..07c8a2c --- /dev/null +++ b/patches.drivers/0001-PCI-rockchip-Factor-out-rockchip_pcie_get_phys.patch @@ -0,0 +1,65 @@ +From cad366ee2c76cc4b081262ba61a6b7e5130c8a48 Mon Sep 17 00:00:00 2001 +From: Shawn Lin +Date: Wed, 19 Jul 2017 17:55:12 +0800 +Subject: [PATCH 1/2] PCI: rockchip: Factor out rockchip_pcie_get_phys() + +Git-commit: 2ba5991f340b28fe467333740a01a17412ba63dc +Patch-mainline: v4.14-rc1 +References: fate#323912 + +We plan to introduce per-lane PHYs, so factor out rockchip_pcie_get_phys() +to make it easier in the future. No functional change intended. + +Tested-by: Jeffy Chen +Signed-off-by: Shawn Lin +Signed-off-by: Bjorn Helgaas +Reviewed-by: Brian Norris +Acked-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pci/host/pcie-rockchip.c | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c +index 4590eb95d5d8..d274ed9ddfb0 100644 +--- a/drivers/pci/host/pcie-rockchip.c ++++ b/drivers/pci/host/pcie-rockchip.c +@@ -818,6 +818,19 @@ static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) + chained_irq_exit(chip, desc); + } + ++static int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip) ++{ ++ struct device *dev = rockchip->dev; ++ ++ rockchip->phy = devm_phy_get(dev, "pcie-phy"); ++ if (IS_ERR(rockchip->phy)) { ++ if (PTR_ERR(rockchip->phy) != -EPROBE_DEFER) ++ dev_err(dev, "missing phy\n"); ++ return PTR_ERR(rockchip->phy); ++ } ++ ++ return 0; ++} + + /** + * rockchip_pcie_parse_dt - Parse Device Tree +@@ -848,12 +861,9 @@ static int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) + if (IS_ERR(rockchip->apb_base)) + return PTR_ERR(rockchip->apb_base); + +- rockchip->phy = devm_phy_get(dev, "pcie-phy"); +- if (IS_ERR(rockchip->phy)) { +- if (PTR_ERR(rockchip->phy) != -EPROBE_DEFER) +- dev_err(dev, "missing phy\n"); +- return PTR_ERR(rockchip->phy); +- } ++ err = rockchip_pcie_get_phys(rockchip); ++ if (err) ++ return err; + + rockchip->lanes = 1; + err = of_property_read_u32(node, "num-lanes", &rockchip->lanes); +-- +2.11.0 + diff --git a/patches.drivers/0001-PM-devfreq-rk3399_dmc-fix-error-return-code-in-rk339.patch b/patches.drivers/0001-PM-devfreq-rk3399_dmc-fix-error-return-code-in-rk339.patch new file mode 100644 index 0000000..b46a425 --- /dev/null +++ b/patches.drivers/0001-PM-devfreq-rk3399_dmc-fix-error-return-code-in-rk339.patch @@ -0,0 +1,46 @@ +From 734578f314b2711e1acb4df18b9faf83ab879671 Mon Sep 17 00:00:00 2001 +From: "Gustavo A. R. Silva" +Date: Mon, 3 Jul 2017 07:59:26 -0500 +Subject: [PATCH 01/86] PM / devfreq: rk3399_dmc: fix error return code in + rk3399_dmcfreq_probe() + +Git-commit: da55b1ad4b29b6ab44d5dc8ea8306260246d2699 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +platform_get_irq() returns an error code, but the rk3399_dmc +driver ignores it and always returns -EINVAL. This is not correct, +and prevents -EPROBE_DEFER from being propagated properly. + +Notice that platform_get_irq() no longer returns 0 on error: +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e330b9a6bb35dc7097a4f02cb1ae7b6f96df92af + +Print and propagate the return value of platform_get_irq on failure. + +Reviewed-by: Chanwoo Choi +Signed-off-by: Gustavo A. R. Silva +Signed-off-by: MyungJoo Ham +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/devfreq/rk3399_dmc.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c +index 40a2499730fc..1b89ebbad02c 100644 +--- a/drivers/devfreq/rk3399_dmc.c ++++ b/drivers/devfreq/rk3399_dmc.c +@@ -336,8 +336,9 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { +- dev_err(&pdev->dev, "Cannot get the dmc interrupt resource\n"); +- return -EINVAL; ++ dev_err(&pdev->dev, ++ "Cannot get the dmc interrupt resource: %d\n", irq); ++ return irq; + } + data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL); + if (!data) +-- +2.11.0 + diff --git a/patches.drivers/0001-lib-mpi-call-cond_resched-from-mpi_powm-loop.patch b/patches.drivers/0001-lib-mpi-call-cond_resched-from-mpi_powm-loop.patch new file mode 100644 index 0000000..8f652d0 --- /dev/null +++ b/patches.drivers/0001-lib-mpi-call-cond_resched-from-mpi_powm-loop.patch @@ -0,0 +1,55 @@ +From d6f56f1f998fb61bbc7c28b5a1cd675837fb1d56 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Tue, 7 Nov 2017 14:15:27 -0800 +Subject: [PATCH] lib/mpi: call cond_resched() from mpi_powm() loop + +Git-commit: 1d9ddde12e3c9bab7f3d3484eb9446315e3571ca +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: bsc#1066688 + +On a non-preemptible kernel, if KEYCTL_DH_COMPUTE is called with the +largest permitted inputs (16384 bits), the kernel spends 10+ seconds +doing modular exponentiation in mpi_powm() without rescheduling. If all +threads do it, it locks up the system. Moreover, it can cause +rcu_sched-stall warnings. + +Notwithstanding the insanity of doing this calculation in kernel mode +rather than in userspace, fix it by calling cond_resched() as each bit +from the exponent is processed. It's still noninterruptible, but at +least it's preemptible now. + +Do the cond_resched() once per bit rather than once per MPI limb because +each limb might still easily take 100+ milliseconds on slow CPUs. + +Cc: # v4.12+ +Signed-off-by: Eric Biggers +Signed-off-by: Herbert Xu +Signed-off-by: Mian Yousaf Kaukab +--- + lib/mpi/mpi-pow.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/lib/mpi/mpi-pow.c b/lib/mpi/mpi-pow.c +index e24388a863a7..468fb7cd1221 100644 +--- a/lib/mpi/mpi-pow.c ++++ b/lib/mpi/mpi-pow.c +@@ -26,6 +26,7 @@ + * however I decided to publish this code under the plain GPL. + */ + ++#include + #include + #include "mpi-internal.h" + #include "longlong.h" +@@ -256,6 +257,7 @@ int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) + } + e <<= 1; + c--; ++ cond_resched(); + } + + i--; +-- +2.11.0 + diff --git a/patches.drivers/0002-PCI-rockchip-Add-per-lane-PHY-support.patch b/patches.drivers/0002-PCI-rockchip-Add-per-lane-PHY-support.patch new file mode 100644 index 0000000..16927f0 --- /dev/null +++ b/patches.drivers/0002-PCI-rockchip-Add-per-lane-PHY-support.patch @@ -0,0 +1,180 @@ +From b5c70231ed3b1b23d4770856157c4e5ba939ede5 Mon Sep 17 00:00:00 2001 +From: Shawn Lin +Date: Fri, 25 Aug 2017 15:59:01 -0500 +Subject: [PATCH 2/2] PCI: rockchip: Add per-lane PHY support + +Git-commit: 9e87240c460637620d9b4b8c6475a53b48267dc6 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +We distinguish the legacy PHY from newer per-lane PHYs by adding legacy_phy +flag. Note that the legacy PHY is still the first option to be searched in +order not to break the backward compatibility of DTB. + +Tested-by: Jeffy Chen +Signed-off-by: Shawn Lin +[bhelgaas: tidy rockchip_pcie_get_phys()] +Signed-off-by: Bjorn Helgaas +Reviewed-by: Brian Norris +Acked-by: Kishon Vijay Abraham I + +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pci/host/pcie-rockchip.c | 78 +++++++++++++++++++++++++++++----------- + 1 file changed, 58 insertions(+), 20 deletions(-) + +diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c +index d274ed9ddfb0..4a0dfff94fa6 100644 +--- a/drivers/pci/host/pcie-rockchip.c ++++ b/drivers/pci/host/pcie-rockchip.c +@@ -47,6 +47,7 @@ + #define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val) + + #define ENCODE_LANES(x) ((((x) >> 1) & 3) << 4) ++#define MAX_LANE_NUM 4 + + #define PCIE_CLIENT_BASE 0x0 + #define PCIE_CLIENT_CONFIG (PCIE_CLIENT_BASE + 0x00) +@@ -204,7 +205,8 @@ + struct rockchip_pcie { + void __iomem *reg_base; /* DT axi-base */ + void __iomem *apb_base; /* DT apb-base */ +- struct phy *phy; ++ bool legacy_phy; ++ struct phy *phys[MAX_LANE_NUM]; + struct reset_control *core_rst; + struct reset_control *mgmt_rst; + struct reset_control *mgmt_sticky_rst; +@@ -474,7 +476,7 @@ static void rockchip_pcie_set_power_limit(struct rockchip_pcie *rockchip) + static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) + { + struct device *dev = rockchip->dev; +- int err; ++ int err, i; + u32 status; + + gpiod_set_value(rockchip->ep_gpio, 0); +@@ -497,10 +499,12 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) + return err; + } + +- err = phy_init(rockchip->phy); +- if (err < 0) { +- dev_err(dev, "fail to init phy, err %d\n", err); +- return err; ++ for (i = 0; i < MAX_LANE_NUM; i++) { ++ err = phy_init(rockchip->phys[i]); ++ if (err) { ++ dev_err(dev, "init phy%d err %d\n", i, err); ++ return err; ++ } + } + + err = reset_control_assert(rockchip->core_rst); +@@ -562,10 +566,12 @@ static int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) + PCIE_CLIENT_MODE_RC, + PCIE_CLIENT_CONFIG); + +- err = phy_power_on(rockchip->phy); +- if (err) { +- dev_err(dev, "fail to power on phy, err %d\n", err); +- return err; ++ for (i = 0; i < MAX_LANE_NUM; i++) { ++ err = phy_power_on(rockchip->phys[i]); ++ if (err) { ++ dev_err(dev, "power on phy%d err %d\n", i, err); ++ return err; ++ } + } + + /* +@@ -821,12 +827,39 @@ static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc) + static int rockchip_pcie_get_phys(struct rockchip_pcie *rockchip) + { + struct device *dev = rockchip->dev; ++ struct phy *phy; ++ char *name; ++ u32 i; ++ ++ phy = devm_phy_get(dev, "pcie-phy"); ++ if (!IS_ERR(phy)) { ++ rockchip->legacy_phy = true; ++ rockchip->phys[0] = phy; ++ dev_warn(dev, "legacy phy model is deprecated!\n"); ++ return 0; ++ } ++ ++ if (PTR_ERR(phy) == -EPROBE_DEFER) ++ return PTR_ERR(phy); ++ ++ dev_dbg(dev, "missing legacy phy; search for per-lane PHY\n"); ++ ++ for (i = 0; i < MAX_LANE_NUM; i++) { ++ name = kasprintf(GFP_KERNEL, "pcie-phy-%u", i); ++ if (!name) ++ return -ENOMEM; + +- rockchip->phy = devm_phy_get(dev, "pcie-phy"); +- if (IS_ERR(rockchip->phy)) { +- if (PTR_ERR(rockchip->phy) != -EPROBE_DEFER) +- dev_err(dev, "missing phy\n"); +- return PTR_ERR(rockchip->phy); ++ phy = devm_of_phy_get(dev, dev->of_node, name); ++ kfree(name); ++ ++ if (IS_ERR(phy)) { ++ if (PTR_ERR(phy) != -EPROBE_DEFER) ++ dev_err(dev, "missing phy for lane %d: %ld\n", ++ i, PTR_ERR(phy)); ++ return PTR_ERR(phy); ++ } ++ ++ rockchip->phys[i] = phy; + } + + return 0; +@@ -1245,7 +1278,7 @@ static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip) + static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) + { + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); +- int ret; ++ int ret, i; + + /* disable core and cli int since we don't need to ack PME_ACK */ + rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) | +@@ -1258,8 +1291,10 @@ static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev) + return ret; + } + +- phy_power_off(rockchip->phy); +- phy_exit(rockchip->phy); ++ for (i = 0; i < MAX_LANE_NUM; i++) { ++ phy_power_off(rockchip->phys[i]); ++ phy_exit(rockchip->phys[i]); ++ } + + clk_disable_unprepare(rockchip->clk_pcie_pm); + clk_disable_unprepare(rockchip->hclk_pcie); +@@ -1451,14 +1486,17 @@ static int rockchip_pcie_remove(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct rockchip_pcie *rockchip = dev_get_drvdata(dev); ++ int i; + + pci_stop_root_bus(rockchip->root_bus); + pci_remove_root_bus(rockchip->root_bus); + pci_unmap_iospace(rockchip->io); + irq_domain_remove(rockchip->irq_domain); + +- phy_power_off(rockchip->phy); +- phy_exit(rockchip->phy); ++ for (i = 0; i < MAX_LANE_NUM; i++) { ++ phy_power_off(rockchip->phys[i]); ++ phy_exit(rockchip->phys[i]); ++ } + + clk_disable_unprepare(rockchip->clk_pcie_pm); + clk_disable_unprepare(rockchip->hclk_pcie); +-- +2.11.0 + diff --git a/patches.drivers/0002-iio-adc-rockchip_saradc-add-NULL-check-on-of_match_d.patch b/patches.drivers/0002-iio-adc-rockchip_saradc-add-NULL-check-on-of_match_d.patch new file mode 100644 index 0000000..2a13fcb --- /dev/null +++ b/patches.drivers/0002-iio-adc-rockchip_saradc-add-NULL-check-on-of_match_d.patch @@ -0,0 +1,41 @@ +From c05a953472695ba7ad8a898639ba6736cf6d019a Mon Sep 17 00:00:00 2001 +From: "Gustavo A. R. Silva" +Date: Fri, 7 Jul 2017 01:51:31 -0500 +Subject: [PATCH 02/86] iio: adc: rockchip_saradc: add NULL check on + of_match_device() return value + +Git-commit: 36d311bc1663f004529d2f870c1fc939d00c49f0 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Check return value from call to of_match_device() +in order to prevent a NULL pointer dereference. + +In case of NULL print error message and return -ENODEV + +Signed-off-by: Gustavo A. R. Silva +Signed-off-by: Jonathan Cameron +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/iio/adc/rockchip_saradc.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c +index ae6d3324f518..2bf2ed15a870 100644 +--- a/drivers/iio/adc/rockchip_saradc.c ++++ b/drivers/iio/adc/rockchip_saradc.c +@@ -224,6 +224,11 @@ static int rockchip_saradc_probe(struct platform_device *pdev) + info = iio_priv(indio_dev); + + match = of_match_device(rockchip_saradc_match, &pdev->dev); ++ if (!match) { ++ dev_err(&pdev->dev, "failed to match device\n"); ++ return -ENODEV; ++ } ++ + info->data = match->data; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +-- +2.11.0 + diff --git a/patches.drivers/0003-iio-adc-rockchip_saradc-explicitly-request-exclusive.patch b/patches.drivers/0003-iio-adc-rockchip_saradc-explicitly-request-exclusive.patch new file mode 100644 index 0000000..d977ce5 --- /dev/null +++ b/patches.drivers/0003-iio-adc-rockchip_saradc-explicitly-request-exclusive.patch @@ -0,0 +1,49 @@ +From dd9a58312c08987e0c9c29c9771cde55a859f056 Mon Sep 17 00:00:00 2001 +From: Philipp Zabel +Date: Wed, 19 Jul 2017 17:25:35 +0200 +Subject: [PATCH 03/86] iio: adc: rockchip_saradc: explicitly request exclusive + reset control + +Git-commit: 87587016f614e96d873f883609a0099e820172e8 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Commit a53e35db70d1 ("reset: Ensure drivers are explicit when requesting +reset lines") started to transition the reset control request API calls +to explicitly state whether the driver needs exclusive or shared reset +control behavior. Convert all drivers requesting exclusive resets to the +explicit API call so the temporary transition helpers can be removed. + +No functional changes. + +Cc: Jonathan Cameron +Cc: Hartmut Knaack +Cc: Lars-Peter Clausen +Cc: Peter Meerwald-Stadler +Cc: Heiko Stuebner +Cc: linux-iio@vger.kernel.org +Cc: linux-rockchip@lists.infradead.org +Signed-off-by: Philipp Zabel +Signed-off-by: Jonathan Cameron +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/iio/adc/rockchip_saradc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c +index 2bf2ed15a870..5f612d694b33 100644 +--- a/drivers/iio/adc/rockchip_saradc.c ++++ b/drivers/iio/adc/rockchip_saradc.c +@@ -240,7 +240,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev) + * The reset should be an optional property, as it should work + * with old devicetrees as well + */ +- info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb"); ++ info->reset = devm_reset_control_get_exclusive(&pdev->dev, ++ "saradc-apb"); + if (IS_ERR(info->reset)) { + ret = PTR_ERR(info->reset); + if (ret != -ENOENT) +-- +2.11.0 + diff --git a/patches.drivers/0004-clocksource-drivers-rockchip-pr_err-strings-should-e.patch b/patches.drivers/0004-clocksource-drivers-rockchip-pr_err-strings-should-e.patch new file mode 100644 index 0000000..b95c87b --- /dev/null +++ b/patches.drivers/0004-clocksource-drivers-rockchip-pr_err-strings-should-e.patch @@ -0,0 +1,37 @@ +From f37e267721786d0ebafea1f3b7d0b06f521c8b23 Mon Sep 17 00:00:00 2001 +From: Arvind Yadav +Date: Mon, 25 Sep 2017 13:46:41 +0530 +Subject: [PATCH 04/86] clocksource/drivers/rockchip: pr_err() strings should + end with newlines + +Git-commit: 7d995655d7fd1606e5a76df59e32909905b17c7a +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +pr_err() messages should end with a new-line to avoid other messages being +concatenated. + +Signed-off-by: Arvind Yadav +Signed-off-by: Daniel Lezcano +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/clocksource/rockchip_timer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c +index 49c02be50eca..54884ba39aba 100644 +--- a/drivers/clocksource/rockchip_timer.c ++++ b/drivers/clocksource/rockchip_timer.c +@@ -274,7 +274,7 @@ static int __init rk_clksrc_init(struct device_node *np) + TIMER_NAME, rk_clksrc->freq, 250, 32, + clocksource_mmio_readl_down); + if (ret) { +- pr_err("Failed to register clocksource"); ++ pr_err("Failed to register clocksource\n"); + goto out_clocksource; + } + +-- +2.11.0 + diff --git a/patches.drivers/0005-pinctrl-rockchip-remove-unneeded-void-casts-in-of_ma.patch b/patches.drivers/0005-pinctrl-rockchip-remove-unneeded-void-casts-in-of_ma.patch new file mode 100644 index 0000000..cacc869 --- /dev/null +++ b/patches.drivers/0005-pinctrl-rockchip-remove-unneeded-void-casts-in-of_ma.patch @@ -0,0 +1,66 @@ +From 5875e6e93281389a51d2e7723e787aabcb1feac5 Mon Sep 17 00:00:00 2001 +From: Masahiro Yamada +Date: Fri, 28 Apr 2017 21:50:35 +0900 +Subject: [PATCH 05/86] pinctrl: rockchip: remove unneeded (void *) casts in + of_match_table + +Git-commit: cdbbd26f482b569b9c3a3b49887699f7a956d4e0 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +of_device_id::data is an opaque pointer. No explicit cast is needed. + +Signed-off-by: Masahiro Yamada +Reviewed-by: Heiko Stuebner +Signed-off-by: Linus Walleij +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pinctrl/pinctrl-rockchip.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c +index 9dd981ddbb17..173bbced3810 100644 +--- a/drivers/pinctrl/pinctrl-rockchip.c ++++ b/drivers/pinctrl/pinctrl-rockchip.c +@@ -2962,27 +2962,27 @@ static struct rockchip_pin_ctrl rk3399_pin_ctrl = { + + static const struct of_device_id rockchip_pinctrl_dt_match[] = { + { .compatible = "rockchip,rv1108-pinctrl", +- .data = (void *)&rv1108_pin_ctrl }, ++ .data = &rv1108_pin_ctrl }, + { .compatible = "rockchip,rk2928-pinctrl", +- .data = (void *)&rk2928_pin_ctrl }, ++ .data = &rk2928_pin_ctrl }, + { .compatible = "rockchip,rk3036-pinctrl", +- .data = (void *)&rk3036_pin_ctrl }, ++ .data = &rk3036_pin_ctrl }, + { .compatible = "rockchip,rk3066a-pinctrl", +- .data = (void *)&rk3066a_pin_ctrl }, ++ .data = &rk3066a_pin_ctrl }, + { .compatible = "rockchip,rk3066b-pinctrl", +- .data = (void *)&rk3066b_pin_ctrl }, ++ .data = &rk3066b_pin_ctrl }, + { .compatible = "rockchip,rk3188-pinctrl", +- .data = (void *)&rk3188_pin_ctrl }, ++ .data = &rk3188_pin_ctrl }, + { .compatible = "rockchip,rk3228-pinctrl", +- .data = (void *)&rk3228_pin_ctrl }, ++ .data = &rk3228_pin_ctrl }, + { .compatible = "rockchip,rk3288-pinctrl", +- .data = (void *)&rk3288_pin_ctrl }, ++ .data = &rk3288_pin_ctrl }, + { .compatible = "rockchip,rk3328-pinctrl", +- .data = (void *)&rk3328_pin_ctrl }, ++ .data = &rk3328_pin_ctrl }, + { .compatible = "rockchip,rk3368-pinctrl", +- .data = (void *)&rk3368_pin_ctrl }, ++ .data = &rk3368_pin_ctrl }, + { .compatible = "rockchip,rk3399-pinctrl", +- .data = (void *)&rk3399_pin_ctrl }, ++ .data = &rk3399_pin_ctrl }, + {}, + }; + +-- +2.11.0 + diff --git a/patches.drivers/0006-pinctrl-rockchip-Add-iomux-route-switching-support.patch b/patches.drivers/0006-pinctrl-rockchip-Add-iomux-route-switching-support.patch new file mode 100644 index 0000000..7468e8e --- /dev/null +++ b/patches.drivers/0006-pinctrl-rockchip-Add-iomux-route-switching-support.patch @@ -0,0 +1,161 @@ +From de6588f7a8e0df76b0a838ac48c93a5a142416fe Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Fri, 26 May 2017 15:20:20 +0800 +Subject: [PATCH 06/86] pinctrl: rockchip: Add iomux-route switching support + +Git-commit: bd35b9bf8284338db35b3ff0d391b95d67b90444 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +On the some rockchip SOCS, some things like rk3399 specific uart2 can use +multiple pins. Somewhere between the pin io-cells and the uart it seems +to have some sort of switch to decide to which pin to actually route the +data. + ++-------+ +--------+ /- GPIO4_B0 (pinmux 2) + +| uart2 | -- | switch | --- GPIO4_C0 (pinmux 2) + ++-------+ +--------+ \- GPIO4_C3 (pinmux 2) +(switch selects one of the 3 pins base on the GRF_SOC_CON7[BIT0, BIT1]) + +The routing switch is determined by one pin of a specific group to be set +to its special pinmux function. If the pinmux setting is wrong for that +pin the ip block won't work correctly anyway. + +Signed-off-by: David Wu +Reviewed-by: Heiko Stuebner +Signed-off-by: Linus Walleij +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pinctrl/pinctrl-rockchip.c | 65 +++++++++++++++++++++++++++++++++++++- + 1 file changed, 64 insertions(+), 1 deletion(-) + +diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c +index 173bbced3810..b4ca2f4ab304 100644 +--- a/drivers/pinctrl/pinctrl-rockchip.c ++++ b/drivers/pinctrl/pinctrl-rockchip.c +@@ -143,6 +143,7 @@ struct rockchip_drv { + * @gpio_chip: gpiolib chip + * @grange: gpio range + * @slock: spinlock for the gpio bank ++ * @route_mask: bits describing the routing pins of per bank + */ + struct rockchip_pin_bank { + void __iomem *reg_base; +@@ -165,6 +166,7 @@ struct rockchip_pin_bank { + struct pinctrl_gpio_range grange; + raw_spinlock_t slock; + u32 toggle_edge_mode; ++ u32 route_mask; + }; + + #define PIN_BANK(id, pins, label) \ +@@ -288,6 +290,22 @@ struct rockchip_pin_bank { + } + + /** ++ * struct rockchip_mux_recalced_data: represent a pin iomux data. ++ * @bank_num: bank number. ++ * @pin: index at register or used to calc index. ++ * @func: the min pin. ++ * @route_offset: the max pin. ++ * @route_val: the register offset. ++ */ ++struct rockchip_mux_route_data { ++ u8 bank_num; ++ u8 pin; ++ u8 func; ++ u32 route_offset; ++ u32 route_val; ++}; ++ ++/** + */ + struct rockchip_pin_ctrl { + struct rockchip_pin_bank *pin_banks; +@@ -299,6 +317,8 @@ struct rockchip_pin_ctrl { + int pmu_mux_offset; + int grf_drv_offset; + int pmu_drv_offset; ++ struct rockchip_mux_route_data *iomux_routes; ++ u32 niomux_routes; + + void (*pull_calc_reg)(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, +@@ -580,6 +600,30 @@ static void rk3328_recalc_mux(u8 bank_num, int pin, int *reg, + *bit = data->bit; + } + ++static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, ++ int mux, u32 *reg, u32 *value) ++{ ++ struct rockchip_pinctrl *info = bank->drvdata; ++ struct rockchip_pin_ctrl *ctrl = info->ctrl; ++ struct rockchip_mux_route_data *data; ++ int i; ++ ++ for (i = 0; i < ctrl->niomux_routes; i++) { ++ data = &ctrl->iomux_routes[i]; ++ if ((data->bank_num == bank->bank_num) && ++ (data->pin == pin) && (data->func == mux)) ++ break; ++ } ++ ++ if (i >= ctrl->niomux_routes) ++ return false; ++ ++ *reg = data->route_offset; ++ *value = data->route_val; ++ ++ return true; ++} ++ + static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) + { + struct rockchip_pinctrl *info = bank->drvdata; +@@ -678,7 +722,7 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) + struct regmap *regmap; + int reg, ret, mask, mux_type; + u8 bit; +- u32 data, rmask; ++ u32 data, rmask, route_reg, route_val; + + ret = rockchip_verify_mux(bank, pin, mux); + if (ret < 0) +@@ -714,6 +758,15 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) + if (ctrl->iomux_recalc && (mux_type & IOMUX_RECALCED)) + ctrl->iomux_recalc(bank->bank_num, pin, ®, &bit, &mask); + ++ if (bank->route_mask & BIT(pin)) { ++ if (rockchip_get_mux_route(bank, pin, mux, &route_reg, ++ &route_val)) { ++ ret = regmap_write(regmap, route_reg, route_val); ++ if (ret) ++ return ret; ++ } ++ } ++ + data = (mask << (bit + 16)); + rmask = data | (data >> 16); + data |= (mux & mask) << bit; +@@ -2549,6 +2602,16 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( + + bank_pins += 8; + } ++ ++ /* calculate the per-bank route_mask */ ++ for (j = 0; j < ctrl->niomux_routes; j++) { ++ int pin = 0; ++ ++ if (ctrl->iomux_routes[j].bank_num == bank->bank_num) { ++ pin = ctrl->iomux_routes[j].pin; ++ bank->route_mask |= BIT(pin); ++ } ++ } + } + + return ctrl; +-- +2.11.0 + diff --git a/patches.drivers/0007-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch b/patches.drivers/0007-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch new file mode 100644 index 0000000..a5b03c5 --- /dev/null +++ b/patches.drivers/0007-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch @@ -0,0 +1,174 @@ +From 14c4f31f10dd866c6f7df46d7150c54858f8a30f Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Fri, 26 May 2017 15:20:21 +0800 +Subject: [PATCH 07/86] pinctrl: rockchip: Add iomux-route switching support + for rk3228 + +Git-commit: d4970ee076f9aed396c322b41f56443a617116df +Patch-mainline: v4.13-rc1 +References: fate#323912 + +There are 9 IP blocks pin routes need to be switched, that are +pwm-0, pwm-1, pwm-2, pwm-3, sdio, spi, emmc, uart2, uart1. + +Signed-off-by: David Wu +Reviewed-by: Heiko Stuebner +Signed-off-by: Linus Walleij +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pinctrl/pinctrl-rockchip.c | 132 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 132 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c +index b4ca2f4ab304..5df96319036f 100644 +--- a/drivers/pinctrl/pinctrl-rockchip.c ++++ b/drivers/pinctrl/pinctrl-rockchip.c +@@ -600,6 +600,136 @@ static void rk3328_recalc_mux(u8 bank_num, int pin, int *reg, + *bit = data->bit; + } + ++static struct rockchip_mux_route_data rk3228_mux_route_data[] = { ++ { ++ /* pwm0-0 */ ++ .bank_num = 0, ++ .pin = 26, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16), ++ }, { ++ /* pwm0-1 */ ++ .bank_num = 3, ++ .pin = 21, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16) | BIT(0), ++ }, { ++ /* pwm1-0 */ ++ .bank_num = 0, ++ .pin = 27, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 1), ++ }, { ++ /* pwm1-1 */ ++ .bank_num = 0, ++ .pin = 30, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 1) | BIT(1), ++ }, { ++ /* pwm2-0 */ ++ .bank_num = 0, ++ .pin = 28, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 2), ++ }, { ++ /* pwm2-1 */ ++ .bank_num = 1, ++ .pin = 12, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 2) | BIT(2), ++ }, { ++ /* pwm3-0 */ ++ .bank_num = 3, ++ .pin = 26, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 3), ++ }, { ++ /* pwm3-1 */ ++ .bank_num = 1, ++ .pin = 11, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 3) | BIT(3), ++ }, { ++ /* sdio-0_d0 */ ++ .bank_num = 1, ++ .pin = 1, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 4), ++ }, { ++ /* sdio-1_d0 */ ++ .bank_num = 3, ++ .pin = 2, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 4) | BIT(4), ++ }, { ++ /* spi-0_rx */ ++ .bank_num = 0, ++ .pin = 13, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 5), ++ }, { ++ /* spi-1_rx */ ++ .bank_num = 2, ++ .pin = 0, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 5) | BIT(5), ++ }, { ++ /* emmc-0_cmd */ ++ .bank_num = 1, ++ .pin = 22, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 7), ++ }, { ++ /* emmc-1_cmd */ ++ .bank_num = 2, ++ .pin = 4, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 7) | BIT(7), ++ }, { ++ /* uart2-0_rx */ ++ .bank_num = 1, ++ .pin = 19, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 8), ++ }, { ++ /* uart2-1_rx */ ++ .bank_num = 1, ++ .pin = 10, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 8) | BIT(8), ++ }, { ++ /* uart1-0_rx */ ++ .bank_num = 1, ++ .pin = 10, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 11), ++ }, { ++ /* uart1-1_rx */ ++ .bank_num = 3, ++ .pin = 13, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 11) | BIT(11), ++ }, ++}; ++ + static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, + int mux, u32 *reg, u32 *value) + { +@@ -2862,6 +2992,8 @@ static struct rockchip_pin_ctrl rk3228_pin_ctrl = { + .label = "RK3228-GPIO", + .type = RK3288, + .grf_mux_offset = 0x0, ++ .iomux_routes = rk3228_mux_route_data, ++ .niomux_routes = ARRAY_SIZE(rk3228_mux_route_data), + .pull_calc_reg = rk3228_calc_pull_reg_and_bit, + .drv_calc_reg = rk3228_calc_drv_reg_and_bit, + }; +-- +2.11.0 + diff --git a/patches.drivers/0008-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch b/patches.drivers/0008-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch new file mode 100644 index 0000000..2a633f4 --- /dev/null +++ b/patches.drivers/0008-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch @@ -0,0 +1,125 @@ +From 475956ac654268726da37522cadc9260db01231d Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Fri, 26 May 2017 15:20:22 +0800 +Subject: [PATCH 08/86] pinctrl: rockchip: Add iomux-route switching support + for rk3328 + +Git-commit: cedc964a59d48c793ddc0884b2f72a68fc234ae4 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +There are 8 IP blocks pin routes need to be switched, that are +uart2dbg, gmac-m1-optimized, pdm, spi, i2s2, card, tsp, cif. + +Signed-off-by: David Wu +Reviewed-by: Heiko Stuebner +Signed-off-by: Linus Walleij +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pinctrl/pinctrl-rockchip.c | 83 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 83 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c +index 5df96319036f..a0568a2cc125 100644 +--- a/drivers/pinctrl/pinctrl-rockchip.c ++++ b/drivers/pinctrl/pinctrl-rockchip.c +@@ -730,6 +730,87 @@ static struct rockchip_mux_route_data rk3228_mux_route_data[] = { + }, + }; + ++static struct rockchip_mux_route_data rk3328_mux_route_data[] = { ++ { ++ /* uart2dbg_rxm0 */ ++ .bank_num = 1, ++ .pin = 1, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16) | BIT(16 + 1), ++ }, { ++ /* uart2dbg_rxm1 */ ++ .bank_num = 2, ++ .pin = 1, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16) | BIT(16 + 1) | BIT(0), ++ }, { ++ /* gmac-m1-optimized_rxd0 */ ++ .bank_num = 1, ++ .pin = 11, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 2) | BIT(16 + 10) | BIT(2) | BIT(10), ++ }, { ++ /* pdm_sdi0m0 */ ++ .bank_num = 2, ++ .pin = 19, ++ .func = 2, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 3), ++ }, { ++ /* pdm_sdi0m1 */ ++ .bank_num = 1, ++ .pin = 23, ++ .func = 3, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 3) | BIT(3), ++ }, { ++ /* spi_rxdm2 */ ++ .bank_num = 3, ++ .pin = 2, ++ .func = 4, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 4) | BIT(16 + 5) | BIT(5), ++ }, { ++ /* i2s2_sdim0 */ ++ .bank_num = 1, ++ .pin = 24, ++ .func = 1, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 6), ++ }, { ++ /* i2s2_sdim1 */ ++ .bank_num = 3, ++ .pin = 2, ++ .func = 6, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 6) | BIT(6), ++ }, { ++ /* card_iom1 */ ++ .bank_num = 2, ++ .pin = 22, ++ .func = 3, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 7) | BIT(7), ++ }, { ++ /* tsp_d5m1 */ ++ .bank_num = 2, ++ .pin = 16, ++ .func = 3, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 8) | BIT(8), ++ }, { ++ /* cif_data5m1 */ ++ .bank_num = 2, ++ .pin = 16, ++ .func = 4, ++ .route_offset = 0x50, ++ .route_val = BIT(16 + 9) | BIT(9), ++ }, ++}; ++ + static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, + int mux, u32 *reg, u32 *value) + { +@@ -3061,6 +3142,8 @@ static struct rockchip_pin_ctrl rk3328_pin_ctrl = { + .label = "RK3328-GPIO", + .type = RK3288, + .grf_mux_offset = 0x0, ++ .iomux_routes = rk3328_mux_route_data, ++ .niomux_routes = ARRAY_SIZE(rk3328_mux_route_data), + .pull_calc_reg = rk3228_calc_pull_reg_and_bit, + .drv_calc_reg = rk3228_calc_drv_reg_and_bit, + .iomux_recalc = rk3328_recalc_mux, +-- +2.11.0 + diff --git a/patches.drivers/0009-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch b/patches.drivers/0009-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch new file mode 100644 index 0000000..f218bc4 --- /dev/null +++ b/patches.drivers/0009-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch @@ -0,0 +1,83 @@ +From 666db9c0405102aa362c0eb6a588d5772b841220 Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Fri, 26 May 2017 15:20:23 +0800 +Subject: [PATCH 09/86] pinctrl: rockchip: Add iomux-route switching support + for rk3399 + +Git-commit: accc1ce7d2ffc6419a8eaf8c0190d9240df0c43f +Patch-mainline: v4.13-rc1 +References: fate#323912 + +There are 2 IP blocks pin routes need to be switched, that are +uart2dbg, pcie_clkreq. + +Signed-off-by: David Wu +Reviewed-by: Heiko Stuebner +Signed-off-by: Linus Walleij +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pinctrl/pinctrl-rockchip.c | 41 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c +index a0568a2cc125..e831647c56a6 100644 +--- a/drivers/pinctrl/pinctrl-rockchip.c ++++ b/drivers/pinctrl/pinctrl-rockchip.c +@@ -811,6 +811,45 @@ static struct rockchip_mux_route_data rk3328_mux_route_data[] = { + }, + }; + ++static struct rockchip_mux_route_data rk3399_mux_route_data[] = { ++ { ++ /* uart2dbga_rx */ ++ .bank_num = 4, ++ .pin = 8, ++ .func = 2, ++ .route_offset = 0xe21c, ++ .route_val = BIT(16 + 10) | BIT(16 + 11), ++ }, { ++ /* uart2dbgb_rx */ ++ .bank_num = 4, ++ .pin = 16, ++ .func = 2, ++ .route_offset = 0xe21c, ++ .route_val = BIT(16 + 10) | BIT(16 + 11) | BIT(10), ++ }, { ++ /* uart2dbgc_rx */ ++ .bank_num = 4, ++ .pin = 19, ++ .func = 1, ++ .route_offset = 0xe21c, ++ .route_val = BIT(16 + 10) | BIT(16 + 11) | BIT(11), ++ }, { ++ /* pcie_clkreqn */ ++ .bank_num = 2, ++ .pin = 26, ++ .func = 2, ++ .route_offset = 0xe21c, ++ .route_val = BIT(16 + 14), ++ }, { ++ /* pcie_clkreqnb */ ++ .bank_num = 4, ++ .pin = 24, ++ .func = 1, ++ .route_offset = 0xe21c, ++ .route_val = BIT(16 + 14) | BIT(14), ++ }, ++}; ++ + static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, + int mux, u32 *reg, u32 *value) + { +@@ -3234,6 +3273,8 @@ static struct rockchip_pin_ctrl rk3399_pin_ctrl = { + .pmu_mux_offset = 0x0, + .grf_drv_offset = 0xe100, + .pmu_drv_offset = 0x80, ++ .iomux_routes = rk3399_mux_route_data, ++ .niomux_routes = ARRAY_SIZE(rk3399_mux_route_data), + .pull_calc_reg = rk3399_calc_pull_reg_and_bit, + .drv_calc_reg = rk3399_calc_drv_reg_and_bit, + }; +-- +2.11.0 + diff --git a/patches.drivers/0010-pinctrl-rockchip-Use-common-interface-for-recalced-i.patch b/patches.drivers/0010-pinctrl-rockchip-Use-common-interface-for-recalced-i.patch new file mode 100644 index 0000000..d8557c5 --- /dev/null +++ b/patches.drivers/0010-pinctrl-rockchip-Use-common-interface-for-recalced-i.patch @@ -0,0 +1,234 @@ +From f18e92e0bdb4b1b0ed7799e243684b1fa8eed642 Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Fri, 21 Jul 2017 14:27:14 +0800 +Subject: [PATCH 10/86] pinctrl: rockchip: Use common interface for recalced + iomux + +Git-commit: c04c3fa65ac5c66711584322669b8e8fd9ae2a7a +Patch-mainline: v4.14-rc1 +References: fate#323912 + +The other Socs also need the feature of recalced iomux, so +make it as a common interface like iomux route feature. + +Signed-off-by: David Wu +Reviewed-by: Heiko Stuebner +Signed-off-by: Linus Walleij +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pinctrl/pinctrl-rockchip.c | 89 +++++++++++++++++++++----------------- + 1 file changed, 50 insertions(+), 39 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c +index e831647c56a6..fd4e491225df 100644 +--- a/drivers/pinctrl/pinctrl-rockchip.c ++++ b/drivers/pinctrl/pinctrl-rockchip.c +@@ -76,7 +76,6 @@ enum rockchip_pinctrl_type { + #define IOMUX_SOURCE_PMU BIT(2) + #define IOMUX_UNROUTED BIT(3) + #define IOMUX_WIDTH_3BIT BIT(4) +-#define IOMUX_RECALCED BIT(5) + + /** + * @type: iomux variant using IOMUX_* constants +@@ -166,6 +165,7 @@ struct rockchip_pin_bank { + struct pinctrl_gpio_range grange; + raw_spinlock_t slock; + u32 toggle_edge_mode; ++ u32 recalced_mask; + u32 route_mask; + }; + +@@ -291,6 +291,22 @@ struct rockchip_pin_bank { + + /** + * struct rockchip_mux_recalced_data: represent a pin iomux data. ++ * @num: bank number. ++ * @pin: pin number. ++ * @bit: index at register. ++ * @reg: register offset. ++ * @mask: mask bit ++ */ ++struct rockchip_mux_recalced_data { ++ u8 num; ++ u8 pin; ++ u8 reg; ++ u8 bit; ++ u8 mask; ++}; ++ ++/** ++ * struct rockchip_mux_recalced_data: represent a pin iomux data. + * @bank_num: bank number. + * @pin: index at register or used to calc index. + * @func: the min pin. +@@ -317,6 +333,8 @@ struct rockchip_pin_ctrl { + int pmu_mux_offset; + int grf_drv_offset; + int pmu_drv_offset; ++ struct rockchip_mux_recalced_data *iomux_recalced; ++ u32 niomux_recalced; + struct rockchip_mux_route_data *iomux_routes; + u32 niomux_routes; + +@@ -326,8 +344,6 @@ struct rockchip_pin_ctrl { + void (*drv_calc_reg)(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit); +- void (*iomux_recalc)(u8 bank_num, int pin, int *reg, +- u8 *bit, int *mask); + int (*schmitt_calc_reg)(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit); +@@ -382,22 +398,6 @@ struct rockchip_pinctrl { + unsigned int nfunctions; + }; + +-/** +- * struct rockchip_mux_recalced_data: represent a pin iomux data. +- * @num: bank number. +- * @pin: pin number. +- * @bit: index at register. +- * @reg: register offset. +- * @mask: mask bit +- */ +-struct rockchip_mux_recalced_data { +- u8 num; +- u8 pin; +- u8 reg; +- u8 bit; +- u8 mask; +-}; +- + static struct regmap_config rockchip_regmap_config = { + .reg_bits = 32, + .val_bits = 32, +@@ -557,7 +557,7 @@ static const struct pinctrl_ops rockchip_pctrl_ops = { + * Hardware access + */ + +-static const struct rockchip_mux_recalced_data rk3328_mux_recalced_data[] = { ++static struct rockchip_mux_recalced_data rk3328_mux_recalced_data[] = { + { + .num = 2, + .pin = 12, +@@ -579,20 +579,22 @@ static const struct rockchip_mux_recalced_data rk3328_mux_recalced_data[] = { + }, + }; + +-static void rk3328_recalc_mux(u8 bank_num, int pin, int *reg, +- u8 *bit, int *mask) ++static void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin, ++ int *reg, u8 *bit, int *mask) + { +- const struct rockchip_mux_recalced_data *data = NULL; ++ struct rockchip_pinctrl *info = bank->drvdata; ++ struct rockchip_pin_ctrl *ctrl = info->ctrl; ++ struct rockchip_mux_recalced_data *data; + int i; + +- for (i = 0; i < ARRAY_SIZE(rk3328_mux_recalced_data); i++) +- if (rk3328_mux_recalced_data[i].num == bank_num && +- rk3328_mux_recalced_data[i].pin == pin) { +- data = &rk3328_mux_recalced_data[i]; ++ for (i = 0; i < ctrl->niomux_recalced; i++) { ++ data = &ctrl->iomux_recalced[i]; ++ if (data->num == bank->bank_num && ++ data->pin == pin) + break; +- } ++ } + +- if (!data) ++ if (i >= ctrl->niomux_recalced) + return; + + *reg = data->reg; +@@ -877,7 +879,6 @@ static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, + static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) + { + struct rockchip_pinctrl *info = bank->drvdata; +- struct rockchip_pin_ctrl *ctrl = info->ctrl; + int iomux_num = (pin / 8); + struct regmap *regmap; + unsigned int val; +@@ -916,8 +917,8 @@ static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) + mask = 0x3; + } + +- if (ctrl->iomux_recalc && (mux_type & IOMUX_RECALCED)) +- ctrl->iomux_recalc(bank->bank_num, pin, ®, &bit, &mask); ++ if (bank->recalced_mask & BIT(pin)) ++ rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask); + + ret = regmap_read(regmap, reg, &val); + if (ret) +@@ -967,7 +968,6 @@ static int rockchip_verify_mux(struct rockchip_pin_bank *bank, + static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) + { + struct rockchip_pinctrl *info = bank->drvdata; +- struct rockchip_pin_ctrl *ctrl = info->ctrl; + int iomux_num = (pin / 8); + struct regmap *regmap; + int reg, ret, mask, mux_type; +@@ -1005,8 +1005,8 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) + mask = 0x3; + } + +- if (ctrl->iomux_recalc && (mux_type & IOMUX_RECALCED)) +- ctrl->iomux_recalc(bank->bank_num, pin, ®, &bit, &mask); ++ if (bank->recalced_mask & BIT(pin)) ++ rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask); + + if (bank->route_mask & BIT(pin)) { + if (rockchip_get_mux_route(bank, pin, mux, &route_reg, +@@ -2853,6 +2853,16 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( + bank_pins += 8; + } + ++ /* calculate the per-bank recalced_mask */ ++ for (j = 0; j < ctrl->niomux_recalced; j++) { ++ int pin = 0; ++ ++ if (ctrl->iomux_recalced[j].num == bank->bank_num) { ++ pin = ctrl->iomux_recalced[j].pin; ++ bank->recalced_mask |= BIT(pin); ++ } ++ } ++ + /* calculate the per-bank route_mask */ + for (j = 0; j < ctrl->niomux_routes; j++) { + int pin = 0; +@@ -3165,12 +3175,12 @@ static struct rockchip_pin_bank rk3328_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, +- IOMUX_WIDTH_3BIT | IOMUX_RECALCED, +- IOMUX_WIDTH_3BIT | IOMUX_RECALCED, ++ IOMUX_WIDTH_3BIT, ++ IOMUX_WIDTH_3BIT, + 0), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", + IOMUX_WIDTH_3BIT, +- IOMUX_WIDTH_3BIT | IOMUX_RECALCED, ++ IOMUX_WIDTH_3BIT, + 0, + 0), + }; +@@ -3181,11 +3191,12 @@ static struct rockchip_pin_ctrl rk3328_pin_ctrl = { + .label = "RK3328-GPIO", + .type = RK3288, + .grf_mux_offset = 0x0, ++ .iomux_recalced = rk3328_mux_recalced_data, ++ .niomux_recalced = ARRAY_SIZE(rk3328_mux_recalced_data), + .iomux_routes = rk3328_mux_route_data, + .niomux_routes = ARRAY_SIZE(rk3328_mux_route_data), + .pull_calc_reg = rk3228_calc_pull_reg_and_bit, + .drv_calc_reg = rk3228_calc_drv_reg_and_bit, +- .iomux_recalc = rk3328_recalc_mux, + .schmitt_calc_reg = rk3328_calc_schmitt_reg_and_bit, + }; + +-- +2.11.0 + diff --git a/patches.drivers/0011-pinctrl-rockchip-Fix-the-rk3399-gpio0-and-gpio1-bank.patch b/patches.drivers/0011-pinctrl-rockchip-Fix-the-rk3399-gpio0-and-gpio1-bank.patch new file mode 100644 index 0000000..62b8d74 --- /dev/null +++ b/patches.drivers/0011-pinctrl-rockchip-Fix-the-rk3399-gpio0-and-gpio1-bank.patch @@ -0,0 +1,56 @@ +From 2e20dd6104584f9e50d54e45b6bb24b68e8ec9c6 Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Sat, 30 Sep 2017 20:13:20 +0800 +Subject: [PATCH 11/86] pinctrl: rockchip: Fix the rk3399 gpio0 and gpio1 + banks' drv_offset at pmu grf + +Git-commit: c437f65c42d2640ababace37eb89bff2395c1dc3 +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +The offset of gpio0 and gpio1 bank drive strength is 0x8, not 0x4. +But the mux is 0x4, we couldn't use the IOMUX_WIDTH_4BIT flag, so +we give them actual offset. + +Signed-off-by: David Wu +Reviewed-by: Heiko Stuebner +Signed-off-by: Linus Walleij +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pinctrl/pinctrl-rockchip.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c +index fd4e491225df..a1928ebb6388 100644 +--- a/drivers/pinctrl/pinctrl-rockchip.c ++++ b/drivers/pinctrl/pinctrl-rockchip.c +@@ -3232,8 +3232,8 @@ static struct rockchip_pin_bank rk3399_pin_banks[] = { + DRV_TYPE_IO_1V8_ONLY, + DRV_TYPE_IO_DEFAULT, + DRV_TYPE_IO_DEFAULT, +- 0x0, +- 0x8, ++ 0x80, ++ 0x88, + -1, + -1, + PULL_TYPE_IO_1V8_ONLY, +@@ -3249,10 +3249,10 @@ static struct rockchip_pin_bank rk3399_pin_banks[] = { + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_OR_3V0, +- 0x20, +- 0x28, +- 0x30, +- 0x38 ++ 0xa0, ++ 0xa8, ++ 0xb0, ++ 0xb8 + ), + PIN_BANK_DRV_FLAGS_PULL_FLAGS(2, 32, "gpio2", DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_OR_3V0, +-- +2.11.0 + diff --git a/patches.drivers/0012-iommu-rockchip-Enable-Rockchip-IOMMU-on-ARM64.patch b/patches.drivers/0012-iommu-rockchip-Enable-Rockchip-IOMMU-on-ARM64.patch new file mode 100644 index 0000000..a73ba06 --- /dev/null +++ b/patches.drivers/0012-iommu-rockchip-Enable-Rockchip-IOMMU-on-ARM64.patch @@ -0,0 +1,39 @@ +From bd9fdcffa0ccf858391511cfa83eac9025b488b6 Mon Sep 17 00:00:00 2001 +From: Simon Xue +Date: Wed, 3 May 2017 17:19:40 +0200 +Subject: [PATCH 12/86] iommu/rockchip: Enable Rockchip IOMMU on ARM64 + +Git-commit: 4f1fcfe94c1700f9e891adc1ca364a5cef00bbfd +Patch-mainline: v4.13-rc1 +References: fate#323912 + +This patch makes it possible to compile the rockchip-iommu driver on +ARM64, so that it can be used with 64-bit SoCs equipped with this type +of IOMMU. + +Signed-off-by: Simon Xue +Signed-off-by: Shunqian Zheng +Signed-off-by: Tomasz Figa +Reviewed-by: Matthias Brugger +Signed-off-by: Joerg Roedel +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/iommu/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig +index 6ee3a25ae731..99c6366a2551 100644 +--- a/drivers/iommu/Kconfig ++++ b/drivers/iommu/Kconfig +@@ -219,7 +219,7 @@ config OMAP_IOMMU_DEBUG + + config ROCKCHIP_IOMMU + bool "Rockchip IOMMU Support" +- depends on ARM ++ depends on ARM || ARM64 + depends on ARCH_ROCKCHIP || COMPILE_TEST + select IOMMU_API + select ARM_DMA_USE_IOMMU +-- +2.11.0 + diff --git a/patches.drivers/0013-iommu-rockchip-add-multi-irqs-support.patch b/patches.drivers/0013-iommu-rockchip-add-multi-irqs-support.patch new file mode 100644 index 0000000..3963bad --- /dev/null +++ b/patches.drivers/0013-iommu-rockchip-add-multi-irqs-support.patch @@ -0,0 +1,89 @@ +From e3c8f4799a03e13ecad4806703d493174a4e2ac8 Mon Sep 17 00:00:00 2001 +From: Simon Xue +Date: Mon, 24 Jul 2017 10:37:14 +0800 +Subject: [PATCH 13/86] iommu/rockchip: add multi irqs support + +Git-commit: 03f732f89034b3f5fbe7ef34cd3482f2e9c335cf +Patch-mainline: v4.14-rc1 +References: fate#323912 + +RK3368 vpu mmu have two irqs, this patch support multi irqs + +Signed-off-by: Simon Xue +Signed-off-by: Joerg Roedel +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/iommu/rockchip-iommu.c | 35 ++++++++++++++++++++++++++--------- + 1 file changed, 26 insertions(+), 9 deletions(-) + +diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c +index 4ba48a26b389..e2852b041231 100644 +--- a/drivers/iommu/rockchip-iommu.c ++++ b/drivers/iommu/rockchip-iommu.c +@@ -90,7 +90,8 @@ struct rk_iommu { + struct device *dev; + void __iomem **bases; + int num_mmu; +- int irq; ++ int *irq; ++ int num_irq; + struct iommu_device iommu; + struct list_head node; /* entry in rk_iommu_domain.iommus */ + struct iommu_domain *domain; /* domain to which iommu is attached */ +@@ -825,10 +826,12 @@ static int rk_iommu_attach_device(struct iommu_domain *domain, + + iommu->domain = domain; + +- ret = devm_request_irq(iommu->dev, iommu->irq, rk_iommu_irq, +- IRQF_SHARED, dev_name(dev), iommu); +- if (ret) +- return ret; ++ for (i = 0; i < iommu->num_irq; i++) { ++ ret = devm_request_irq(iommu->dev, iommu->irq[i], rk_iommu_irq, ++ IRQF_SHARED, dev_name(dev), iommu); ++ if (ret) ++ return ret; ++ } + + for (i = 0; i < iommu->num_mmu; i++) { + rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, +@@ -878,7 +881,8 @@ static void rk_iommu_detach_device(struct iommu_domain *domain, + } + rk_iommu_disable_stall(iommu); + +- devm_free_irq(iommu->dev, iommu->irq, iommu); ++ for (i = 0; i < iommu->num_irq; i++) ++ devm_free_irq(iommu->dev, iommu->irq[i], iommu); + + iommu->domain = NULL; + +@@ -1157,10 +1161,23 @@ static int rk_iommu_probe(struct platform_device *pdev) + if (iommu->num_mmu == 0) + return PTR_ERR(iommu->bases[0]); + +- iommu->irq = platform_get_irq(pdev, 0); +- if (iommu->irq < 0) { +- dev_err(dev, "Failed to get IRQ, %d\n", iommu->irq); ++ iommu->num_irq = platform_irq_count(pdev); ++ if (iommu->num_irq < 0) ++ return iommu->num_irq; ++ if (iommu->num_irq == 0) + return -ENXIO; ++ ++ iommu->irq = devm_kcalloc(dev, iommu->num_irq, sizeof(*iommu->irq), ++ GFP_KERNEL); ++ if (!iommu->irq) ++ return -ENOMEM; ++ ++ for (i = 0; i < iommu->num_irq; i++) { ++ iommu->irq[i] = platform_get_irq(pdev, i); ++ if (iommu->irq[i] < 0) { ++ dev_err(dev, "Failed to get IRQ, %d\n", iommu->irq[i]); ++ return -ENXIO; ++ } + } + + err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev)); +-- +2.11.0 + diff --git a/patches.drivers/0014-iommu-rockchip-ignore-isp-mmu-reset-operation.patch b/patches.drivers/0014-iommu-rockchip-ignore-isp-mmu-reset-operation.patch new file mode 100644 index 0000000..b2e84c6 --- /dev/null +++ b/patches.drivers/0014-iommu-rockchip-ignore-isp-mmu-reset-operation.patch @@ -0,0 +1,55 @@ +From 70c45286e5697f4b2173a7dd9fc722375a9cfb37 Mon Sep 17 00:00:00 2001 +From: Simon Xue +Date: Mon, 24 Jul 2017 10:37:15 +0800 +Subject: [PATCH 14/86] iommu/rockchip: ignore isp mmu reset operation + +Git-commit: c3aa47424918acdfed8982d5a3588351ebefdfc1 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +ISP mmu can't support reset operation, it won't get the +expected result when reset, but rest functions work normally. +Add this patch as a WA for this issue. + +Signed-off-by: Simon Xue +Signed-off-by: Joerg Roedel +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/iommu/rockchip-iommu.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c +index e2852b041231..78ea341c7c75 100644 +--- a/drivers/iommu/rockchip-iommu.c ++++ b/drivers/iommu/rockchip-iommu.c +@@ -92,6 +92,7 @@ struct rk_iommu { + int num_mmu; + int *irq; + int num_irq; ++ bool reset_disabled; + struct iommu_device iommu; + struct list_head node; /* entry in rk_iommu_domain.iommus */ + struct iommu_domain *domain; /* domain to which iommu is attached */ +@@ -415,6 +416,9 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu) + int ret, i; + u32 dte_addr; + ++ if (iommu->reset_disabled) ++ return 0; ++ + /* + * Check if register DTE_ADDR is working by writing DTE_ADDR_DUMMY + * and verifying that upper 5 nybbles are read back. +@@ -1180,6 +1184,9 @@ static int rk_iommu_probe(struct platform_device *pdev) + } + } + ++ iommu->reset_disabled = device_property_read_bool(dev, ++ "rockchip,disable-mmu-reset"); ++ + err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev)); + if (err) + return err; +-- +2.11.0 + diff --git a/patches.drivers/0015-spi-rockchip-fix-error-handling-when-probe.patch b/patches.drivers/0015-spi-rockchip-fix-error-handling-when-probe.patch new file mode 100644 index 0000000..ee7527d --- /dev/null +++ b/patches.drivers/0015-spi-rockchip-fix-error-handling-when-probe.patch @@ -0,0 +1,115 @@ +From 415d52ab7f221846bd0171072216649087210e2e Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Tue, 13 Jun 2017 13:25:40 +0800 +Subject: [PATCH 15/86] spi: rockchip: fix error handling when probe + +Git-commit: c351587e2502050f36d16e9e32c6c98975ecd6a2 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +After failed to request dma tx chain, we need to disable pm_runtime. +Also cleanup error labels for better readability. + +Signed-off-by: Jeffy Chen +Reviewed-by: Brian Norris +Signed-off-by: Mark Brown +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/spi/spi-rockchip.c | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c +index acf31f36b898..bab9b13f0ad0 100644 +--- a/drivers/spi/spi-rockchip.c ++++ b/drivers/spi/spi-rockchip.c +@@ -684,33 +684,33 @@ static int rockchip_spi_probe(struct platform_device *pdev) + rs->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(rs->regs)) { + ret = PTR_ERR(rs->regs); +- goto err_ioremap_resource; ++ goto err_put_master; + } + + rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk"); + if (IS_ERR(rs->apb_pclk)) { + dev_err(&pdev->dev, "Failed to get apb_pclk\n"); + ret = PTR_ERR(rs->apb_pclk); +- goto err_ioremap_resource; ++ goto err_put_master; + } + + rs->spiclk = devm_clk_get(&pdev->dev, "spiclk"); + if (IS_ERR(rs->spiclk)) { + dev_err(&pdev->dev, "Failed to get spi_pclk\n"); + ret = PTR_ERR(rs->spiclk); +- goto err_ioremap_resource; ++ goto err_put_master; + } + + ret = clk_prepare_enable(rs->apb_pclk); + if (ret) { + dev_err(&pdev->dev, "Failed to enable apb_pclk\n"); +- goto err_ioremap_resource; ++ goto err_put_master; + } + + ret = clk_prepare_enable(rs->spiclk); + if (ret) { + dev_err(&pdev->dev, "Failed to enable spi_clk\n"); +- goto err_spiclk_enable; ++ goto err_disable_apbclk; + } + + spi_enable_chip(rs, 0); +@@ -728,7 +728,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) + if (!rs->fifo_len) { + dev_err(&pdev->dev, "Failed to get fifo length\n"); + ret = -EINVAL; +- goto err_get_fifo_len; ++ goto err_disable_spiclk; + } + + spin_lock_init(&rs->lock); +@@ -755,7 +755,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) + /* Check tx to see if we need defer probing driver */ + if (PTR_ERR(rs->dma_tx.ch) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; +- goto err_get_fifo_len; ++ goto err_disable_pm_runtime; + } + dev_warn(rs->dev, "Failed to request TX DMA channel\n"); + rs->dma_tx.ch = NULL; +@@ -786,23 +786,24 @@ static int rockchip_spi_probe(struct platform_device *pdev) + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "Failed to register master\n"); +- goto err_register_master; ++ goto err_free_dma_rx; + } + + return 0; + +-err_register_master: +- pm_runtime_disable(&pdev->dev); ++err_free_dma_rx: + if (rs->dma_rx.ch) + dma_release_channel(rs->dma_rx.ch); + err_free_dma_tx: + if (rs->dma_tx.ch) + dma_release_channel(rs->dma_tx.ch); +-err_get_fifo_len: ++err_disable_pm_runtime: ++ pm_runtime_disable(&pdev->dev); ++err_disable_spiclk: + clk_disable_unprepare(rs->spiclk); +-err_spiclk_enable: ++err_disable_apbclk: + clk_disable_unprepare(rs->apb_pclk); +-err_ioremap_resource: ++err_put_master: + spi_master_put(master); + + return ret; +-- +2.11.0 + diff --git a/patches.drivers/0016-spi-rockchip-Set-GPIO_SS-flag-to-enable-Slave-Select.patch b/patches.drivers/0016-spi-rockchip-Set-GPIO_SS-flag-to-enable-Slave-Select.patch new file mode 100644 index 0000000..d4c5431 --- /dev/null +++ b/patches.drivers/0016-spi-rockchip-Set-GPIO_SS-flag-to-enable-Slave-Select.patch @@ -0,0 +1,35 @@ +From 0f2a43a55a74671100286ac908f71efe3b840507 Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Wed, 28 Jun 2017 12:38:42 +0800 +Subject: [PATCH 16/86] spi: rockchip: Set GPIO_SS flag to enable Slave Select + with GPIO CS + +Git-commit: c863795c4c0787e5f885099e3b673e1433448b82 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +The rockchip spi still requires slave selection when using GPIO CS. + +Signed-off-by: Jeffy Chen +Reviewed-by: Douglas Anderson +Signed-off-by: Mark Brown +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/spi/spi-rockchip.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c +index bab9b13f0ad0..52ea1605d4c6 100644 +--- a/drivers/spi/spi-rockchip.c ++++ b/drivers/spi/spi-rockchip.c +@@ -749,6 +749,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) + master->transfer_one = rockchip_spi_transfer_one; + master->max_transfer_size = rockchip_spi_max_transfer_size; + master->handle_err = rockchip_spi_handle_err; ++ master->flags = SPI_MASTER_GPIO_SS; + + rs->dma_tx.ch = dma_request_chan(rs->dev, "tx"); + if (IS_ERR(rs->dma_tx.ch)) { +-- +2.11.0 + diff --git a/patches.drivers/0017-spi-rockchip-Disable-Runtime-PM-when-chip-select-is-.patch b/patches.drivers/0017-spi-rockchip-Disable-Runtime-PM-when-chip-select-is-.patch new file mode 100644 index 0000000..c4fe93c --- /dev/null +++ b/patches.drivers/0017-spi-rockchip-Disable-Runtime-PM-when-chip-select-is-.patch @@ -0,0 +1,129 @@ +From 77eb994453b29c93be8e2bd31036b0844f5ce430 Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Wed, 28 Jun 2017 12:38:43 +0800 +Subject: [PATCH 17/86] spi: rockchip: Disable Runtime PM when chip select is + asserted + +Git-commit: aa099382ac0cda27e10fa8f45bf91edea0d1d35e +Patch-mainline: v4.13-rc1 +References: fate#323912 + +The rockchip spi would stop driving pins when runtime suspended, which +might break slave's xfer(for example cros_ec). + +Since we have pullups on those pins, we only need to care about this +when the CS asserted. + +So let's keep the spi alive when chip select is asserted. + +Also use pm_runtime_put instead of pm_runtime_put_sync. + +Suggested-by: Doug Anderson +Signed-off-by: Jeffy Chen +Reviewed-by: Douglas Anderson +Signed-off-by: Mark Brown +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/spi/spi-rockchip.c | 51 +++++++++++++++++++++++----------------------- + 1 file changed, 26 insertions(+), 25 deletions(-) + +diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c +index 52ea1605d4c6..0b4a52b3e1dc 100644 +--- a/drivers/spi/spi-rockchip.c ++++ b/drivers/spi/spi-rockchip.c +@@ -25,6 +25,11 @@ + + #define DRIVER_NAME "rockchip-spi" + ++#define ROCKCHIP_SPI_CLR_BITS(reg, bits) \ ++ writel_relaxed(readl_relaxed(reg) & ~(bits), reg) ++#define ROCKCHIP_SPI_SET_BITS(reg, bits) \ ++ writel_relaxed(readl_relaxed(reg) | (bits), reg) ++ + /* SPI register offsets */ + #define ROCKCHIP_SPI_CTRLR0 0x0000 + #define ROCKCHIP_SPI_CTRLR1 0x0004 +@@ -149,6 +154,8 @@ + */ + #define ROCKCHIP_SPI_MAX_TRANLEN 0xffff + ++#define ROCKCHIP_SPI_MAX_CS_NUM 2 ++ + enum rockchip_ssi_type { + SSI_MOTO_SPI = 0, + SSI_TI_SSP, +@@ -193,6 +200,8 @@ struct rockchip_spi { + /* protect state */ + spinlock_t lock; + ++ bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; ++ + u32 use_dma; + struct sg_table tx_sg; + struct sg_table rx_sg; +@@ -264,37 +273,29 @@ static inline u32 rx_max(struct rockchip_spi *rs) + + static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) + { +- u32 ser; + struct spi_master *master = spi->master; + struct rockchip_spi *rs = spi_master_get_devdata(master); ++ bool cs_asserted = !enable; + +- pm_runtime_get_sync(rs->dev); ++ /* Return immediately for no-op */ ++ if (cs_asserted == rs->cs_asserted[spi->chip_select]) ++ return; + +- ser = readl_relaxed(rs->regs + ROCKCHIP_SPI_SER) & SER_MASK; ++ if (cs_asserted) { ++ /* Keep things powered as long as CS is asserted */ ++ pm_runtime_get_sync(rs->dev); + +- /* +- * drivers/spi/spi.c: +- * static void spi_set_cs(struct spi_device *spi, bool enable) +- * { +- * if (spi->mode & SPI_CS_HIGH) +- * enable = !enable; +- * +- * if (spi->cs_gpio >= 0) +- * gpio_set_value(spi->cs_gpio, !enable); +- * else if (spi->master->set_cs) +- * spi->master->set_cs(spi, !enable); +- * } +- * +- * Note: enable(rockchip_spi_set_cs) = !enable(spi_set_cs) +- */ +- if (!enable) +- ser |= 1 << spi->chip_select; +- else +- ser &= ~(1 << spi->chip_select); ++ ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, ++ BIT(spi->chip_select)); ++ } else { ++ ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, ++ BIT(spi->chip_select)); + +- writel_relaxed(ser, rs->regs + ROCKCHIP_SPI_SER); ++ /* Drop reference from when we first asserted CS */ ++ pm_runtime_put(rs->dev); ++ } + +- pm_runtime_put_sync(rs->dev); ++ rs->cs_asserted[spi->chip_select] = cs_asserted; + } + + static int rockchip_spi_prepare_message(struct spi_master *master, +@@ -739,7 +740,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) + master->auto_runtime_pm = true; + master->bus_num = pdev->id; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; +- master->num_chipselect = 2; ++ master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; + master->dev.of_node = pdev->dev.of_node; + master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8); + +-- +2.11.0 + diff --git a/patches.drivers/0018-spi-rockchip-Slightly-rework-return-value-handling.patch b/patches.drivers/0018-spi-rockchip-Slightly-rework-return-value-handling.patch new file mode 100644 index 0000000..457dd6b --- /dev/null +++ b/patches.drivers/0018-spi-rockchip-Slightly-rework-return-value-handling.patch @@ -0,0 +1,116 @@ +From 2b325874b66c47bc34dd9c62a2d42b58515aaf96 Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Mon, 7 Aug 2017 20:40:18 +0800 +Subject: [PATCH 18/86] spi: rockchip: Slightly rework return value handling + +Git-commit: 43de979ddc099c57858b243619c66e2f663a2f97 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Slightly rework return value handling, no functional changes. + +Signed-off-by: Jeffy Chen +Signed-off-by: Mark Brown +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/spi/spi-rockchip.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c +index 0b4a52b3e1dc..5497650992c7 100644 +--- a/drivers/spi/spi-rockchip.c ++++ b/drivers/spi/spi-rockchip.c +@@ -666,7 +666,7 @@ static bool rockchip_spi_can_dma(struct spi_master *master, + + static int rockchip_spi_probe(struct platform_device *pdev) + { +- int ret = 0; ++ int ret; + struct rockchip_spi *rs; + struct spi_master *master; + struct resource *mem; +@@ -703,13 +703,13 @@ static int rockchip_spi_probe(struct platform_device *pdev) + } + + ret = clk_prepare_enable(rs->apb_pclk); +- if (ret) { ++ if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable apb_pclk\n"); + goto err_put_master; + } + + ret = clk_prepare_enable(rs->spiclk); +- if (ret) { ++ if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable spi_clk\n"); + goto err_disable_apbclk; + } +@@ -786,7 +786,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) + } + + ret = devm_spi_register_master(&pdev->dev, master); +- if (ret) { ++ if (ret < 0) { + dev_err(&pdev->dev, "Failed to register master\n"); + goto err_free_dma_rx; + } +@@ -834,12 +834,12 @@ static int rockchip_spi_remove(struct platform_device *pdev) + #ifdef CONFIG_PM_SLEEP + static int rockchip_spi_suspend(struct device *dev) + { +- int ret = 0; ++ int ret; + struct spi_master *master = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_master_get_devdata(master); + + ret = spi_master_suspend(rs->master); +- if (ret) ++ if (ret < 0) + return ret; + + if (!pm_runtime_suspended(dev)) { +@@ -849,12 +849,12 @@ static int rockchip_spi_suspend(struct device *dev) + + pinctrl_pm_select_sleep_state(dev); + +- return ret; ++ return 0; + } + + static int rockchip_spi_resume(struct device *dev) + { +- int ret = 0; ++ int ret; + struct spi_master *master = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_master_get_devdata(master); + +@@ -878,7 +878,7 @@ static int rockchip_spi_resume(struct device *dev) + clk_disable_unprepare(rs->apb_pclk); + } + +- return ret; ++ return 0; + } + #endif /* CONFIG_PM_SLEEP */ + +@@ -901,14 +901,14 @@ static int rockchip_spi_runtime_resume(struct device *dev) + struct rockchip_spi *rs = spi_master_get_devdata(master); + + ret = clk_prepare_enable(rs->apb_pclk); +- if (ret) ++ if (ret < 0) + return ret; + + ret = clk_prepare_enable(rs->spiclk); +- if (ret) ++ if (ret < 0) + clk_disable_unprepare(rs->apb_pclk); + +- return ret; ++ return 0; + } + #endif /* CONFIG_PM */ + +-- +2.11.0 + diff --git a/patches.drivers/0019-spi-rockchip-Fix-clock-handling-in-remove.patch b/patches.drivers/0019-spi-rockchip-Fix-clock-handling-in-remove.patch new file mode 100644 index 0000000..cffc0b1 --- /dev/null +++ b/patches.drivers/0019-spi-rockchip-Fix-clock-handling-in-remove.patch @@ -0,0 +1,46 @@ +From 221f384bbc66b53166592509daf1f44d34fb61dd Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Mon, 7 Aug 2017 20:40:19 +0800 +Subject: [PATCH 19/86] spi: rockchip: Fix clock handling in remove + +Git-commit: 6a06e895b262621a81b3b08126b4bc5e1b8eef05 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +We are assuming clocks enabled when calling rockchip_spi_remove, which +is not always true. Those clocks might already been disabled by the +runtime PM at that time. + +Call pm_runtime_get_sync before trying to disable clocks to avoid that. + +Signed-off-by: Jeffy Chen +Signed-off-by: Mark Brown +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/spi/spi-rockchip.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c +index 5497650992c7..a75fd9bb76de 100644 +--- a/drivers/spi/spi-rockchip.c ++++ b/drivers/spi/spi-rockchip.c +@@ -816,11 +816,15 @@ static int rockchip_spi_remove(struct platform_device *pdev) + struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); + struct rockchip_spi *rs = spi_master_get_devdata(master); + +- pm_runtime_disable(&pdev->dev); ++ pm_runtime_get_sync(&pdev->dev); + + clk_disable_unprepare(rs->spiclk); + clk_disable_unprepare(rs->apb_pclk); + ++ pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ pm_runtime_set_suspended(&pdev->dev); ++ + if (rs->dma_tx.ch) + dma_release_channel(rs->dma_tx.ch); + if (rs->dma_rx.ch) +-- +2.11.0 + diff --git a/patches.drivers/0020-spi-rockchip-Fix-clock-handling-in-suspend-resume.patch b/patches.drivers/0020-spi-rockchip-Fix-clock-handling-in-suspend-resume.patch new file mode 100644 index 0000000..448ace1 --- /dev/null +++ b/patches.drivers/0020-spi-rockchip-Fix-clock-handling-in-suspend-resume.patch @@ -0,0 +1,65 @@ +From dc72d4940fe373c97446790b4b083250c7ed0c7c Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Mon, 7 Aug 2017 20:40:20 +0800 +Subject: [PATCH 20/86] spi: rockchip: Fix clock handling in suspend/resume + +Git-commit: d38c4ae194bb8a3d8cf7d95378c5b2799cdd0a3b +Patch-mainline: v4.14-rc1 +References: fate#323912 + +The runtime suspend callback might be called by pm domain framework at +suspend_noirq stage. It would try to disable the clocks which already +been disabled by rockchip_spi_suspend. + +Call pm_runtime_force_suspend/pm_runtime_force_resume when +suspend/resume to avoid that. + +Signed-off-by: Jeffy Chen +Signed-off-by: Mark Brown +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/spi/spi-rockchip.c | 21 ++++++--------------- + 1 file changed, 6 insertions(+), 15 deletions(-) + +diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c +index a75fd9bb76de..34f6440a5255 100644 +--- a/drivers/spi/spi-rockchip.c ++++ b/drivers/spi/spi-rockchip.c +@@ -846,10 +846,9 @@ static int rockchip_spi_suspend(struct device *dev) + if (ret < 0) + return ret; + +- if (!pm_runtime_suspended(dev)) { +- clk_disable_unprepare(rs->spiclk); +- clk_disable_unprepare(rs->apb_pclk); +- } ++ ret = pm_runtime_force_suspend(dev); ++ if (ret < 0) ++ return ret; + + pinctrl_pm_select_sleep_state(dev); + +@@ -864,17 +863,9 @@ static int rockchip_spi_resume(struct device *dev) + + pinctrl_pm_select_default_state(dev); + +- if (!pm_runtime_suspended(dev)) { +- ret = clk_prepare_enable(rs->apb_pclk); +- if (ret < 0) +- return ret; +- +- ret = clk_prepare_enable(rs->spiclk); +- if (ret < 0) { +- clk_disable_unprepare(rs->apb_pclk); +- return ret; +- } +- } ++ ret = pm_runtime_force_resume(dev); ++ if (ret < 0) ++ return ret; + + ret = spi_master_resume(rs->master); + if (ret < 0) { +-- +2.11.0 + diff --git a/patches.drivers/0021-spi-rockchip-configure-CTRLR1-according-to-size-and-.patch b/patches.drivers/0021-spi-rockchip-configure-CTRLR1-according-to-size-and-.patch new file mode 100644 index 0000000..1532718 --- /dev/null +++ b/patches.drivers/0021-spi-rockchip-configure-CTRLR1-according-to-size-and-.patch @@ -0,0 +1,43 @@ +From 3e4c194c78a397ccb227951cb67b5077c4d8ca34 Mon Sep 17 00:00:00 2001 +From: Huibin Hong +Date: Wed, 16 Aug 2017 10:12:02 +0800 +Subject: [PATCH 21/86] spi: rockchip: configure CTRLR1 according to size and + data frame + +Git-commit: 04b37d2d02c0a5ae2f4e59326ef6deaff18e0456 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +CTRLR1 is number of data frames, when rx only. +When data frame is 8 bit, CTRLR1 is len-1. +When data frame is 16 bit, CTRLR1 is (len/2)-1. + +Signed-off-by: Huibin Hong +Signed-off-by: Mark Brown +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/spi/spi-rockchip.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c +index 34f6440a5255..7df207f176fc 100644 +--- a/drivers/spi/spi-rockchip.c ++++ b/drivers/spi/spi-rockchip.c +@@ -568,7 +568,13 @@ static void rockchip_spi_config(struct rockchip_spi *rs) + + writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0); + +- writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); ++ if (rs->n_bytes == 1) ++ writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); ++ else if (rs->n_bytes == 2) ++ writel_relaxed((rs->len / 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); ++ else ++ writel_relaxed((rs->len * 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); ++ + writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_TXFTLR); + writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR); + +-- +2.11.0 + diff --git a/patches.drivers/0022-clk-rockchip-add-ids-for-camera-on-rk3399.patch b/patches.drivers/0022-clk-rockchip-add-ids-for-camera-on-rk3399.patch new file mode 100644 index 0000000..5f54cc8 --- /dev/null +++ b/patches.drivers/0022-clk-rockchip-add-ids-for-camera-on-rk3399.patch @@ -0,0 +1,34 @@ +From 3eec7529aa71fde5ca51359e57464e88a3a901ef Mon Sep 17 00:00:00 2001 +From: Eddie Cai +Date: Tue, 25 Apr 2017 14:41:09 +0800 +Subject: [PATCH 22/86] clk: rockchip: add ids for camera on rk3399 + +Git-commit: f22e4359cd73fc335df4b13e85dbcda8bf1f25f0 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +we use SCLK_TESTCLKOUT1 and SCLK_TESTCLKOUT2 for camera, so add those ids. + +Signed-off-by: Eddie Cai +Signed-off-by: Heiko Stuebner +Signed-off-by: Mian Yousaf Kaukab +--- + include/dt-bindings/clock/rk3399-cru.h | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h +index 220a60f20d3b..22cb1dfa9004 100644 +--- a/include/dt-bindings/clock/rk3399-cru.h ++++ b/include/dt-bindings/clock/rk3399-cru.h +@@ -132,6 +132,8 @@ + #define SCLK_RMII_SRC 166 + #define SCLK_PCIEPHY_REF100M 167 + #define SCLK_DDRC 168 ++#define SCLK_TESTCLKOUT1 169 ++#define SCLK_TESTCLKOUT2 170 + + #define DCLK_VOP0 180 + #define DCLK_VOP1 181 +-- +2.11.0 + diff --git a/patches.drivers/0023-clk-rockchip-add-dt-binding-header-for-rk3128.patch b/patches.drivers/0023-clk-rockchip-add-dt-binding-header-for-rk3128.patch new file mode 100644 index 0000000..1d5dac9 --- /dev/null +++ b/patches.drivers/0023-clk-rockchip-add-dt-binding-header-for-rk3128.patch @@ -0,0 +1,315 @@ +From 9ae2aed3761adb3104d733cc6d2767c176794065 Mon Sep 17 00:00:00 2001 +From: Elaine Zhang +Date: Fri, 2 Jun 2017 09:47:23 +0800 +Subject: [PATCH 23/86] clk: rockchip: add dt-binding header for rk3128 + +Git-commit: b20841b9e0d730206de6ee95f4d00e3f8815ad50 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +Add the dt-bindings header for the rk3128, +that gets shared between the clock controller and +the clock references in the dts. +Add softreset ID for rk3128. +And it also applies to the RK3126 SoC. + +Signed-off-by: Elaine Zhang +Acked-by: Rob Herring +Signed-off-by: Heiko Stuebner +Signed-off-by: Mian Yousaf Kaukab +--- + include/dt-bindings/clock/rk3128-cru.h | 282 +++++++++++++++++++++++++++++++++ + 1 file changed, 282 insertions(+) + create mode 100644 include/dt-bindings/clock/rk3128-cru.h + +diff --git a/include/dt-bindings/clock/rk3128-cru.h b/include/dt-bindings/clock/rk3128-cru.h +new file mode 100644 +index 000000000000..92894f4306cf +--- /dev/null ++++ b/include/dt-bindings/clock/rk3128-cru.h +@@ -0,0 +1,282 @@ ++/* ++ * Copyright (c) 2017 Rockchip Electronics Co. Ltd. ++ * Author: Elaine ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _DT_BINDINGS_CLK_ROCKCHIP_RK3128_H ++#define _DT_BINDINGS_CLK_ROCKCHIP_RK3128_H ++ ++/* core clocks */ ++#define PLL_APLL 1 ++#define PLL_DPLL 2 ++#define PLL_CPLL 3 ++#define PLL_GPLL 4 ++#define ARMCLK 5 ++#define PLL_GPLL_DIV2 6 ++#define PLL_GPLL_DIV3 7 ++ ++/* sclk gates (special clocks) */ ++#define SCLK_SPI0 65 ++#define SCLK_NANDC 67 ++#define SCLK_SDMMC 68 ++#define SCLK_SDIO 69 ++#define SCLK_EMMC 71 ++#define SCLK_UART0 77 ++#define SCLK_UART1 78 ++#define SCLK_UART2 79 ++#define SCLK_I2S0 80 ++#define SCLK_I2S1 81 ++#define SCLK_SPDIF 83 ++#define SCLK_TIMER0 85 ++#define SCLK_TIMER1 86 ++#define SCLK_TIMER2 87 ++#define SCLK_TIMER3 88 ++#define SCLK_TIMER4 89 ++#define SCLK_TIMER5 90 ++#define SCLK_SARADC 91 ++#define SCLK_I2S_OUT 113 ++#define SCLK_SDMMC_DRV 114 ++#define SCLK_SDIO_DRV 115 ++#define SCLK_EMMC_DRV 117 ++#define SCLK_SDMMC_SAMPLE 118 ++#define SCLK_SDIO_SAMPLE 119 ++#define SCLK_EMMC_SAMPLE 121 ++#define SCLK_VOP 122 ++#define SCLK_MAC_SRC 124 ++#define SCLK_MAC 126 ++#define SCLK_MAC_REFOUT 127 ++#define SCLK_MAC_REF 128 ++#define SCLK_MAC_RX 129 ++#define SCLK_MAC_TX 130 ++#define SCLK_HEVC_CORE 134 ++#define SCLK_RGA 135 ++#define SCLK_CRYPTO 138 ++#define SCLK_TSP 139 ++#define SCLK_OTGPHY0 142 ++#define SCLK_OTGPHY1 143 ++#define SCLK_DDRC 144 ++#define SCLK_PVTM_FUNC 145 ++#define SCLK_PVTM_CORE 146 ++#define SCLK_PVTM_GPU 147 ++#define SCLK_MIPI_24M 148 ++#define SCLK_PVTM 149 ++#define SCLK_CIF_SRC 150 ++#define SCLK_CIF_OUT_SRC 151 ++#define SCLK_CIF_OUT 152 ++#define SCLK_SFC 153 ++#define SCLK_USB480M 154 ++ ++/* dclk gates */ ++#define DCLK_VOP 190 ++#define DCLK_EBC 191 ++ ++/* aclk gates */ ++#define ACLK_VIO0 192 ++#define ACLK_VIO1 193 ++#define ACLK_DMAC 194 ++#define ACLK_CPU 195 ++#define ACLK_VEPU 196 ++#define ACLK_VDPU 197 ++#define ACLK_CIF 198 ++#define ACLK_IEP 199 ++#define ACLK_LCDC0 204 ++#define ACLK_RGA 205 ++#define ACLK_PERI 210 ++#define ACLK_VOP 211 ++#define ACLK_GMAC 212 ++#define ACLK_GPU 213 ++ ++/* pclk gates */ ++#define PCLK_SARADC 318 ++#define PCLK_WDT 319 ++#define PCLK_GPIO0 320 ++#define PCLK_GPIO1 321 ++#define PCLK_GPIO2 322 ++#define PCLK_GPIO3 323 ++#define PCLK_VIO_H2P 324 ++#define PCLK_MIPI 325 ++#define PCLK_EFUSE 326 ++#define PCLK_HDMI 327 ++#define PCLK_ACODEC 328 ++#define PCLK_GRF 329 ++#define PCLK_I2C0 332 ++#define PCLK_I2C1 333 ++#define PCLK_I2C2 334 ++#define PCLK_I2C3 335 ++#define PCLK_SPI0 338 ++#define PCLK_UART0 341 ++#define PCLK_UART1 342 ++#define PCLK_UART2 343 ++#define PCLK_TSADC 344 ++#define PCLK_PWM 350 ++#define PCLK_TIMER 353 ++#define PCLK_CPU 354 ++#define PCLK_PERI 363 ++#define PCLK_GMAC 367 ++#define PCLK_PMU_PRE 368 ++#define PCLK_SIM_CARD 369 ++ ++/* hclk gates */ ++#define HCLK_SPDIF 440 ++#define HCLK_GPS 441 ++#define HCLK_USBHOST 442 ++#define HCLK_I2S_8CH 443 ++#define HCLK_I2S_2CH 444 ++#define HCLK_VOP 452 ++#define HCLK_NANDC 453 ++#define HCLK_SDMMC 456 ++#define HCLK_SDIO 457 ++#define HCLK_EMMC 459 ++#define HCLK_CPU 460 ++#define HCLK_VEPU 461 ++#define HCLK_VDPU 462 ++#define HCLK_LCDC0 463 ++#define HCLK_EBC 465 ++#define HCLK_VIO 466 ++#define HCLK_RGA 467 ++#define HCLK_IEP 468 ++#define HCLK_VIO_H2P 469 ++#define HCLK_CIF 470 ++#define HCLK_HOST2 473 ++#define HCLK_OTG 474 ++#define HCLK_TSP 475 ++#define HCLK_CRYPTO 476 ++#define HCLK_PERI 478 ++ ++#define CLK_NR_CLKS (HCLK_PERI + 1) ++ ++/* soft-reset indices */ ++#define SRST_CORE0_PO 0 ++#define SRST_CORE1_PO 1 ++#define SRST_CORE2_PO 2 ++#define SRST_CORE3_PO 3 ++#define SRST_CORE0 4 ++#define SRST_CORE1 5 ++#define SRST_CORE2 6 ++#define SRST_CORE3 7 ++#define SRST_CORE0_DBG 8 ++#define SRST_CORE1_DBG 9 ++#define SRST_CORE2_DBG 10 ++#define SRST_CORE3_DBG 11 ++#define SRST_TOPDBG 12 ++#define SRST_ACLK_CORE 13 ++#define SRST_STRC_SYS_A 14 ++#define SRST_L2C 15 ++ ++#define SRST_CPUSYS_H 18 ++#define SRST_AHB2APBSYS_H 19 ++#define SRST_SPDIF 20 ++#define SRST_INTMEM 21 ++#define SRST_ROM 22 ++#define SRST_PERI_NIU 23 ++#define SRST_I2S_2CH 24 ++#define SRST_I2S_8CH 25 ++#define SRST_GPU_PVTM 26 ++#define SRST_FUNC_PVTM 27 ++#define SRST_CORE_PVTM 29 ++#define SRST_EFUSE_P 30 ++#define SRST_ACODEC_P 31 ++ ++#define SRST_GPIO0 32 ++#define SRST_GPIO1 33 ++#define SRST_GPIO2 34 ++#define SRST_GPIO3 35 ++#define SRST_MIPIPHY_P 36 ++#define SRST_UART0 39 ++#define SRST_UART1 40 ++#define SRST_UART2 41 ++#define SRST_I2C0 43 ++#define SRST_I2C1 44 ++#define SRST_I2C2 45 ++#define SRST_I2C3 46 ++#define SRST_SFC 47 ++ ++#define SRST_PWM 48 ++#define SRST_DAP_PO 50 ++#define SRST_DAP 51 ++#define SRST_DAP_SYS 52 ++#define SRST_CRYPTO 53 ++#define SRST_GRF 55 ++#define SRST_GMAC 56 ++#define SRST_PERIPH_SYS_A 57 ++#define SRST_PERIPH_SYS_H 58 ++#define SRST_PERIPH_SYS_P 59 ++#define SRST_SMART_CARD 60 ++#define SRST_CPU_PERI 61 ++#define SRST_EMEM_PERI 62 ++#define SRST_USB_PERI 63 ++ ++#define SRST_DMA 64 ++#define SRST_GPS 67 ++#define SRST_NANDC 68 ++#define SRST_USBOTG0 69 ++#define SRST_OTGC0 71 ++#define SRST_USBOTG1 72 ++#define SRST_OTGC1 74 ++#define SRST_DDRMSCH 79 ++ ++#define SRST_SDMMC 81 ++#define SRST_SDIO 82 ++#define SRST_EMMC 83 ++#define SRST_SPI 84 ++#define SRST_WDT 86 ++#define SRST_SARADC 87 ++#define SRST_DDRPHY 88 ++#define SRST_DDRPHY_P 89 ++#define SRST_DDRCTRL 90 ++#define SRST_DDRCTRL_P 91 ++#define SRST_TSP 92 ++#define SRST_TSP_CLKIN 93 ++#define SRST_HOST0_ECHI 94 ++ ++#define SRST_HDMI_P 96 ++#define SRST_VIO_ARBI_H 97 ++#define SRST_VIO0_A 98 ++#define SRST_VIO_BUS_H 99 ++#define SRST_VOP_A 100 ++#define SRST_VOP_H 101 ++#define SRST_VOP_D 102 ++#define SRST_UTMI0 103 ++#define SRST_UTMI1 104 ++#define SRST_USBPOR 105 ++#define SRST_IEP_A 106 ++#define SRST_IEP_H 107 ++#define SRST_RGA_A 108 ++#define SRST_RGA_H 109 ++#define SRST_CIF0 110 ++#define SRST_PMU 111 ++ ++#define SRST_VCODEC_A 112 ++#define SRST_VCODEC_H 113 ++#define SRST_VIO1_A 114 ++#define SRST_HEVC_CORE 115 ++#define SRST_VCODEC_NIU_A 116 ++#define SRST_PMU_NIU_P 117 ++#define SRST_LCDC0_S 119 ++#define SRST_GPU 120 ++#define SRST_GPU_NIU_A 122 ++#define SRST_EBC_A 123 ++#define SRST_EBC_H 124 ++ ++#define SRST_CORE_DBG 128 ++#define SRST_DBG_P 129 ++#define SRST_TIMER0 130 ++#define SRST_TIMER1 131 ++#define SRST_TIMER2 132 ++#define SRST_TIMER3 133 ++#define SRST_TIMER4 134 ++#define SRST_TIMER5 135 ++#define SRST_VIO_H2P 136 ++#define SRST_VIO_MIPI_DSI 137 ++ ++#endif +-- +2.11.0 + diff --git a/patches.drivers/0024-clk-rockchip-add-ids-for-rk3399-testclks-used-for-ca.patch b/patches.drivers/0024-clk-rockchip-add-ids-for-rk3399-testclks-used-for-ca.patch new file mode 100644 index 0000000..3197b78 --- /dev/null +++ b/patches.drivers/0024-clk-rockchip-add-ids-for-rk3399-testclks-used-for-ca.patch @@ -0,0 +1,42 @@ +From fbec6cbce3c0d825b4f78ae995a257586d2e21f3 Mon Sep 17 00:00:00 2001 +From: Eddie Cai +Date: Tue, 25 Apr 2017 14:41:10 +0800 +Subject: [PATCH 24/86] clk: rockchip: add ids for rk3399 testclks used for + camera handling + +Git-commit: 25fb42b1cfde85f905dd1d61ea1e089c16e1ad77 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +clk_testout1 and clk_testout2 are used for camera handling, so add their ids. + +Signed-off-by: Eddie Cai +Signed-off-by: Heiko Stuebner +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/clk/rockchip/clk-rk3399.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c +index fa3cbef08776..6847120b61cd 100644 +--- a/drivers/clk/rockchip/clk-rk3399.c ++++ b/drivers/clk/rockchip/clk-rk3399.c +@@ -1066,13 +1066,13 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { + /* cif_testout */ + MUX(0, "clk_testout1_pll_src", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(38), 6, 2, MFLAGS), +- COMPOSITE(0, "clk_testout1", mux_clk_testout1_p, 0, ++ COMPOSITE(SCLK_TESTCLKOUT1, "clk_testout1", mux_clk_testout1_p, 0, + RK3399_CLKSEL_CON(38), 5, 1, MFLAGS, 0, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 14, GFLAGS), + + MUX(0, "clk_testout2_pll_src", mux_pll_src_cpll_gpll_npll_p, 0, + RK3399_CLKSEL_CON(38), 14, 2, MFLAGS), +- COMPOSITE(0, "clk_testout2", mux_clk_testout2_p, 0, ++ COMPOSITE(SCLK_TESTCLKOUT2, "clk_testout2", mux_clk_testout2_p, 0, + RK3399_CLKSEL_CON(38), 13, 1, MFLAGS, 8, 5, DFLAGS, + RK3399_CLKGATE_CON(13), 15, GFLAGS), + +-- +2.11.0 + diff --git a/patches.drivers/0025-clk-fractional-divider-allow-overriding-of-approxima.patch b/patches.drivers/0025-clk-fractional-divider-allow-overriding-of-approxima.patch new file mode 100644 index 0000000..c9695f5 --- /dev/null +++ b/patches.drivers/0025-clk-fractional-divider-allow-overriding-of-approxima.patch @@ -0,0 +1,94 @@ +From 449c3a968dbfd0fe1a65d5a725c9cac4c9dc7fda Mon Sep 17 00:00:00 2001 +From: Elaine Zhang +Date: Tue, 1 Aug 2017 18:21:22 +0200 +Subject: [PATCH 25/86] clk: fractional-divider: allow overriding of + approximation + +Git-commit: ec52e462564b9c5bfdf1f79638c537c7103e1d2b +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Fractional dividers may have special requirements concerning numerator +and denominator selection that differ from just getting the best +approximation. + +For example on Rockchip socs the denominator must be at least 20 times +larger than the numerator to generate precise clock frequencies. + +Therefore add the ability to provide custom approximation functions. + +Signed-off-by: Elaine Zhang +Acked-by: Stephen Boyd +Signed-off-by: Heiko Stuebner +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/clk/clk-fractional-divider.c | 28 ++++++++++++++++++++-------- + include/linux/clk-provider.h | 3 +++ + 2 files changed, 23 insertions(+), 8 deletions(-) + +diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c +index aab904618eb6..fdf625fb10fa 100644 +--- a/drivers/clk/clk-fractional-divider.c ++++ b/drivers/clk/clk-fractional-divider.c +@@ -49,16 +49,12 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, + return ret; + } + +-static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, +- unsigned long *parent_rate) ++static void clk_fd_general_approximation(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate, ++ unsigned long *m, unsigned long *n) + { + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long scale; +- unsigned long m, n; +- u64 ret; +- +- if (!rate || rate >= *parent_rate) +- return *parent_rate; + + /* + * Get rate closer to *parent_rate to guarantee there is no overflow +@@ -71,7 +67,23 @@ static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, + + rational_best_approximation(rate, *parent_rate, + GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), +- &m, &n); ++ m, n); ++} ++ ++static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ struct clk_fractional_divider *fd = to_clk_fd(hw); ++ unsigned long m, n; ++ u64 ret; ++ ++ if (!rate || rate >= *parent_rate) ++ return *parent_rate; ++ ++ if (fd->approximation) ++ fd->approximation(hw, rate, parent_rate, &m, &n); ++ else ++ clk_fd_general_approximation(hw, rate, parent_rate, &m, &n); + + ret = (u64)*parent_rate * m; + do_div(ret, n); +diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h +index a428aec36ace..1bbd5a4a4616 100644 +--- a/include/linux/clk-provider.h ++++ b/include/linux/clk-provider.h +@@ -564,6 +564,9 @@ struct clk_fractional_divider { + u8 nwidth; + u32 nmask; + u8 flags; ++ void (*approximation)(struct clk_hw *hw, ++ unsigned long rate, unsigned long *parent_rate, ++ unsigned long *m, unsigned long *n); + spinlock_t *lock; + }; + +-- +2.11.0 + diff --git a/patches.drivers/0026-clk-rockchip-add-special-approximation-to-fix-up-fra.patch b/patches.drivers/0026-clk-rockchip-add-special-approximation-to-fix-up-fra.patch new file mode 100644 index 0000000..5b0fca3 --- /dev/null +++ b/patches.drivers/0026-clk-rockchip-add-special-approximation-to-fix-up-fra.patch @@ -0,0 +1,92 @@ +From f9d1e46e334518eba69491134ae6b4f7c114327f Mon Sep 17 00:00:00 2001 +From: Elaine Zhang +Date: Tue, 1 Aug 2017 18:22:24 +0200 +Subject: [PATCH 26/86] clk: rockchip: add special approximation to fix up + fractional clk's jitter + +Git-commit: 5d890c2df900db0197d46ec75383d7633ef41c82 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +>From Rockchips fractional divider description: + 3.1.9 Fractional divider usage + To get specific frequency, clocks of I2S, SPDIF, UARTcan be generated by + fractional divider. Generally you must set that denominator is 20 times + larger than numerator to generate precise clock frequency. So the + fractional divider applies only to generate low frequency clock like + I2S, UART. + +Therefore add a special approximation function that handles this +special requirement. + +Signed-off-by: Elaine Zhang +Signed-off-by: Heiko Stuebner +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/clk/rockchip/clk.c | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c +index fe1d393cf678..b6db79a00602 100644 +--- a/drivers/clk/rockchip/clk.c ++++ b/drivers/clk/rockchip/clk.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include "clk.h" + + /** +@@ -164,6 +165,40 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, + return notifier_from_errno(ret); + } + ++/** ++ * fractional divider must set that denominator is 20 times larger than ++ * numerator to generate precise clock frequency. ++ */ ++void rockchip_fractional_approximation(struct clk_hw *hw, ++ unsigned long rate, unsigned long *parent_rate, ++ unsigned long *m, unsigned long *n) ++{ ++ struct clk_fractional_divider *fd = to_clk_fd(hw); ++ unsigned long p_rate, p_parent_rate; ++ struct clk_hw *p_parent; ++ unsigned long scale; ++ ++ p_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); ++ if ((rate * 20 > p_rate) && (p_rate % rate != 0)) { ++ p_parent = clk_hw_get_parent(clk_hw_get_parent(hw)); ++ p_parent_rate = clk_hw_get_rate(p_parent); ++ *parent_rate = p_parent_rate; ++ } ++ ++ /* ++ * Get rate closer to *parent_rate to guarantee there is no overflow ++ * for m and n. In the result it will be the nearest rate left shifted ++ * by (scale - fd->nwidth) bits. ++ */ ++ scale = fls_long(*parent_rate / rate - 1); ++ if (scale > fd->nwidth) ++ rate <<= scale - fd->nwidth; ++ ++ rational_best_approximation(rate, *parent_rate, ++ GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), ++ m, n); ++} ++ + static struct clk *rockchip_clk_register_frac_branch( + struct rockchip_clk_provider *ctx, const char *name, + const char *const *parent_names, u8 num_parents, +@@ -210,6 +245,7 @@ static struct clk *rockchip_clk_register_frac_branch( + div->nwidth = 16; + div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; + div->lock = lock; ++ div->approximation = rockchip_fractional_approximation; + div_ops = &clk_fractional_divider_ops; + + clk = clk_register_composite(NULL, name, parent_names, num_parents, +-- +2.11.0 + diff --git a/patches.drivers/0027-clk-rockchip-Mark-rockchip_fractional_approximation-.patch b/patches.drivers/0027-clk-rockchip-Mark-rockchip_fractional_approximation-.patch new file mode 100644 index 0000000..4ab3e2a --- /dev/null +++ b/patches.drivers/0027-clk-rockchip-Mark-rockchip_fractional_approximation-.patch @@ -0,0 +1,38 @@ +From f3e13b9cfa18f1c2a851578bda3d81c8b15b2d46 Mon Sep 17 00:00:00 2001 +From: Stephen Boyd +Date: Wed, 23 Aug 2017 15:35:41 -0700 +Subject: [PATCH 27/86] clk: rockchip: Mark rockchip_fractional_approximation + static + +Git-commit: 1dfcfa721f9390ed5fd1e9c48e9fd6e8208a4963 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Silence the sparse warning + +clk/rockchip/clk.c:172:6: warning: symbol 'rockchip_fractional_approximation' was not declared. Should it be static? + +Cc: Elaine Zhang +Cc: Heiko Stuebner +Signed-off-by: Stephen Boyd +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/clk/rockchip/clk.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c +index b6db79a00602..35dbd63c2f49 100644 +--- a/drivers/clk/rockchip/clk.c ++++ b/drivers/clk/rockchip/clk.c +@@ -169,7 +169,7 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, + * fractional divider must set that denominator is 20 times larger than + * numerator to generate precise clock frequency. + */ +-void rockchip_fractional_approximation(struct clk_hw *hw, ++static void rockchip_fractional_approximation(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate, + unsigned long *m, unsigned long *n) + { +-- +2.11.0 + diff --git a/patches.drivers/0028-clk-rockchip-Remove-superfluous-error-message-in-roc.patch b/patches.drivers/0028-clk-rockchip-Remove-superfluous-error-message-in-roc.patch new file mode 100644 index 0000000..26fc96e --- /dev/null +++ b/patches.drivers/0028-clk-rockchip-Remove-superfluous-error-message-in-roc.patch @@ -0,0 +1,38 @@ +From 3d08cc94146be2319fb8895139aa4d7b34230dc6 Mon Sep 17 00:00:00 2001 +From: Markus Elfring +Date: Wed, 27 Sep 2017 11:38:17 +0200 +Subject: [PATCH 28/86] clk: rockchip: Remove superfluous error message in + rockchip_clk_register_cpuclk() + +Git-commit: 9edb39d750c0a9a6f7dafb8d9d15e63f6d44b68c +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +Omit an extra message for a memory allocation failure in this function. + +This issue was detected by using the Coccinelle software. + +Signed-off-by: Markus Elfring +Signed-off-by: Heiko Stuebner +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/clk/rockchip/clk-cpu.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c +index 0e09684d43a5..32c19c0f1e14 100644 +--- a/drivers/clk/rockchip/clk-cpu.c ++++ b/drivers/clk/rockchip/clk-cpu.c +@@ -322,8 +322,6 @@ struct clk *rockchip_clk_register_cpuclk(const char *name, + sizeof(*rates) * nrates, + GFP_KERNEL); + if (!cpuclk->rate_table) { +- pr_err("%s: could not allocate memory for cpuclk rates\n", +- __func__); + ret = -ENOMEM; + goto unregister_notifier; + } +-- +2.11.0 + diff --git a/patches.drivers/0029-phy-qcom-usb-Remove-unused-ulpi-phy-header.patch b/patches.drivers/0029-phy-qcom-usb-Remove-unused-ulpi-phy-header.patch new file mode 100644 index 0000000..ea2c5e9 --- /dev/null +++ b/patches.drivers/0029-phy-qcom-usb-Remove-unused-ulpi-phy-header.patch @@ -0,0 +1,66 @@ +From 5d77c4b0af9689c7b4850d95d276f7f985c01614 Mon Sep 17 00:00:00 2001 +From: Vivek Gautam +Date: Thu, 11 May 2017 12:17:40 +0530 +Subject: [PATCH 29/86] phy: qcom-usb: Remove unused ulpi phy header + +Git-commit: 706a3b69955816bd0f3591be738db64cba74b674 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +Ulpi phy header is not used for anything. Remove the same +from qcom-hs and qcom-hsic phy drivers. + +Signed-off-by: Vivek Gautam +Suggested-by: Stephen Boyd +Cc: Kishon Vijay Abraham I +Cc: linux-arm-kernel@lists.infradead.org +Cc: linux-arm-msm@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Cc: linux-usb@vger.kernel.org +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/phy-qcom-usb-hs.c | 3 +-- + drivers/phy/phy-qcom-usb-hsic.c | 3 +-- + 2 files changed, 2 insertions(+), 4 deletions(-) + +diff --git a/drivers/phy/phy-qcom-usb-hs.c b/drivers/phy/phy-qcom-usb-hs.c +index 94dfbfd739c3..4b20abc3ae2f 100644 +--- a/drivers/phy/phy-qcom-usb-hs.c ++++ b/drivers/phy/phy-qcom-usb-hs.c +@@ -11,12 +11,11 @@ + #include + #include + #include ++#include + #include + #include + #include + +-#include "ulpi_phy.h" +- + #define ULPI_PWR_CLK_MNG_REG 0x88 + # define ULPI_PWR_OTG_COMP_DISABLE BIT(0) + +diff --git a/drivers/phy/phy-qcom-usb-hsic.c b/drivers/phy/phy-qcom-usb-hsic.c +index 47690f9945b9..c110563a73cb 100644 +--- a/drivers/phy/phy-qcom-usb-hsic.c ++++ b/drivers/phy/phy-qcom-usb-hsic.c +@@ -8,13 +8,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + +-#include "ulpi_phy.h" +- + #define ULPI_HSIC_CFG 0x30 + #define ULPI_HSIC_IO_CAL 0x33 + +-- +2.11.0 + diff --git a/patches.drivers/0030-phy-Move-ULPI-phy-header-out-of-drivers-to-include-p.patch b/patches.drivers/0030-phy-Move-ULPI-phy-header-out-of-drivers-to-include-p.patch new file mode 100644 index 0000000..3bde85c --- /dev/null +++ b/patches.drivers/0030-phy-Move-ULPI-phy-header-out-of-drivers-to-include-p.patch @@ -0,0 +1,124 @@ +From d2ccd317892f1c5eb1edacdaa751336037c57740 Mon Sep 17 00:00:00 2001 +From: Vivek Gautam +Date: Thu, 11 May 2017 12:17:41 +0530 +Subject: [PATCH 30/86] phy: Move ULPI phy header out of drivers to include + path + +Git-commit: 858edde001e14f070d0fff347fb56c6c79e15312 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +Although ULPI phy is currently being used by tusb1210, +there can be other consumers too in future. So move this +to the includes path for phy. + +Signed-off-by: Vivek Gautam +Cc: Stephen Boyd +Cc: Heikki Krogerus +Cc: Kishon Vijay Abraham I +Cc: linux-arm-kernel@lists.infradead.org +Cc: linux-kernel@vger.kernel.org +Cc: linux-omap@vger.kernel.org +Cc: linux-usb@vger.kernel.org +Acked-by: Heikki Krogerus +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/phy-tusb1210.c | 3 +-- + drivers/phy/ulpi_phy.h | 31 ------------------------------- + include/linux/phy/ulpi_phy.h | 31 +++++++++++++++++++++++++++++++ + 3 files changed, 32 insertions(+), 33 deletions(-) + delete mode 100644 drivers/phy/ulpi_phy.h + create mode 100644 include/linux/phy/ulpi_phy.h + +diff --git a/drivers/phy/phy-tusb1210.c b/drivers/phy/phy-tusb1210.c +index 4f6d5e71507d..bb3fb031c478 100644 +--- a/drivers/phy/phy-tusb1210.c ++++ b/drivers/phy/phy-tusb1210.c +@@ -12,8 +12,7 @@ + #include + #include + #include +- +-#include "ulpi_phy.h" ++#include + + #define TUSB1210_VENDOR_SPECIFIC2 0x80 + #define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 +diff --git a/drivers/phy/ulpi_phy.h b/drivers/phy/ulpi_phy.h +deleted file mode 100644 +index f2ebe490a4bc..000000000000 +--- a/drivers/phy/ulpi_phy.h ++++ /dev/null +@@ -1,31 +0,0 @@ +-#include +- +-/** +- * Helper that registers PHY for a ULPI device and adds a lookup for binding it +- * and it's controller, which is always the parent. +- */ +-static inline struct phy +-*ulpi_phy_create(struct ulpi *ulpi, const struct phy_ops *ops) +-{ +- struct phy *phy; +- int ret; +- +- phy = phy_create(&ulpi->dev, NULL, ops); +- if (IS_ERR(phy)) +- return phy; +- +- ret = phy_create_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); +- if (ret) { +- phy_destroy(phy); +- return ERR_PTR(ret); +- } +- +- return phy; +-} +- +-/* Remove a PHY that was created with ulpi_phy_create() and it's lookup. */ +-static inline void ulpi_phy_destroy(struct ulpi *ulpi, struct phy *phy) +-{ +- phy_remove_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); +- phy_destroy(phy); +-} +diff --git a/include/linux/phy/ulpi_phy.h b/include/linux/phy/ulpi_phy.h +new file mode 100644 +index 000000000000..f2ebe490a4bc +--- /dev/null ++++ b/include/linux/phy/ulpi_phy.h +@@ -0,0 +1,31 @@ ++#include ++ ++/** ++ * Helper that registers PHY for a ULPI device and adds a lookup for binding it ++ * and it's controller, which is always the parent. ++ */ ++static inline struct phy ++*ulpi_phy_create(struct ulpi *ulpi, const struct phy_ops *ops) ++{ ++ struct phy *phy; ++ int ret; ++ ++ phy = phy_create(&ulpi->dev, NULL, ops); ++ if (IS_ERR(phy)) ++ return phy; ++ ++ ret = phy_create_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); ++ if (ret) { ++ phy_destroy(phy); ++ return ERR_PTR(ret); ++ } ++ ++ return phy; ++} ++ ++/* Remove a PHY that was created with ulpi_phy_create() and it's lookup. */ ++static inline void ulpi_phy_destroy(struct ulpi *ulpi, struct phy *phy) ++{ ++ phy_remove_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); ++ phy_destroy(phy); ++} +-- +2.11.0 + diff --git a/patches.drivers/0031-phy-Group-vendor-specific-phy-drivers.patch b/patches.drivers/0031-phy-Group-vendor-specific-phy-drivers.patch new file mode 100644 index 0000000..9631838 --- /dev/null +++ b/patches.drivers/0031-phy-Group-vendor-specific-phy-drivers.patch @@ -0,0 +1,45969 @@ +From aa8e9cfd9ac2755fbdf5e667b132c57f124e3738 Mon Sep 17 00:00:00 2001 +From: Vivek Gautam +Date: Thu, 11 May 2017 12:17:42 +0530 +Subject: [PATCH 31/86] phy: Group vendor specific phy drivers + +Git-commit: 0b56e9a7e8358e59b21d8a425e463072bfae523c +Patch-mainline: v4.13-rc1 +References: fate#323912 + +Adding vendor specific directories in phy to group +phy drivers under their respective vendor umbrella. + +Also updated the MAINTAINERS file to reflect the correct +directory structure for phy drivers. + +Signed-off-by: Vivek Gautam +Acked-by: Heiko Stuebner +Acked-by: Viresh Kumar +Acked-by: Krzysztof Kozlowski +Acked-by: Yoshihiro Shimoda +Reviewed-by: Jaehoon Chung +Cc: Kishon Vijay Abraham I +Cc: David S. Miller +Cc: Geert Uytterhoeven +Cc: Yoshihiro Shimoda +Cc: Guenter Roeck +Cc: Heiko Stuebner +Cc: Viresh Kumar +Cc: Maxime Ripard +Cc: Chen-Yu Tsai +Cc: Sylwester Nawrocki +Cc: Krzysztof Kozlowski +Cc: Jaehoon Chung +Cc: Stephen Boyd +Cc: Martin Blumenstingl +Cc: linux-arm-kernel@lists.infradead.org +Cc: linux-arm-msm@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Cc: linux-omap@vger.kernel.org +Cc: linux-renesas-soc@vger.kernel.org +Cc: linux-rockchip@lists.infradead.org +Cc: linux-samsung-soc@vger.kernel.org +Cc: linux-usb@vger.kernel.org +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + MAINTAINERS | 18 +- + drivers/phy/Kconfig | 491 +--------- + drivers/phy/Makefile | 70 +- + drivers/phy/allwinner/Kconfig | 31 + + drivers/phy/allwinner/Makefile | 2 + + drivers/phy/allwinner/phy-sun4i-usb.c | 891 +++++++++++++++++ + drivers/phy/allwinner/phy-sun9i-usb.c | 202 ++++ + drivers/phy/amlogic/Kconfig | 14 + + drivers/phy/amlogic/Makefile | 1 + + drivers/phy/amlogic/phy-meson8b-usb2.c | 286 ++++++ + drivers/phy/broadcom/Kconfig | 55 ++ + drivers/phy/broadcom/Makefile | 6 + + drivers/phy/broadcom/phy-bcm-cygnus-pcie.c | 221 +++++ + drivers/phy/broadcom/phy-bcm-kona-usb2.c | 155 +++ + drivers/phy/broadcom/phy-bcm-ns-usb2.c | 137 +++ + drivers/phy/broadcom/phy-bcm-ns-usb3.c | 303 ++++++ + drivers/phy/broadcom/phy-bcm-ns2-pcie.c | 101 ++ + drivers/phy/broadcom/phy-brcm-sata.c | 493 ++++++++++ + drivers/phy/hisilicon/Kconfig | 20 + + drivers/phy/hisilicon/Makefile | 2 + + drivers/phy/hisilicon/phy-hi6220-usb.c | 168 ++++ + drivers/phy/hisilicon/phy-hix5hd2-sata.c | 191 ++++ + drivers/phy/marvell/Kconfig | 50 + + drivers/phy/marvell/Makefile | 6 + + drivers/phy/marvell/phy-armada375-usb2.c | 158 +++ + drivers/phy/marvell/phy-berlin-sata.c | 299 ++++++ + drivers/phy/marvell/phy-berlin-usb.c | 214 ++++ + drivers/phy/marvell/phy-mvebu-sata.c | 138 +++ + drivers/phy/marvell/phy-pxa-28nm-hsic.c | 220 +++++ + drivers/phy/marvell/phy-pxa-28nm-usb2.c | 355 +++++++ + drivers/phy/phy-armada375-usb2.c | 158 --- + drivers/phy/phy-bcm-cygnus-pcie.c | 221 ----- + drivers/phy/phy-bcm-kona-usb2.c | 155 --- + drivers/phy/phy-bcm-ns-usb2.c | 137 --- + drivers/phy/phy-bcm-ns-usb3.c | 303 ------ + drivers/phy/phy-bcm-ns2-pcie.c | 101 -- + drivers/phy/phy-berlin-sata.c | 299 ------ + drivers/phy/phy-berlin-usb.c | 214 ---- + drivers/phy/phy-brcm-sata.c | 493 ---------- + drivers/phy/phy-da8xx-usb.c | 251 ----- + drivers/phy/phy-dm816x-usb.c | 290 ------ + drivers/phy/phy-exynos-dp-video.c | 122 --- + drivers/phy/phy-exynos-mipi-video.c | 377 -------- + drivers/phy/phy-exynos-pcie.c | 281 ------ + drivers/phy/phy-exynos4210-usb2.c | 260 ----- + drivers/phy/phy-exynos4x12-usb2.c | 378 -------- + drivers/phy/phy-exynos5-usbdrd.c | 782 --------------- + drivers/phy/phy-exynos5250-sata.c | 250 ----- + drivers/phy/phy-exynos5250-usb2.c | 401 -------- + drivers/phy/phy-hi6220-usb.c | 168 ---- + drivers/phy/phy-hix5hd2-sata.c | 191 ---- + drivers/phy/phy-meson8b-usb2.c | 286 ------ + drivers/phy/phy-miphy28lp.c | 1286 ------------------------- + drivers/phy/phy-mvebu-sata.c | 138 --- + drivers/phy/phy-omap-control.c | 360 ------- + drivers/phy/phy-omap-usb2.c | 439 --------- + drivers/phy/phy-pxa-28nm-hsic.c | 220 ----- + drivers/phy/phy-pxa-28nm-usb2.c | 355 ------- + drivers/phy/phy-qcom-apq8064-sata.c | 287 ------ + drivers/phy/phy-qcom-ipq806x-sata.c | 209 ---- + drivers/phy/phy-qcom-qmp.c | 1153 ---------------------- + drivers/phy/phy-qcom-qusb2.c | 493 ---------- + drivers/phy/phy-qcom-ufs-i.h | 155 --- + drivers/phy/phy-qcom-ufs-qmp-14nm.c | 178 ---- + drivers/phy/phy-qcom-ufs-qmp-14nm.h | 177 ---- + drivers/phy/phy-qcom-ufs-qmp-20nm.c | 232 ----- + drivers/phy/phy-qcom-ufs-qmp-20nm.h | 235 ----- + drivers/phy/phy-qcom-ufs.c | 691 ------------- + drivers/phy/phy-qcom-usb-hs.c | 295 ------ + drivers/phy/phy-qcom-usb-hsic.c | 159 --- + drivers/phy/phy-rcar-gen2.c | 337 ------- + drivers/phy/phy-rcar-gen3-usb2.c | 507 ---------- + drivers/phy/phy-rockchip-dp.c | 154 --- + drivers/phy/phy-rockchip-emmc.c | 383 -------- + drivers/phy/phy-rockchip-inno-usb2.c | 1284 ------------------------ + drivers/phy/phy-rockchip-pcie.c | 346 ------- + drivers/phy/phy-rockchip-typec.c | 1023 -------------------- + drivers/phy/phy-rockchip-usb.c | 540 ----------- + drivers/phy/phy-s5pv210-usb2.c | 187 ---- + drivers/phy/phy-samsung-usb2.c | 267 ----- + drivers/phy/phy-samsung-usb2.h | 73 -- + drivers/phy/phy-spear1310-miphy.c | 261 ----- + drivers/phy/phy-spear1340-miphy.c | 294 ------ + drivers/phy/phy-stih407-usb.c | 180 ---- + drivers/phy/phy-sun4i-usb.c | 891 ----------------- + drivers/phy/phy-sun9i-usb.c | 202 ---- + drivers/phy/phy-ti-pipe3.c | 697 -------------- + drivers/phy/phy-tusb1210.c | 146 --- + drivers/phy/phy-twl4030-usb.c | 839 ---------------- + drivers/phy/qualcomm/Kconfig | 58 ++ + drivers/phy/qualcomm/Makefile | 9 + + drivers/phy/qualcomm/phy-qcom-apq8064-sata.c | 287 ++++++ + drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c | 209 ++++ + drivers/phy/qualcomm/phy-qcom-qmp.c | 1153 ++++++++++++++++++++++ + drivers/phy/qualcomm/phy-qcom-qusb2.c | 493 ++++++++++ + drivers/phy/qualcomm/phy-qcom-ufs-i.h | 155 +++ + drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 178 ++++ + drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h | 177 ++++ + drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 232 +++++ + drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h | 235 +++++ + drivers/phy/qualcomm/phy-qcom-ufs.c | 691 +++++++++++++ + drivers/phy/qualcomm/phy-qcom-usb-hs.c | 295 ++++++ + drivers/phy/qualcomm/phy-qcom-usb-hsic.c | 159 +++ + drivers/phy/renesas/Kconfig | 17 + + drivers/phy/renesas/Makefile | 2 + + drivers/phy/renesas/phy-rcar-gen2.c | 337 +++++++ + drivers/phy/renesas/phy-rcar-gen3-usb2.c | 507 ++++++++++ + drivers/phy/rockchip/Kconfig | 51 + + drivers/phy/rockchip/Makefile | 6 + + drivers/phy/rockchip/phy-rockchip-dp.c | 154 +++ + drivers/phy/rockchip/phy-rockchip-emmc.c | 383 ++++++++ + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 1284 ++++++++++++++++++++++++ + drivers/phy/rockchip/phy-rockchip-pcie.c | 346 +++++++ + drivers/phy/rockchip/phy-rockchip-typec.c | 1023 ++++++++++++++++++++ + drivers/phy/rockchip/phy-rockchip-usb.c | 540 +++++++++++ + drivers/phy/samsung/Kconfig | 95 ++ + drivers/phy/samsung/Makefile | 11 + + drivers/phy/samsung/phy-exynos-dp-video.c | 122 +++ + drivers/phy/samsung/phy-exynos-mipi-video.c | 377 ++++++++ + drivers/phy/samsung/phy-exynos-pcie.c | 281 ++++++ + drivers/phy/samsung/phy-exynos4210-usb2.c | 260 +++++ + drivers/phy/samsung/phy-exynos4x12-usb2.c | 378 ++++++++ + drivers/phy/samsung/phy-exynos5-usbdrd.c | 782 +++++++++++++++ + drivers/phy/samsung/phy-exynos5250-sata.c | 250 +++++ + drivers/phy/samsung/phy-exynos5250-usb2.c | 401 ++++++++ + drivers/phy/samsung/phy-s5pv210-usb2.c | 187 ++++ + drivers/phy/samsung/phy-samsung-usb2.c | 267 +++++ + drivers/phy/samsung/phy-samsung-usb2.h | 73 ++ + drivers/phy/st/Kconfig | 33 + + drivers/phy/st/Makefile | 4 + + drivers/phy/st/phy-miphy28lp.c | 1286 +++++++++++++++++++++++++ + drivers/phy/st/phy-spear1310-miphy.c | 261 +++++ + drivers/phy/st/phy-spear1340-miphy.c | 294 ++++++ + drivers/phy/st/phy-stih407-usb.c | 180 ++++ + drivers/phy/ti/Kconfig | 78 ++ + drivers/phy/ti/Makefile | 7 + + drivers/phy/ti/phy-da8xx-usb.c | 251 +++++ + drivers/phy/ti/phy-dm816x-usb.c | 290 ++++++ + drivers/phy/ti/phy-omap-control.c | 360 +++++++ + drivers/phy/ti/phy-omap-usb2.c | 439 +++++++++ + drivers/phy/ti/phy-ti-pipe3.c | 697 ++++++++++++++ + drivers/phy/ti/phy-tusb1210.c | 146 +++ + drivers/phy/ti/phy-twl4030-usb.c | 839 ++++++++++++++++ + 143 files changed, 22382 insertions(+), 22337 deletions(-) + create mode 100644 drivers/phy/allwinner/Kconfig + create mode 100644 drivers/phy/allwinner/Makefile + create mode 100644 drivers/phy/allwinner/phy-sun4i-usb.c + create mode 100644 drivers/phy/allwinner/phy-sun9i-usb.c + create mode 100644 drivers/phy/amlogic/Kconfig + create mode 100644 drivers/phy/amlogic/Makefile + create mode 100644 drivers/phy/amlogic/phy-meson8b-usb2.c + create mode 100644 drivers/phy/broadcom/Kconfig + create mode 100644 drivers/phy/broadcom/Makefile + create mode 100644 drivers/phy/broadcom/phy-bcm-cygnus-pcie.c + create mode 100644 drivers/phy/broadcom/phy-bcm-kona-usb2.c + create mode 100644 drivers/phy/broadcom/phy-bcm-ns-usb2.c + create mode 100644 drivers/phy/broadcom/phy-bcm-ns-usb3.c + create mode 100644 drivers/phy/broadcom/phy-bcm-ns2-pcie.c + create mode 100644 drivers/phy/broadcom/phy-brcm-sata.c + create mode 100644 drivers/phy/hisilicon/Kconfig + create mode 100644 drivers/phy/hisilicon/Makefile + create mode 100644 drivers/phy/hisilicon/phy-hi6220-usb.c + create mode 100644 drivers/phy/hisilicon/phy-hix5hd2-sata.c + create mode 100644 drivers/phy/marvell/Kconfig + create mode 100644 drivers/phy/marvell/Makefile + create mode 100644 drivers/phy/marvell/phy-armada375-usb2.c + create mode 100644 drivers/phy/marvell/phy-berlin-sata.c + create mode 100644 drivers/phy/marvell/phy-berlin-usb.c + create mode 100644 drivers/phy/marvell/phy-mvebu-sata.c + create mode 100644 drivers/phy/marvell/phy-pxa-28nm-hsic.c + create mode 100644 drivers/phy/marvell/phy-pxa-28nm-usb2.c + delete mode 100644 drivers/phy/phy-armada375-usb2.c + delete mode 100644 drivers/phy/phy-bcm-cygnus-pcie.c + delete mode 100644 drivers/phy/phy-bcm-kona-usb2.c + delete mode 100644 drivers/phy/phy-bcm-ns-usb2.c + delete mode 100644 drivers/phy/phy-bcm-ns-usb3.c + delete mode 100644 drivers/phy/phy-bcm-ns2-pcie.c + delete mode 100644 drivers/phy/phy-berlin-sata.c + delete mode 100644 drivers/phy/phy-berlin-usb.c + delete mode 100644 drivers/phy/phy-brcm-sata.c + delete mode 100644 drivers/phy/phy-da8xx-usb.c + delete mode 100644 drivers/phy/phy-dm816x-usb.c + delete mode 100644 drivers/phy/phy-exynos-dp-video.c + delete mode 100644 drivers/phy/phy-exynos-mipi-video.c + delete mode 100644 drivers/phy/phy-exynos-pcie.c + delete mode 100644 drivers/phy/phy-exynos4210-usb2.c + delete mode 100644 drivers/phy/phy-exynos4x12-usb2.c + delete mode 100644 drivers/phy/phy-exynos5-usbdrd.c + delete mode 100644 drivers/phy/phy-exynos5250-sata.c + delete mode 100644 drivers/phy/phy-exynos5250-usb2.c + delete mode 100644 drivers/phy/phy-hi6220-usb.c + delete mode 100644 drivers/phy/phy-hix5hd2-sata.c + delete mode 100644 drivers/phy/phy-meson8b-usb2.c + delete mode 100644 drivers/phy/phy-miphy28lp.c + delete mode 100644 drivers/phy/phy-mvebu-sata.c + delete mode 100644 drivers/phy/phy-omap-control.c + delete mode 100644 drivers/phy/phy-omap-usb2.c + delete mode 100644 drivers/phy/phy-pxa-28nm-hsic.c + delete mode 100644 drivers/phy/phy-pxa-28nm-usb2.c + delete mode 100644 drivers/phy/phy-qcom-apq8064-sata.c + delete mode 100644 drivers/phy/phy-qcom-ipq806x-sata.c + delete mode 100644 drivers/phy/phy-qcom-qmp.c + delete mode 100644 drivers/phy/phy-qcom-qusb2.c + delete mode 100644 drivers/phy/phy-qcom-ufs-i.h + delete mode 100644 drivers/phy/phy-qcom-ufs-qmp-14nm.c + delete mode 100644 drivers/phy/phy-qcom-ufs-qmp-14nm.h + delete mode 100644 drivers/phy/phy-qcom-ufs-qmp-20nm.c + delete mode 100644 drivers/phy/phy-qcom-ufs-qmp-20nm.h + delete mode 100644 drivers/phy/phy-qcom-ufs.c + delete mode 100644 drivers/phy/phy-qcom-usb-hs.c + delete mode 100644 drivers/phy/phy-qcom-usb-hsic.c + delete mode 100644 drivers/phy/phy-rcar-gen2.c + delete mode 100644 drivers/phy/phy-rcar-gen3-usb2.c + delete mode 100644 drivers/phy/phy-rockchip-dp.c + delete mode 100644 drivers/phy/phy-rockchip-emmc.c + delete mode 100644 drivers/phy/phy-rockchip-inno-usb2.c + delete mode 100644 drivers/phy/phy-rockchip-pcie.c + delete mode 100644 drivers/phy/phy-rockchip-typec.c + delete mode 100644 drivers/phy/phy-rockchip-usb.c + delete mode 100644 drivers/phy/phy-s5pv210-usb2.c + delete mode 100644 drivers/phy/phy-samsung-usb2.c + delete mode 100644 drivers/phy/phy-samsung-usb2.h + delete mode 100644 drivers/phy/phy-spear1310-miphy.c + delete mode 100644 drivers/phy/phy-spear1340-miphy.c + delete mode 100644 drivers/phy/phy-stih407-usb.c + delete mode 100644 drivers/phy/phy-sun4i-usb.c + delete mode 100644 drivers/phy/phy-sun9i-usb.c + delete mode 100644 drivers/phy/phy-ti-pipe3.c + delete mode 100644 drivers/phy/phy-tusb1210.c + delete mode 100644 drivers/phy/phy-twl4030-usb.c + create mode 100644 drivers/phy/qualcomm/Kconfig + create mode 100644 drivers/phy/qualcomm/Makefile + create mode 100644 drivers/phy/qualcomm/phy-qcom-apq8064-sata.c + create mode 100644 drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c + create mode 100644 drivers/phy/qualcomm/phy-qcom-qmp.c + create mode 100644 drivers/phy/qualcomm/phy-qcom-qusb2.c + create mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-i.h + create mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c + create mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h + create mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c + create mode 100644 drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h + create mode 100644 drivers/phy/qualcomm/phy-qcom-ufs.c + create mode 100644 drivers/phy/qualcomm/phy-qcom-usb-hs.c + create mode 100644 drivers/phy/qualcomm/phy-qcom-usb-hsic.c + create mode 100644 drivers/phy/renesas/Kconfig + create mode 100644 drivers/phy/renesas/Makefile + create mode 100644 drivers/phy/renesas/phy-rcar-gen2.c + create mode 100644 drivers/phy/renesas/phy-rcar-gen3-usb2.c + create mode 100644 drivers/phy/rockchip/Kconfig + create mode 100644 drivers/phy/rockchip/Makefile + create mode 100644 drivers/phy/rockchip/phy-rockchip-dp.c + create mode 100644 drivers/phy/rockchip/phy-rockchip-emmc.c + create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb2.c + create mode 100644 drivers/phy/rockchip/phy-rockchip-pcie.c + create mode 100644 drivers/phy/rockchip/phy-rockchip-typec.c + create mode 100644 drivers/phy/rockchip/phy-rockchip-usb.c + create mode 100644 drivers/phy/samsung/Kconfig + create mode 100644 drivers/phy/samsung/Makefile + create mode 100644 drivers/phy/samsung/phy-exynos-dp-video.c + create mode 100644 drivers/phy/samsung/phy-exynos-mipi-video.c + create mode 100644 drivers/phy/samsung/phy-exynos-pcie.c + create mode 100644 drivers/phy/samsung/phy-exynos4210-usb2.c + create mode 100644 drivers/phy/samsung/phy-exynos4x12-usb2.c + create mode 100644 drivers/phy/samsung/phy-exynos5-usbdrd.c + create mode 100644 drivers/phy/samsung/phy-exynos5250-sata.c + create mode 100644 drivers/phy/samsung/phy-exynos5250-usb2.c + create mode 100644 drivers/phy/samsung/phy-s5pv210-usb2.c + create mode 100644 drivers/phy/samsung/phy-samsung-usb2.c + create mode 100644 drivers/phy/samsung/phy-samsung-usb2.h + create mode 100644 drivers/phy/st/Kconfig + create mode 100644 drivers/phy/st/Makefile + create mode 100644 drivers/phy/st/phy-miphy28lp.c + create mode 100644 drivers/phy/st/phy-spear1310-miphy.c + create mode 100644 drivers/phy/st/phy-spear1340-miphy.c + create mode 100644 drivers/phy/st/phy-stih407-usb.c + create mode 100644 drivers/phy/ti/Kconfig + create mode 100644 drivers/phy/ti/Makefile + create mode 100644 drivers/phy/ti/phy-da8xx-usb.c + create mode 100644 drivers/phy/ti/phy-dm816x-usb.c + create mode 100644 drivers/phy/ti/phy-omap-control.c + create mode 100644 drivers/phy/ti/phy-omap-usb2.c + create mode 100644 drivers/phy/ti/phy-ti-pipe3.c + create mode 100644 drivers/phy/ti/phy-tusb1210.c + create mode 100644 drivers/phy/ti/phy-twl4030-usb.c + +diff --git a/MAINTAINERS b/MAINTAINERS +index 336eec0f140e..521fe1f094b1 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1843,8 +1843,8 @@ F: drivers/i2c/busses/i2c-st.c + F: drivers/media/rc/st_rc.c + F: drivers/media/platform/sti/c8sectpfe/ + F: drivers/mmc/host/sdhci-st.c +-F: drivers/phy/phy-miphy28lp.c +-F: drivers/phy/phy-stih407-usb.c ++F: drivers/phy/st/phy-miphy28lp.c ++F: drivers/phy/st/phy-stih407-usb.c + F: drivers/pinctrl/pinctrl-st.c + F: drivers/remoteproc/st_remoteproc.c + F: drivers/remoteproc/st_slim_rproc.c +@@ -10855,7 +10855,7 @@ RENESAS USB2 PHY DRIVER + M: Yoshihiro Shimoda + L: linux-renesas-soc@vger.kernel.org + S: Maintained +-F: drivers/phy/phy-rcar-gen3-usb2.c ++F: drivers/phy/renesas/phy-rcar-gen3-usb2.c + + RESET CONTROLLER FRAMEWORK + M: Philipp Zabel +@@ -11257,12 +11257,12 @@ L: linux-kernel@vger.kernel.org + S: Supported + F: Documentation/devicetree/bindings/phy/samsung-phy.txt + F: Documentation/phy/samsung-usb2.txt +-F: drivers/phy/phy-exynos4210-usb2.c +-F: drivers/phy/phy-exynos4x12-usb2.c +-F: drivers/phy/phy-exynos5250-usb2.c +-F: drivers/phy/phy-s5pv210-usb2.c +-F: drivers/phy/phy-samsung-usb2.c +-F: drivers/phy/phy-samsung-usb2.h ++F: drivers/phy/samsung/phy-exynos4210-usb2.c ++F: drivers/phy/samsung/phy-exynos4x12-usb2.c ++F: drivers/phy/samsung/phy-exynos5250-usb2.c ++F: drivers/phy/samsung/phy-s5pv210-usb2.c ++F: drivers/phy/samsung/phy-samsung-usb2.c ++F: drivers/phy/samsung/phy-samsung-usb2.h + + SERIAL DRIVERS + M: Greg Kroah-Hartman +diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig +index afaf7b643eeb..01009b2a7d74 100644 +--- a/drivers/phy/Kconfig ++++ b/drivers/phy/Kconfig +@@ -15,73 +15,6 @@ config GENERIC_PHY + phy users can obtain reference to the PHY. All the users of this + framework should select this config. + +-config PHY_BCM_NS_USB2 +- tristate "Broadcom Northstar USB 2.0 PHY Driver" +- depends on ARCH_BCM_IPROC || COMPILE_TEST +- depends on HAS_IOMEM && OF +- select GENERIC_PHY +- help +- Enable this to support Broadcom USB 2.0 PHY connected to the USB +- controller on Northstar family. +- +-config PHY_BCM_NS_USB3 +- tristate "Broadcom Northstar USB 3.0 PHY Driver" +- depends on ARCH_BCM_IPROC || COMPILE_TEST +- depends on HAS_IOMEM && OF +- select GENERIC_PHY +- help +- Enable this to support Broadcom USB 3.0 PHY connected to the USB +- controller on Northstar family. +- +-config PHY_BERLIN_USB +- tristate "Marvell Berlin USB PHY Driver" +- depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF +- select GENERIC_PHY +- help +- Enable this to support the USB PHY on Marvell Berlin SoCs. +- +-config PHY_BERLIN_SATA +- tristate "Marvell Berlin SATA PHY driver" +- depends on ARCH_BERLIN && HAS_IOMEM && OF +- select GENERIC_PHY +- help +- Enable this to support the SATA PHY on Marvell Berlin SoCs. +- +-config ARMADA375_USBCLUSTER_PHY +- def_bool y +- depends on MACH_ARMADA_375 || COMPILE_TEST +- depends on OF && HAS_IOMEM +- select GENERIC_PHY +- +-config PHY_DA8XX_USB +- tristate "TI DA8xx USB PHY Driver" +- depends on ARCH_DAVINCI_DA8XX +- select GENERIC_PHY +- select MFD_SYSCON +- help +- Enable this to support the USB PHY on DA8xx SoCs. +- +- This driver controls both the USB 1.1 PHY and the USB 2.0 PHY. +- +-config PHY_DM816X_USB +- tristate "TI dm816x USB PHY driver" +- depends on ARCH_OMAP2PLUS +- depends on USB_SUPPORT +- select GENERIC_PHY +- select USB_PHY +- help +- Enable this for dm816x USB to work. +- +-config PHY_EXYNOS_MIPI_VIDEO +- tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" +- depends on HAS_IOMEM +- depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST +- select GENERIC_PHY +- default y if ARCH_S5PV210 || ARCH_EXYNOS +- help +- Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P +- and EXYNOS SoCs. +- + config PHY_LPC18XX_USB_OTG + tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver" + depends on OF && (ARCH_LPC18XX || COMPILE_TEST) +@@ -93,146 +26,6 @@ config PHY_LPC18XX_USB_OTG + This driver is need for USB0 support on LPC18xx/43xx and takes + care of enabling and clock setup. + +-config PHY_PXA_28NM_HSIC +- tristate "Marvell USB HSIC 28nm PHY Driver" +- depends on HAS_IOMEM +- select GENERIC_PHY +- help +- Enable this to support Marvell USB HSIC PHY driver for Marvell +- SoC. This driver will do the PHY initialization and shutdown. +- The PHY driver will be used by Marvell ehci driver. +- +- To compile this driver as a module, choose M here. +- +-config PHY_PXA_28NM_USB2 +- tristate "Marvell USB 2.0 28nm PHY Driver" +- depends on HAS_IOMEM +- select GENERIC_PHY +- help +- Enable this to support Marvell USB 2.0 PHY driver for Marvell +- SoC. This driver will do the PHY initialization and shutdown. +- The PHY driver will be used by Marvell udc/ehci/otg driver. +- +- To compile this driver as a module, choose M here. +- +-config PHY_MVEBU_SATA +- def_bool y +- depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD +- depends on OF +- select GENERIC_PHY +- +-config PHY_MIPHY28LP +- tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407" +- depends on ARCH_STI +- select GENERIC_PHY +- help +- Enable this to support the miphy transceiver (for SATA/PCIE/USB3) +- that is part of STMicroelectronics STiH407 SoC. +- +-config PHY_RCAR_GEN2 +- tristate "Renesas R-Car generation 2 USB PHY driver" +- depends on ARCH_RENESAS +- depends on GENERIC_PHY +- help +- Support for USB PHY found on Renesas R-Car generation 2 SoCs. +- +-config PHY_RCAR_GEN3_USB2 +- tristate "Renesas R-Car generation 3 USB 2.0 PHY driver" +- depends on ARCH_RENESAS +- depends on EXTCON +- select GENERIC_PHY +- help +- Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs. +- +-config OMAP_CONTROL_PHY +- tristate "OMAP CONTROL PHY Driver" +- depends on ARCH_OMAP2PLUS || COMPILE_TEST +- help +- Enable this to add support for the PHY part present in the control +- module. This driver has API to power on the USB2 PHY and to write to +- the mailbox. The mailbox is present only in omap4 and the register to +- power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an +- additional register to power on USB3 PHY/SATA PHY/PCIE PHY +- (PIPE3 PHY). +- +-config OMAP_USB2 +- tristate "OMAP USB2 PHY Driver" +- depends on ARCH_OMAP2PLUS +- depends on USB_SUPPORT +- select GENERIC_PHY +- select USB_PHY +- select OMAP_CONTROL_PHY +- depends on OMAP_OCP2SCP +- help +- Enable this to support the transceiver that is part of SOC. This +- driver takes care of all the PHY functionality apart from comparator. +- The USB OTG controller communicates with the comparator using this +- driver. +- +-config TI_PIPE3 +- tristate "TI PIPE3 PHY Driver" +- depends on ARCH_OMAP2PLUS || COMPILE_TEST +- select GENERIC_PHY +- select OMAP_CONTROL_PHY +- depends on OMAP_OCP2SCP +- help +- Enable this to support the PIPE3 PHY that is part of TI SOCs. This +- driver takes care of all the PHY functionality apart from comparator. +- This driver interacts with the "OMAP Control PHY Driver" to power +- on/off the PHY. +- +-config TWL4030_USB +- tristate "TWL4030 USB Transceiver Driver" +- depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS +- depends on USB_SUPPORT +- depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't 'y' +- select GENERIC_PHY +- select USB_PHY +- help +- Enable this to support the USB OTG transceiver on TWL4030 +- family chips (including the TWL5030 and TPS659x0 devices). +- This transceiver supports high and full speed devices plus, +- in host mode, low speed. +- +-config PHY_EXYNOS_DP_VIDEO +- tristate "EXYNOS SoC series Display Port PHY driver" +- depends on OF +- depends on ARCH_EXYNOS || COMPILE_TEST +- default ARCH_EXYNOS +- select GENERIC_PHY +- help +- Support for Display Port PHY found on Samsung EXYNOS SoCs. +- +-config BCM_KONA_USB2_PHY +- tristate "Broadcom Kona USB2 PHY Driver" +- depends on HAS_IOMEM +- select GENERIC_PHY +- help +- Enable this to support the Broadcom Kona USB 2.0 PHY. +- +-config PHY_EXYNOS5250_SATA +- tristate "Exynos5250 Sata SerDes/PHY driver" +- depends on SOC_EXYNOS5250 +- depends on HAS_IOMEM +- depends on OF +- select GENERIC_PHY +- select I2C +- select I2C_S3C2410 +- select MFD_SYSCON +- help +- Enable this to support SATA SerDes/Phy found on Samsung's +- Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s, +- SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host +- port to accept one SATA device. +- +-config PHY_HIX5HD2_SATA +- tristate "HIX5HD2 SATA PHY Driver" +- depends on ARCH_HIX5HD2 && OF && HAS_IOMEM +- select GENERIC_PHY +- select MFD_SYSCON +- help +- Support for SATA PHY on Hisilicon hix5hd2 Soc. +- + config PHY_MT65XX_USB3 + tristate "Mediatek USB3.0 PHY Driver" + depends on ARCH_MEDIATEK && OF +@@ -241,104 +34,6 @@ config PHY_MT65XX_USB3 + Say 'Y' here to add support for Mediatek USB3.0 PHY driver, + it supports multiple usb2.0 and usb3.0 ports. + +-config PHY_HI6220_USB +- tristate "hi6220 USB PHY support" +- depends on (ARCH_HISI && ARM64) || COMPILE_TEST +- select GENERIC_PHY +- select MFD_SYSCON +- help +- Enable this to support the HISILICON HI6220 USB PHY. +- +- To compile this driver as a module, choose M here. +- +-config PHY_SUN4I_USB +- tristate "Allwinner sunxi SoC USB PHY driver" +- depends on ARCH_SUNXI && HAS_IOMEM && OF +- depends on RESET_CONTROLLER +- depends on EXTCON +- depends on POWER_SUPPLY +- depends on USB_SUPPORT +- select GENERIC_PHY +- select USB_COMMON +- help +- Enable this to support the transceiver that is part of Allwinner +- sunxi SoCs. +- +- This driver controls the entire USB PHY block, both the USB OTG +- parts, as well as the 2 regular USB 2 host PHYs. +- +-config PHY_SUN9I_USB +- tristate "Allwinner sun9i SoC USB PHY driver" +- depends on ARCH_SUNXI && HAS_IOMEM && OF +- depends on RESET_CONTROLLER +- depends on USB_SUPPORT +- select USB_COMMON +- select GENERIC_PHY +- help +- Enable this to support the transceiver that is part of Allwinner +- sun9i SoCs. +- +- This driver controls each individual USB 2 host PHY. +- +-config PHY_SAMSUNG_USB2 +- tristate "Samsung USB 2.0 PHY driver" +- depends on HAS_IOMEM +- depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 +- select GENERIC_PHY +- select MFD_SYSCON +- default ARCH_EXYNOS +- help +- Enable this to support the Samsung USB 2.0 PHY driver for Samsung +- SoCs. This driver provides the interface for USB 2.0 PHY. Support +- for particular PHYs will be enabled based on the SoC type in addition +- to this driver. +- +-config PHY_S5PV210_USB2 +- bool "Support for S5PV210" +- depends on PHY_SAMSUNG_USB2 +- depends on ARCH_S5PV210 +- help +- Enable USB PHY support for S5PV210. This option requires that Samsung +- USB 2.0 PHY driver is enabled and means that support for this +- particular SoC is compiled in the driver. In case of S5PV210 two phys +- are available - device and host. +- +-config PHY_EXYNOS4210_USB2 +- bool +- depends on PHY_SAMSUNG_USB2 +- default CPU_EXYNOS4210 +- +-config PHY_EXYNOS4X12_USB2 +- bool +- depends on PHY_SAMSUNG_USB2 +- default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412 +- +-config PHY_EXYNOS5250_USB2 +- bool +- depends on PHY_SAMSUNG_USB2 +- default SOC_EXYNOS5250 || SOC_EXYNOS5420 +- +-config PHY_EXYNOS5_USBDRD +- tristate "Exynos5 SoC series USB DRD PHY driver" +- depends on ARCH_EXYNOS && OF +- depends on HAS_IOMEM +- depends on USB_DWC3_EXYNOS +- select GENERIC_PHY +- select MFD_SYSCON +- default y +- help +- Enable USB DRD PHY support for Exynos 5 SoC series. +- This driver provides PHY interface for USB 3.0 DRD controller +- present on Exynos5 SoC series. +- +-config PHY_EXYNOS_PCIE +- bool "Exynos PCIe PHY driver" +- depends on OF && (ARCH_EXYNOS || COMPILE_TEST) +- select GENERIC_PHY +- help +- Enable PCIe PHY support for Exynos SoC series. +- This driver provides PHY interface for Exynos PCIe controller. +- + config PHY_PISTACHIO_USB + tristate "IMG Pistachio USB2.0 PHY driver" + depends on MACH_PISTACHIO +@@ -346,83 +41,6 @@ config PHY_PISTACHIO_USB + help + Enable this to support the USB2.0 PHY on the IMG Pistachio SoC. + +-config PHY_QCOM_APQ8064_SATA +- tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" +- depends on ARCH_QCOM +- depends on HAS_IOMEM +- depends on OF +- select GENERIC_PHY +- +-config PHY_QCOM_IPQ806X_SATA +- tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" +- depends on ARCH_QCOM +- depends on HAS_IOMEM +- depends on OF +- select GENERIC_PHY +- +-config PHY_ROCKCHIP_USB +- tristate "Rockchip USB2 PHY Driver" +- depends on ARCH_ROCKCHIP && OF +- select GENERIC_PHY +- help +- Enable this to support the Rockchip USB 2.0 PHY. +- +-config PHY_ROCKCHIP_INNO_USB2 +- tristate "Rockchip INNO USB2PHY Driver" +- depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF +- depends on COMMON_CLK +- depends on EXTCON +- depends on USB_SUPPORT +- select GENERIC_PHY +- select USB_COMMON +- help +- Support for Rockchip USB2.0 PHY with Innosilicon IP block. +- +-config PHY_ROCKCHIP_EMMC +- tristate "Rockchip EMMC PHY Driver" +- depends on ARCH_ROCKCHIP && OF +- select GENERIC_PHY +- help +- Enable this to support the Rockchip EMMC PHY. +- +-config PHY_ROCKCHIP_DP +- tristate "Rockchip Display Port PHY Driver" +- depends on ARCH_ROCKCHIP && OF +- select GENERIC_PHY +- help +- Enable this to support the Rockchip Display Port PHY. +- +-config PHY_ROCKCHIP_PCIE +- tristate "Rockchip PCIe PHY Driver" +- depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST +- select GENERIC_PHY +- select MFD_SYSCON +- help +- Enable this to support the Rockchip PCIe PHY. +- +-config PHY_ROCKCHIP_TYPEC +- tristate "Rockchip TYPEC PHY Driver" +- depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) +- select EXTCON +- select GENERIC_PHY +- select RESET_CONTROLLER +- help +- Enable this to support the Rockchip USB TYPEC PHY. +- +-config PHY_ST_SPEAR1310_MIPHY +- tristate "ST SPEAR1310-MIPHY driver" +- select GENERIC_PHY +- depends on MACH_SPEAR1310 || COMPILE_TEST +- help +- Support for ST SPEAr1310 MIPHY which can be used for PCIe and SATA. +- +-config PHY_ST_SPEAR1340_MIPHY +- tristate "ST SPEAR1340-MIPHY driver" +- select GENERIC_PHY +- depends on MACH_SPEAR1340 || COMPILE_TEST +- help +- Support for ST SPEAr1340 MIPHY which can be used for PCIe and SATA. +- + config PHY_XGENE + tristate "APM X-Gene 15Gbps PHY support" + depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST) +@@ -430,104 +48,17 @@ config PHY_XGENE + help + This option enables support for APM X-Gene SoC multi-purpose PHY. + +-config PHY_STIH407_USB +- tristate "STMicroelectronics USB2 picoPHY driver for STiH407 family" +- depends on RESET_CONTROLLER +- depends on ARCH_STI || COMPILE_TEST +- select GENERIC_PHY +- help +- Enable this support to enable the picoPHY device used by USB2 +- and USB3 controllers on STMicroelectronics STiH407 SoC families. +- +-config PHY_QCOM_QMP +- tristate "Qualcomm QMP PHY Driver" +- depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST) +- select GENERIC_PHY +- help +- Enable this to support the QMP PHY transceiver that is used +- with controllers such as PCIe, UFS, and USB on Qualcomm chips. +- +-config PHY_QCOM_QUSB2 +- tristate "Qualcomm QUSB2 PHY Driver" +- depends on OF && (ARCH_QCOM || COMPILE_TEST) +- depends on NVMEM || !NVMEM +- select GENERIC_PHY +- help +- Enable this to support the HighSpeed QUSB2 PHY transceiver for USB +- controllers on Qualcomm chips. This driver supports the high-speed +- PHY which is usually paired with either the ChipIdea or Synopsys DWC3 +- USB IPs on MSM SOCs. +- +-config PHY_QCOM_UFS +- tristate "Qualcomm UFS PHY driver" +- depends on OF && ARCH_QCOM +- select GENERIC_PHY +- help +- Support for UFS PHY on QCOM chipsets. +- +-config PHY_QCOM_USB_HS +- tristate "Qualcomm USB HS PHY module" +- depends on USB_ULPI_BUS +- depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in +- select GENERIC_PHY +- help +- Support for the USB high-speed ULPI compliant phy on Qualcomm +- chipsets. +- +-config PHY_QCOM_USB_HSIC +- tristate "Qualcomm USB HSIC ULPI PHY module" +- depends on USB_ULPI_BUS +- select GENERIC_PHY +- help +- Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. +- +-config PHY_TUSB1210 +- tristate "TI TUSB1210 ULPI PHY module" +- depends on USB_ULPI_BUS +- select GENERIC_PHY +- help +- Support for TI TUSB1210 USB ULPI PHY. +- +-config PHY_BRCM_SATA +- tristate "Broadcom SATA PHY driver" +- depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST +- depends on OF +- select GENERIC_PHY +- default ARCH_BCM_IPROC +- help +- Enable this to support the Broadcom SATA PHY. +- If unsure, say N. +- +-config PHY_CYGNUS_PCIE +- tristate "Broadcom Cygnus PCIe PHY driver" +- depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) +- select GENERIC_PHY +- default ARCH_BCM_CYGNUS +- help +- Enable this to support the Broadcom Cygnus PCIe PHY. +- If unsure, say N. +- ++source "drivers/phy/allwinner/Kconfig" ++source "drivers/phy/amlogic/Kconfig" ++source "drivers/phy/broadcom/Kconfig" ++source "drivers/phy/hisilicon/Kconfig" ++source "drivers/phy/marvell/Kconfig" ++source "drivers/phy/qualcomm/Kconfig" ++source "drivers/phy/renesas/Kconfig" ++source "drivers/phy/rockchip/Kconfig" ++source "drivers/phy/samsung/Kconfig" ++source "drivers/phy/st/Kconfig" + source "drivers/phy/tegra/Kconfig" +- +-config PHY_NS2_PCIE +- tristate "Broadcom Northstar2 PCIe PHY driver" +- depends on OF && MDIO_BUS_MUX_BCM_IPROC +- select GENERIC_PHY +- default ARCH_BCM_IPROC +- help +- Enable this to support the Broadcom Northstar2 PCIe PHY. +- If unsure, say N. +- +-config PHY_MESON8B_USB2 +- tristate "Meson8b and GXBB USB2 PHY driver" +- default ARCH_MESON +- depends on OF && (ARCH_MESON || COMPILE_TEST) +- depends on USB_SUPPORT +- select USB_COMMON +- select GENERIC_PHY +- help +- Enable this to support the Meson USB2 PHYs found in Meson8b +- and GXBB SoCs. +- If unsure, say N. ++source "drivers/phy/ti/Kconfig" + + endmenu +diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile +index f8047b4639fa..c1bd1fa3c853 100644 +--- a/drivers/phy/Makefile ++++ b/drivers/phy/Makefile +@@ -3,64 +3,20 @@ + # + + obj-$(CONFIG_GENERIC_PHY) += phy-core.o +-obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o +-obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o +-obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o +-obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o +-obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o +-obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o +-obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o +-obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o +-obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o +-obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o + obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o +-obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o +-obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o +-obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o +-obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o +-obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o +-obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o +-obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o +-obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o +-obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o +-obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o +-obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o +-obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o +-obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o + obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o +-obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o +-obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o +-obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o +-phy-exynos-usb2-y += phy-samsung-usb2.o +-phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o +-phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o +-phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o +-phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o +-obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o +-obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o +-obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o +-obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o +-obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o +-obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o +-obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o +-obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o +-obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o +-obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o +-obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o +-obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o + obj-$(CONFIG_PHY_XGENE) += phy-xgene.o +-obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o +-obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o +-obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o +-obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o +-obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o +-obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.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_TUSB1210) += phy-tusb1210.o +-obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o + obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o +-obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o +-obj-$(CONFIG_ARCH_TEGRA) += tegra/ +-obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o +-obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o ++ ++obj-$(CONFIG_ARCH_SUNXI) += allwinner/ ++obj-$(CONFIG_ARCH_MESON) += amlogic/ ++obj-$(CONFIG_ARCH_RENESAS) += renesas/ ++obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ ++obj-$(CONFIG_ARCH_TEGRA) += tegra/ ++obj-y += broadcom/ \ ++ hisilicon/ \ ++ marvell/ \ ++ qualcomm/ \ ++ samsung/ \ ++ st/ \ ++ ti/ +diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig +new file mode 100644 +index 000000000000..cdc1e745ba47 +--- /dev/null ++++ b/drivers/phy/allwinner/Kconfig +@@ -0,0 +1,31 @@ ++# ++# Phy drivers for Allwinner platforms ++# ++config PHY_SUN4I_USB ++ tristate "Allwinner sunxi SoC USB PHY driver" ++ depends on ARCH_SUNXI && HAS_IOMEM && OF ++ depends on RESET_CONTROLLER ++ depends on EXTCON ++ depends on POWER_SUPPLY ++ depends on USB_SUPPORT ++ select GENERIC_PHY ++ select USB_COMMON ++ help ++ Enable this to support the transceiver that is part of Allwinner ++ sunxi SoCs. ++ ++ This driver controls the entire USB PHY block, both the USB OTG ++ parts, as well as the 2 regular USB 2 host PHYs. ++ ++config PHY_SUN9I_USB ++ tristate "Allwinner sun9i SoC USB PHY driver" ++ depends on ARCH_SUNXI && HAS_IOMEM && OF ++ depends on RESET_CONTROLLER ++ depends on USB_SUPPORT ++ select USB_COMMON ++ select GENERIC_PHY ++ help ++ Enable this to support the transceiver that is part of Allwinner ++ sun9i SoCs. ++ ++ This driver controls each individual USB 2 host PHY. +diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile +new file mode 100644 +index 000000000000..8605529c01a1 +--- /dev/null ++++ b/drivers/phy/allwinner/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o ++obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o +diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c +new file mode 100644 +index 000000000000..bbf06cfe5898 +--- /dev/null ++++ b/drivers/phy/allwinner/phy-sun4i-usb.c +@@ -0,0 +1,891 @@ ++/* ++ * Allwinner sun4i USB phy driver ++ * ++ * Copyright (C) 2014-2015 Hans de Goede ++ * ++ * Based on code from ++ * Allwinner Technology Co., Ltd. ++ * ++ * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Author: Sylwester Nawrocki ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define REG_ISCR 0x00 ++#define REG_PHYCTL_A10 0x04 ++#define REG_PHYBIST 0x08 ++#define REG_PHYTUNE 0x0c ++#define REG_PHYCTL_A33 0x10 ++#define REG_PHY_OTGCTL 0x20 ++ ++#define REG_PMU_UNK1 0x10 ++ ++#define PHYCTL_DATA BIT(7) ++ ++#define OTGCTL_ROUTE_MUSB BIT(0) ++ ++#define SUNXI_AHB_ICHR8_EN BIT(10) ++#define SUNXI_AHB_INCR4_BURST_EN BIT(9) ++#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) ++#define SUNXI_ULPI_BYPASS_EN BIT(0) ++ ++/* ISCR, Interface Status and Control bits */ ++#define ISCR_ID_PULLUP_EN (1 << 17) ++#define ISCR_DPDM_PULLUP_EN (1 << 16) ++/* sunxi has the phy id/vbus pins not connected, so we use the force bits */ ++#define ISCR_FORCE_ID_MASK (3 << 14) ++#define ISCR_FORCE_ID_LOW (2 << 14) ++#define ISCR_FORCE_ID_HIGH (3 << 14) ++#define ISCR_FORCE_VBUS_MASK (3 << 12) ++#define ISCR_FORCE_VBUS_LOW (2 << 12) ++#define ISCR_FORCE_VBUS_HIGH (3 << 12) ++ ++/* Common Control Bits for Both PHYs */ ++#define PHY_PLL_BW 0x03 ++#define PHY_RES45_CAL_EN 0x0c ++ ++/* Private Control Bits for Each PHY */ ++#define PHY_TX_AMPLITUDE_TUNE 0x20 ++#define PHY_TX_SLEWRATE_TUNE 0x22 ++#define PHY_VBUSVALID_TH_SEL 0x25 ++#define PHY_PULLUP_RES_SEL 0x27 ++#define PHY_OTG_FUNC_EN 0x28 ++#define PHY_VBUS_DET_EN 0x29 ++#define PHY_DISCON_TH_SEL 0x2a ++#define PHY_SQUELCH_DETECT 0x3c ++ ++#define MAX_PHYS 4 ++ ++/* ++ * Note do not raise the debounce time, we must report Vusb high within 100ms ++ * otherwise we get Vbus errors ++ */ ++#define DEBOUNCE_TIME msecs_to_jiffies(50) ++#define POLL_TIME msecs_to_jiffies(250) ++ ++enum sun4i_usb_phy_type { ++ sun4i_a10_phy, ++ sun6i_a31_phy, ++ sun8i_a33_phy, ++ sun8i_h3_phy, ++ sun8i_v3s_phy, ++ sun50i_a64_phy, ++}; ++ ++struct sun4i_usb_phy_cfg { ++ int num_phys; ++ enum sun4i_usb_phy_type type; ++ u32 disc_thresh; ++ u8 phyctl_offset; ++ bool dedicated_clocks; ++ bool enable_pmu_unk1; ++ bool phy0_dual_route; ++}; ++ ++struct sun4i_usb_phy_data { ++ void __iomem *base; ++ const struct sun4i_usb_phy_cfg *cfg; ++ enum usb_dr_mode dr_mode; ++ spinlock_t reg_lock; /* guard access to phyctl reg */ ++ struct sun4i_usb_phy { ++ struct phy *phy; ++ void __iomem *pmu; ++ struct regulator *vbus; ++ struct reset_control *reset; ++ struct clk *clk; ++ bool regulator_on; ++ int index; ++ } phys[MAX_PHYS]; ++ /* phy0 / otg related variables */ ++ struct extcon_dev *extcon; ++ bool phy0_init; ++ struct gpio_desc *id_det_gpio; ++ struct gpio_desc *vbus_det_gpio; ++ struct power_supply *vbus_power_supply; ++ struct notifier_block vbus_power_nb; ++ bool vbus_power_nb_registered; ++ bool force_session_end; ++ int id_det_irq; ++ int vbus_det_irq; ++ int id_det; ++ int vbus_det; ++ struct delayed_work detect; ++}; ++ ++#define to_sun4i_usb_phy_data(phy) \ ++ container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index]) ++ ++static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); ++ u32 iscr; ++ ++ iscr = readl(data->base + REG_ISCR); ++ iscr &= ~clr; ++ iscr |= set; ++ writel(iscr, data->base + REG_ISCR); ++} ++ ++static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val) ++{ ++ if (val) ++ val = ISCR_FORCE_ID_HIGH; ++ else ++ val = ISCR_FORCE_ID_LOW; ++ ++ sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val); ++} ++ ++static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val) ++{ ++ if (val) ++ val = ISCR_FORCE_VBUS_HIGH; ++ else ++ val = ISCR_FORCE_VBUS_LOW; ++ ++ sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val); ++} ++ ++static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, ++ int len) ++{ ++ struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); ++ u32 temp, usbc_bit = BIT(phy->index * 2); ++ void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&phy_data->reg_lock, flags); ++ ++ if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { ++ /* SoCs newer than A33 need us to set phyctl to 0 explicitly */ ++ writel(0, phyctl); ++ } ++ ++ for (i = 0; i < len; i++) { ++ temp = readl(phyctl); ++ ++ /* clear the address portion */ ++ temp &= ~(0xff << 8); ++ ++ /* set the address */ ++ temp |= ((addr + i) << 8); ++ writel(temp, phyctl); ++ ++ /* set the data bit and clear usbc bit*/ ++ temp = readb(phyctl); ++ if (data & 0x1) ++ temp |= PHYCTL_DATA; ++ else ++ temp &= ~PHYCTL_DATA; ++ temp &= ~usbc_bit; ++ writeb(temp, phyctl); ++ ++ /* pulse usbc_bit */ ++ temp = readb(phyctl); ++ temp |= usbc_bit; ++ writeb(temp, phyctl); ++ ++ temp = readb(phyctl); ++ temp &= ~usbc_bit; ++ writeb(temp, phyctl); ++ ++ data >>= 1; ++ } ++ ++ spin_unlock_irqrestore(&phy_data->reg_lock, flags); ++} ++ ++static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable) ++{ ++ u32 bits, reg_value; ++ ++ if (!phy->pmu) ++ return; ++ ++ bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | ++ SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; ++ ++ reg_value = readl(phy->pmu); ++ ++ if (enable) ++ reg_value |= bits; ++ else ++ reg_value &= ~bits; ++ ++ writel(reg_value, phy->pmu); ++} ++ ++static int sun4i_usb_phy_init(struct phy *_phy) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); ++ int ret; ++ u32 val; ++ ++ ret = clk_prepare_enable(phy->clk); ++ if (ret) ++ return ret; ++ ++ ret = reset_control_deassert(phy->reset); ++ if (ret) { ++ clk_disable_unprepare(phy->clk); ++ return ret; ++ } ++ ++ if (phy->pmu && data->cfg->enable_pmu_unk1) { ++ val = readl(phy->pmu + REG_PMU_UNK1); ++ writel(val & ~2, phy->pmu + REG_PMU_UNK1); ++ } ++ ++ /* Enable USB 45 Ohm resistor calibration */ ++ if (phy->index == 0) ++ sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); ++ ++ /* Adjust PHY's magnitude and rate */ ++ sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); ++ ++ /* Disconnect threshold adjustment */ ++ sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, ++ data->cfg->disc_thresh, 2); ++ ++ sun4i_usb_phy_passby(phy, 1); ++ ++ if (phy->index == 0) { ++ data->phy0_init = true; ++ ++ /* Enable pull-ups */ ++ sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); ++ sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); ++ ++ /* Force ISCR and cable state updates */ ++ data->id_det = -1; ++ data->vbus_det = -1; ++ queue_delayed_work(system_wq, &data->detect, 0); ++ } ++ ++ return 0; ++} ++ ++static int sun4i_usb_phy_exit(struct phy *_phy) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); ++ ++ if (phy->index == 0) { ++ /* Disable pull-ups */ ++ sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0); ++ sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0); ++ data->phy0_init = false; ++ } ++ ++ sun4i_usb_phy_passby(phy, 0); ++ reset_control_assert(phy->reset); ++ clk_disable_unprepare(phy->clk); ++ ++ return 0; ++} ++ ++static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) ++{ ++ switch (data->dr_mode) { ++ case USB_DR_MODE_OTG: ++ if (data->id_det_gpio) ++ return gpiod_get_value_cansleep(data->id_det_gpio); ++ else ++ return 1; /* Fallback to peripheral mode */ ++ case USB_DR_MODE_HOST: ++ return 0; ++ case USB_DR_MODE_PERIPHERAL: ++ default: ++ return 1; ++ } ++} ++ ++static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) ++{ ++ if (data->vbus_det_gpio) ++ return gpiod_get_value_cansleep(data->vbus_det_gpio); ++ ++ if (data->vbus_power_supply) { ++ union power_supply_propval val; ++ int r; ++ ++ r = power_supply_get_property(data->vbus_power_supply, ++ POWER_SUPPLY_PROP_PRESENT, &val); ++ if (r == 0) ++ return val.intval; ++ } ++ ++ /* Fallback: report vbus as high */ ++ return 1; ++} ++ ++static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) ++{ ++ return data->vbus_det_gpio || data->vbus_power_supply; ++} ++ ++static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) ++{ ++ if ((data->id_det_gpio && data->id_det_irq <= 0) || ++ (data->vbus_det_gpio && data->vbus_det_irq <= 0)) ++ return true; ++ ++ /* ++ * The A31 companion pmic (axp221) does not generate vbus change ++ * interrupts when the board is driving vbus, so we must poll ++ * when using the pmic for vbus-det _and_ we're driving vbus. ++ */ ++ if (data->cfg->type == sun6i_a31_phy && ++ data->vbus_power_supply && data->phys[0].regulator_on) ++ return true; ++ ++ return false; ++} ++ ++static int sun4i_usb_phy_power_on(struct phy *_phy) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); ++ int ret; ++ ++ if (!phy->vbus || phy->regulator_on) ++ return 0; ++ ++ /* For phy0 only turn on Vbus if we don't have an ext. Vbus */ ++ if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) && ++ data->vbus_det) { ++ dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n"); ++ return 0; ++ } ++ ++ ret = regulator_enable(phy->vbus); ++ if (ret) ++ return ret; ++ ++ phy->regulator_on = true; ++ ++ /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ ++ if (phy->index == 0 && sun4i_usb_phy0_poll(data)) ++ mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); ++ ++ return 0; ++} ++ ++static int sun4i_usb_phy_power_off(struct phy *_phy) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); ++ ++ if (!phy->vbus || !phy->regulator_on) ++ return 0; ++ ++ regulator_disable(phy->vbus); ++ phy->regulator_on = false; ++ ++ /* ++ * phy0 vbus typically slowly discharges, sometimes this causes the ++ * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. ++ */ ++ if (phy->index == 0 && !sun4i_usb_phy0_poll(data)) ++ mod_delayed_work(system_wq, &data->detect, POLL_TIME); ++ ++ return 0; ++} ++ ++static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); ++ int new_mode; ++ ++ if (phy->index != 0) ++ return -EINVAL; ++ ++ switch (mode) { ++ case PHY_MODE_USB_HOST: ++ new_mode = USB_DR_MODE_HOST; ++ break; ++ case PHY_MODE_USB_DEVICE: ++ new_mode = USB_DR_MODE_PERIPHERAL; ++ break; ++ case PHY_MODE_USB_OTG: ++ new_mode = USB_DR_MODE_OTG; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (new_mode != data->dr_mode) { ++ dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); ++ data->dr_mode = new_mode; ++ } ++ ++ data->id_det = -1; /* Force reprocessing of id */ ++ data->force_session_end = true; ++ queue_delayed_work(system_wq, &data->detect, 0); ++ ++ return 0; ++} ++ ++void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) ++{ ++ struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); ++ ++ sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); ++} ++EXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect); ++ ++static const struct phy_ops sun4i_usb_phy_ops = { ++ .init = sun4i_usb_phy_init, ++ .exit = sun4i_usb_phy_exit, ++ .power_on = sun4i_usb_phy_power_on, ++ .power_off = sun4i_usb_phy_power_off, ++ .set_mode = sun4i_usb_phy_set_mode, ++ .owner = THIS_MODULE, ++}; ++ ++static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, int id_det) ++{ ++ u32 regval; ++ ++ regval = readl(data->base + REG_PHY_OTGCTL); ++ if (id_det == 0) { ++ /* Host mode. Route phy0 to EHCI/OHCI */ ++ regval &= ~OTGCTL_ROUTE_MUSB; ++ } else { ++ /* Peripheral mode. Route phy0 to MUSB */ ++ regval |= OTGCTL_ROUTE_MUSB; ++ } ++ writel(regval, data->base + REG_PHY_OTGCTL); ++} ++ ++static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) ++{ ++ struct sun4i_usb_phy_data *data = ++ container_of(work, struct sun4i_usb_phy_data, detect.work); ++ struct phy *phy0 = data->phys[0].phy; ++ bool force_session_end, id_notify = false, vbus_notify = false; ++ int id_det, vbus_det; ++ ++ if (phy0 == NULL) ++ return; ++ ++ id_det = sun4i_usb_phy0_get_id_det(data); ++ vbus_det = sun4i_usb_phy0_get_vbus_det(data); ++ ++ mutex_lock(&phy0->mutex); ++ ++ if (!data->phy0_init) { ++ mutex_unlock(&phy0->mutex); ++ return; ++ } ++ ++ force_session_end = data->force_session_end; ++ data->force_session_end = false; ++ ++ if (id_det != data->id_det) { ++ /* id-change, force session end if we've no vbus detection */ ++ if (data->dr_mode == USB_DR_MODE_OTG && ++ !sun4i_usb_phy0_have_vbus_det(data)) ++ force_session_end = true; ++ ++ /* When entering host mode (id = 0) force end the session now */ ++ if (force_session_end && id_det == 0) { ++ sun4i_usb_phy0_set_vbus_detect(phy0, 0); ++ msleep(200); ++ sun4i_usb_phy0_set_vbus_detect(phy0, 1); ++ } ++ sun4i_usb_phy0_set_id_detect(phy0, id_det); ++ data->id_det = id_det; ++ id_notify = true; ++ } ++ ++ if (vbus_det != data->vbus_det) { ++ sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det); ++ data->vbus_det = vbus_det; ++ vbus_notify = true; ++ } ++ ++ mutex_unlock(&phy0->mutex); ++ ++ if (id_notify) { ++ extcon_set_state_sync(data->extcon, EXTCON_USB_HOST, ++ !id_det); ++ /* When leaving host mode force end the session here */ ++ if (force_session_end && id_det == 1) { ++ mutex_lock(&phy0->mutex); ++ sun4i_usb_phy0_set_vbus_detect(phy0, 0); ++ msleep(1000); ++ sun4i_usb_phy0_set_vbus_detect(phy0, 1); ++ mutex_unlock(&phy0->mutex); ++ } ++ ++ /* Re-route PHY0 if necessary */ ++ if (data->cfg->phy0_dual_route) ++ sun4i_usb_phy0_reroute(data, id_det); ++ } ++ ++ if (vbus_notify) ++ extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det); ++ ++ if (sun4i_usb_phy0_poll(data)) ++ queue_delayed_work(system_wq, &data->detect, POLL_TIME); ++} ++ ++static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id) ++{ ++ struct sun4i_usb_phy_data *data = dev_id; ++ ++ /* vbus or id changed, let the pins settle and then scan them */ ++ mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); ++ ++ return IRQ_HANDLED; ++} ++ ++static int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb, ++ unsigned long val, void *v) ++{ ++ struct sun4i_usb_phy_data *data = ++ container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb); ++ struct power_supply *psy = v; ++ ++ /* Properties on the vbus_power_supply changed, scan vbus_det */ ++ if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) ++ mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); ++ ++ return NOTIFY_OK; ++} ++ ++static struct phy *sun4i_usb_phy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); ++ ++ if (args->args[0] >= data->cfg->num_phys) ++ return ERR_PTR(-ENODEV); ++ ++ return data->phys[args->args[0]].phy; ++} ++ ++static int sun4i_usb_phy_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); ++ ++ if (data->vbus_power_nb_registered) ++ power_supply_unreg_notifier(&data->vbus_power_nb); ++ if (data->id_det_irq > 0) ++ devm_free_irq(dev, data->id_det_irq, data); ++ if (data->vbus_det_irq > 0) ++ devm_free_irq(dev, data->vbus_det_irq, data); ++ ++ cancel_delayed_work_sync(&data->detect); ++ ++ return 0; ++} ++ ++static const unsigned int sun4i_usb_phy0_cable[] = { ++ EXTCON_USB, ++ EXTCON_USB_HOST, ++ EXTCON_NONE, ++}; ++ ++static int sun4i_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct sun4i_usb_phy_data *data; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ int i, ret; ++ ++ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ spin_lock_init(&data->reg_lock); ++ INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); ++ dev_set_drvdata(dev, data); ++ data->cfg = of_device_get_match_data(dev); ++ if (!data->cfg) ++ return -EINVAL; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); ++ data->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(data->base)) ++ return PTR_ERR(data->base); ++ ++ data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", ++ GPIOD_IN); ++ if (IS_ERR(data->id_det_gpio)) ++ return PTR_ERR(data->id_det_gpio); ++ ++ data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", ++ GPIOD_IN); ++ if (IS_ERR(data->vbus_det_gpio)) ++ return PTR_ERR(data->vbus_det_gpio); ++ ++ if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { ++ data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, ++ "usb0_vbus_power-supply"); ++ if (IS_ERR(data->vbus_power_supply)) ++ return PTR_ERR(data->vbus_power_supply); ++ ++ if (!data->vbus_power_supply) ++ return -EPROBE_DEFER; ++ } ++ ++ data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); ++ ++ data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable); ++ if (IS_ERR(data->extcon)) ++ return PTR_ERR(data->extcon); ++ ++ ret = devm_extcon_dev_register(dev, data->extcon); ++ if (ret) { ++ dev_err(dev, "failed to register extcon: %d\n", ret); ++ return ret; ++ } ++ ++ for (i = 0; i < data->cfg->num_phys; i++) { ++ struct sun4i_usb_phy *phy = data->phys + i; ++ char name[16]; ++ ++ snprintf(name, sizeof(name), "usb%d_vbus", i); ++ phy->vbus = devm_regulator_get_optional(dev, name); ++ if (IS_ERR(phy->vbus)) { ++ if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ phy->vbus = NULL; ++ } ++ ++ if (data->cfg->dedicated_clocks) ++ snprintf(name, sizeof(name), "usb%d_phy", i); ++ else ++ strlcpy(name, "usb_phy", sizeof(name)); ++ ++ phy->clk = devm_clk_get(dev, name); ++ if (IS_ERR(phy->clk)) { ++ dev_err(dev, "failed to get clock %s\n", name); ++ return PTR_ERR(phy->clk); ++ } ++ ++ snprintf(name, sizeof(name), "usb%d_reset", i); ++ phy->reset = devm_reset_control_get(dev, name); ++ if (IS_ERR(phy->reset)) { ++ dev_err(dev, "failed to get reset %s\n", name); ++ return PTR_ERR(phy->reset); ++ } ++ ++ if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ ++ snprintf(name, sizeof(name), "pmu%d", i); ++ res = platform_get_resource_byname(pdev, ++ IORESOURCE_MEM, name); ++ phy->pmu = devm_ioremap_resource(dev, res); ++ if (IS_ERR(phy->pmu)) ++ return PTR_ERR(phy->pmu); ++ } ++ ++ phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); ++ if (IS_ERR(phy->phy)) { ++ dev_err(dev, "failed to create PHY %d\n", i); ++ return PTR_ERR(phy->phy); ++ } ++ ++ phy->index = i; ++ phy_set_drvdata(phy->phy, &data->phys[i]); ++ } ++ ++ data->id_det_irq = gpiod_to_irq(data->id_det_gpio); ++ if (data->id_det_irq > 0) { ++ ret = devm_request_irq(dev, data->id_det_irq, ++ sun4i_usb_phy0_id_vbus_det_irq, ++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ "usb0-id-det", data); ++ if (ret) { ++ dev_err(dev, "Err requesting id-det-irq: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); ++ if (data->vbus_det_irq > 0) { ++ ret = devm_request_irq(dev, data->vbus_det_irq, ++ sun4i_usb_phy0_id_vbus_det_irq, ++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ "usb0-vbus-det", data); ++ if (ret) { ++ dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret); ++ data->vbus_det_irq = -1; ++ sun4i_usb_phy_remove(pdev); /* Stop detect work */ ++ return ret; ++ } ++ } ++ ++ if (data->vbus_power_supply) { ++ data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; ++ data->vbus_power_nb.priority = 0; ++ ret = power_supply_reg_notifier(&data->vbus_power_nb); ++ if (ret) { ++ sun4i_usb_phy_remove(pdev); /* Stop detect work */ ++ return ret; ++ } ++ data->vbus_power_nb_registered = true; ++ } ++ ++ phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); ++ if (IS_ERR(phy_provider)) { ++ sun4i_usb_phy_remove(pdev); /* Stop detect work */ ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { ++ .num_phys = 3, ++ .type = sun4i_a10_phy, ++ .disc_thresh = 3, ++ .phyctl_offset = REG_PHYCTL_A10, ++ .dedicated_clocks = false, ++ .enable_pmu_unk1 = false, ++}; ++ ++static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { ++ .num_phys = 2, ++ .type = sun4i_a10_phy, ++ .disc_thresh = 2, ++ .phyctl_offset = REG_PHYCTL_A10, ++ .dedicated_clocks = false, ++ .enable_pmu_unk1 = false, ++}; ++ ++static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { ++ .num_phys = 3, ++ .type = sun6i_a31_phy, ++ .disc_thresh = 3, ++ .phyctl_offset = REG_PHYCTL_A10, ++ .dedicated_clocks = true, ++ .enable_pmu_unk1 = false, ++}; ++ ++static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { ++ .num_phys = 3, ++ .type = sun4i_a10_phy, ++ .disc_thresh = 2, ++ .phyctl_offset = REG_PHYCTL_A10, ++ .dedicated_clocks = false, ++ .enable_pmu_unk1 = false, ++}; ++ ++static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { ++ .num_phys = 2, ++ .type = sun4i_a10_phy, ++ .disc_thresh = 3, ++ .phyctl_offset = REG_PHYCTL_A10, ++ .dedicated_clocks = true, ++ .enable_pmu_unk1 = false, ++}; ++ ++static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { ++ .num_phys = 2, ++ .type = sun8i_a33_phy, ++ .disc_thresh = 3, ++ .phyctl_offset = REG_PHYCTL_A33, ++ .dedicated_clocks = true, ++ .enable_pmu_unk1 = false, ++}; ++ ++static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { ++ .num_phys = 4, ++ .type = sun8i_h3_phy, ++ .disc_thresh = 3, ++ .phyctl_offset = REG_PHYCTL_A33, ++ .dedicated_clocks = true, ++ .enable_pmu_unk1 = true, ++ .phy0_dual_route = true, ++}; ++ ++static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { ++ .num_phys = 1, ++ .type = sun8i_v3s_phy, ++ .disc_thresh = 3, ++ .phyctl_offset = REG_PHYCTL_A33, ++ .dedicated_clocks = true, ++ .enable_pmu_unk1 = true, ++}; ++ ++static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { ++ .num_phys = 2, ++ .type = sun50i_a64_phy, ++ .disc_thresh = 3, ++ .phyctl_offset = REG_PHYCTL_A33, ++ .dedicated_clocks = true, ++ .enable_pmu_unk1 = true, ++ .phy0_dual_route = true, ++}; ++ ++static const struct of_device_id sun4i_usb_phy_of_match[] = { ++ { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg }, ++ { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg }, ++ { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg }, ++ { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg }, ++ { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg }, ++ { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, ++ { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, ++ { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, ++ { .compatible = "allwinner,sun50i-a64-usb-phy", ++ .data = &sun50i_a64_cfg}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); ++ ++static struct platform_driver sun4i_usb_phy_driver = { ++ .probe = sun4i_usb_phy_probe, ++ .remove = sun4i_usb_phy_remove, ++ .driver = { ++ .of_match_table = sun4i_usb_phy_of_match, ++ .name = "sun4i-usb-phy", ++ } ++}; ++module_platform_driver(sun4i_usb_phy_driver); ++ ++MODULE_DESCRIPTION("Allwinner sun4i USB phy driver"); ++MODULE_AUTHOR("Hans de Goede "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/allwinner/phy-sun9i-usb.c b/drivers/phy/allwinner/phy-sun9i-usb.c +new file mode 100644 +index 000000000000..28fce4bce638 +--- /dev/null ++++ b/drivers/phy/allwinner/phy-sun9i-usb.c +@@ -0,0 +1,202 @@ ++/* ++ * Allwinner sun9i USB phy driver ++ * ++ * Copyright (C) 2014-2015 Chen-Yu Tsai ++ * ++ * Based on phy-sun4i-usb.c from ++ * Hans de Goede ++ * ++ * and code from ++ * Allwinner Technology Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SUNXI_AHB_INCR16_BURST_EN BIT(11) ++#define SUNXI_AHB_INCR8_BURST_EN BIT(10) ++#define SUNXI_AHB_INCR4_BURST_EN BIT(9) ++#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) ++#define SUNXI_ULPI_BYPASS_EN BIT(0) ++ ++/* usb1 HSIC specific bits */ ++#define SUNXI_EHCI_HS_FORCE BIT(20) ++#define SUNXI_HSIC_CONNECT_DET BIT(17) ++#define SUNXI_HSIC_CONNECT_INT BIT(16) ++#define SUNXI_HSIC BIT(1) ++ ++struct sun9i_usb_phy { ++ struct phy *phy; ++ void __iomem *pmu; ++ struct reset_control *reset; ++ struct clk *clk; ++ struct clk *hsic_clk; ++ enum usb_phy_interface type; ++}; ++ ++static void sun9i_usb_phy_passby(struct sun9i_usb_phy *phy, int enable) ++{ ++ u32 bits, reg_value; ++ ++ bits = SUNXI_AHB_INCR16_BURST_EN | SUNXI_AHB_INCR8_BURST_EN | ++ SUNXI_AHB_INCR4_BURST_EN | SUNXI_AHB_INCRX_ALIGN_EN | ++ SUNXI_ULPI_BYPASS_EN; ++ ++ if (phy->type == USBPHY_INTERFACE_MODE_HSIC) ++ bits |= SUNXI_HSIC | SUNXI_EHCI_HS_FORCE | ++ SUNXI_HSIC_CONNECT_DET | SUNXI_HSIC_CONNECT_INT; ++ ++ reg_value = readl(phy->pmu); ++ ++ if (enable) ++ reg_value |= bits; ++ else ++ reg_value &= ~bits; ++ ++ writel(reg_value, phy->pmu); ++} ++ ++static int sun9i_usb_phy_init(struct phy *_phy) ++{ ++ struct sun9i_usb_phy *phy = phy_get_drvdata(_phy); ++ int ret; ++ ++ ret = clk_prepare_enable(phy->clk); ++ if (ret) ++ goto err_clk; ++ ++ ret = clk_prepare_enable(phy->hsic_clk); ++ if (ret) ++ goto err_hsic_clk; ++ ++ ret = reset_control_deassert(phy->reset); ++ if (ret) ++ goto err_reset; ++ ++ sun9i_usb_phy_passby(phy, 1); ++ return 0; ++ ++err_reset: ++ clk_disable_unprepare(phy->hsic_clk); ++ ++err_hsic_clk: ++ clk_disable_unprepare(phy->clk); ++ ++err_clk: ++ return ret; ++} ++ ++static int sun9i_usb_phy_exit(struct phy *_phy) ++{ ++ struct sun9i_usb_phy *phy = phy_get_drvdata(_phy); ++ ++ sun9i_usb_phy_passby(phy, 0); ++ reset_control_assert(phy->reset); ++ clk_disable_unprepare(phy->hsic_clk); ++ clk_disable_unprepare(phy->clk); ++ ++ return 0; ++} ++ ++static const struct phy_ops sun9i_usb_phy_ops = { ++ .init = sun9i_usb_phy_init, ++ .exit = sun9i_usb_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int sun9i_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct sun9i_usb_phy *phy; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ phy->type = of_usb_get_phy_mode(np); ++ if (phy->type == USBPHY_INTERFACE_MODE_HSIC) { ++ phy->clk = devm_clk_get(dev, "hsic_480M"); ++ if (IS_ERR(phy->clk)) { ++ dev_err(dev, "failed to get hsic_480M clock\n"); ++ return PTR_ERR(phy->clk); ++ } ++ ++ phy->hsic_clk = devm_clk_get(dev, "hsic_12M"); ++ if (IS_ERR(phy->hsic_clk)) { ++ dev_err(dev, "failed to get hsic_12M clock\n"); ++ return PTR_ERR(phy->hsic_clk); ++ } ++ ++ phy->reset = devm_reset_control_get(dev, "hsic"); ++ if (IS_ERR(phy->reset)) { ++ dev_err(dev, "failed to get reset control\n"); ++ return PTR_ERR(phy->reset); ++ } ++ } else { ++ phy->clk = devm_clk_get(dev, "phy"); ++ if (IS_ERR(phy->clk)) { ++ dev_err(dev, "failed to get phy clock\n"); ++ return PTR_ERR(phy->clk); ++ } ++ ++ phy->reset = devm_reset_control_get(dev, "phy"); ++ if (IS_ERR(phy->reset)) { ++ dev_err(dev, "failed to get reset control\n"); ++ return PTR_ERR(phy->reset); ++ } ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy->pmu = devm_ioremap_resource(dev, res); ++ if (IS_ERR(phy->pmu)) ++ return PTR_ERR(phy->pmu); ++ ++ phy->phy = devm_phy_create(dev, NULL, &sun9i_usb_phy_ops); ++ if (IS_ERR(phy->phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ return PTR_ERR(phy->phy); ++ } ++ ++ phy_set_drvdata(phy->phy, phy); ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id sun9i_usb_phy_of_match[] = { ++ { .compatible = "allwinner,sun9i-a80-usb-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, sun9i_usb_phy_of_match); ++ ++static struct platform_driver sun9i_usb_phy_driver = { ++ .probe = sun9i_usb_phy_probe, ++ .driver = { ++ .of_match_table = sun9i_usb_phy_of_match, ++ .name = "sun9i-usb-phy", ++ } ++}; ++module_platform_driver(sun9i_usb_phy_driver); ++ ++MODULE_DESCRIPTION("Allwinner sun9i USB phy driver"); ++MODULE_AUTHOR("Chen-Yu Tsai "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig +new file mode 100644 +index 000000000000..edcd5b65179f +--- /dev/null ++++ b/drivers/phy/amlogic/Kconfig +@@ -0,0 +1,14 @@ ++# ++# Phy drivers for Amlogic platforms ++# ++config PHY_MESON8B_USB2 ++ tristate "Meson8b and GXBB USB2 PHY driver" ++ default ARCH_MESON ++ depends on OF && (ARCH_MESON || COMPILE_TEST) ++ depends on USB_SUPPORT ++ select USB_COMMON ++ select GENERIC_PHY ++ help ++ Enable this to support the Meson USB2 PHYs found in Meson8b ++ and GXBB SoCs. ++ If unsure, say N. +diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile +new file mode 100644 +index 000000000000..47b6eecc3864 +--- /dev/null ++++ b/drivers/phy/amlogic/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o +diff --git a/drivers/phy/amlogic/phy-meson8b-usb2.c b/drivers/phy/amlogic/phy-meson8b-usb2.c +new file mode 100644 +index 000000000000..30f56a6a411f +--- /dev/null ++++ b/drivers/phy/amlogic/phy-meson8b-usb2.c +@@ -0,0 +1,286 @@ ++/* ++ * Meson8b and GXBB USB2 PHY driver ++ * ++ * Copyright (C) 2016 Martin Blumenstingl ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define REG_CONFIG 0x00 ++ #define REG_CONFIG_CLK_EN BIT(0) ++ #define REG_CONFIG_CLK_SEL_MASK GENMASK(3, 1) ++ #define REG_CONFIG_CLK_DIV_MASK GENMASK(10, 4) ++ #define REG_CONFIG_CLK_32k_ALTSEL BIT(15) ++ #define REG_CONFIG_TEST_TRIG BIT(31) ++ ++#define REG_CTRL 0x04 ++ #define REG_CTRL_SOFT_PRST BIT(0) ++ #define REG_CTRL_SOFT_HRESET BIT(1) ++ #define REG_CTRL_SS_SCALEDOWN_MODE_MASK GENMASK(3, 2) ++ #define REG_CTRL_CLK_DET_RST BIT(4) ++ #define REG_CTRL_INTR_SEL BIT(5) ++ #define REG_CTRL_CLK_DETECTED BIT(8) ++ #define REG_CTRL_SOF_SENT_RCVD_TGL BIT(9) ++ #define REG_CTRL_SOF_TOGGLE_OUT BIT(10) ++ #define REG_CTRL_POWER_ON_RESET BIT(15) ++ #define REG_CTRL_SLEEPM BIT(16) ++ #define REG_CTRL_TX_BITSTUFF_ENN_H BIT(17) ++ #define REG_CTRL_TX_BITSTUFF_ENN BIT(18) ++ #define REG_CTRL_COMMON_ON BIT(19) ++ #define REG_CTRL_REF_CLK_SEL_MASK GENMASK(21, 20) ++ #define REG_CTRL_REF_CLK_SEL_SHIFT 20 ++ #define REG_CTRL_FSEL_MASK GENMASK(24, 22) ++ #define REG_CTRL_FSEL_SHIFT 22 ++ #define REG_CTRL_PORT_RESET BIT(25) ++ #define REG_CTRL_THREAD_ID_MASK GENMASK(31, 26) ++ ++#define REG_ENDP_INTR 0x08 ++ ++/* bits [31:26], [24:21] and [15:3] seem to be read-only */ ++#define REG_ADP_BC 0x0c ++ #define REG_ADP_BC_VBUS_VLD_EXT_SEL BIT(0) ++ #define REG_ADP_BC_VBUS_VLD_EXT BIT(1) ++ #define REG_ADP_BC_OTG_DISABLE BIT(2) ++ #define REG_ADP_BC_ID_PULLUP BIT(3) ++ #define REG_ADP_BC_DRV_VBUS BIT(4) ++ #define REG_ADP_BC_ADP_PRB_EN BIT(5) ++ #define REG_ADP_BC_ADP_DISCHARGE BIT(6) ++ #define REG_ADP_BC_ADP_CHARGE BIT(7) ++ #define REG_ADP_BC_SESS_END BIT(8) ++ #define REG_ADP_BC_DEVICE_SESS_VLD BIT(9) ++ #define REG_ADP_BC_B_VALID BIT(10) ++ #define REG_ADP_BC_A_VALID BIT(11) ++ #define REG_ADP_BC_ID_DIG BIT(12) ++ #define REG_ADP_BC_VBUS_VALID BIT(13) ++ #define REG_ADP_BC_ADP_PROBE BIT(14) ++ #define REG_ADP_BC_ADP_SENSE BIT(15) ++ #define REG_ADP_BC_ACA_ENABLE BIT(16) ++ #define REG_ADP_BC_DCD_ENABLE BIT(17) ++ #define REG_ADP_BC_VDAT_DET_EN_B BIT(18) ++ #define REG_ADP_BC_VDAT_SRC_EN_B BIT(19) ++ #define REG_ADP_BC_CHARGE_SEL BIT(20) ++ #define REG_ADP_BC_CHARGE_DETECT BIT(21) ++ #define REG_ADP_BC_ACA_PIN_RANGE_C BIT(22) ++ #define REG_ADP_BC_ACA_PIN_RANGE_B BIT(23) ++ #define REG_ADP_BC_ACA_PIN_RANGE_A BIT(24) ++ #define REG_ADP_BC_ACA_PIN_GND BIT(25) ++ #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) ++ ++#define REG_DBG_UART 0x10 ++ ++#define REG_TEST 0x14 ++ #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) ++ #define REG_TEST_EN_MASK GENMASK(7, 4) ++ #define REG_TEST_ADDR_MASK GENMASK(11, 8) ++ #define REG_TEST_DATA_OUT_SEL BIT(12) ++ #define REG_TEST_CLK BIT(13) ++ #define REG_TEST_VA_TEST_EN_B_MASK GENMASK(15, 14) ++ #define REG_TEST_DATA_OUT_MASK GENMASK(19, 16) ++ #define REG_TEST_DISABLE_ID_PULLUP BIT(20) ++ ++#define REG_TUNE 0x18 ++ #define REG_TUNE_TX_RES_TUNE_MASK GENMASK(1, 0) ++ #define REG_TUNE_TX_HSXV_TUNE_MASK GENMASK(3, 2) ++ #define REG_TUNE_TX_VREF_TUNE_MASK GENMASK(7, 4) ++ #define REG_TUNE_TX_RISE_TUNE_MASK GENMASK(9, 8) ++ #define REG_TUNE_TX_PREEMP_PULSE_TUNE BIT(10) ++ #define REG_TUNE_TX_PREEMP_AMP_TUNE_MASK GENMASK(12, 11) ++ #define REG_TUNE_TX_FSLS_TUNE_MASK GENMASK(16, 13) ++ #define REG_TUNE_SQRX_TUNE_MASK GENMASK(19, 17) ++ #define REG_TUNE_OTG_TUNE GENMASK(22, 20) ++ #define REG_TUNE_COMP_DIS_TUNE GENMASK(25, 23) ++ #define REG_TUNE_HOST_DM_PULLDOWN BIT(26) ++ #define REG_TUNE_HOST_DP_PULLDOWN BIT(27) ++ ++#define RESET_COMPLETE_TIME 500 ++#define ACA_ENABLE_COMPLETE_TIME 50 ++ ++struct phy_meson8b_usb2_priv { ++ void __iomem *regs; ++ enum usb_dr_mode dr_mode; ++ struct clk *clk_usb_general; ++ struct clk *clk_usb; ++ struct reset_control *reset; ++}; ++ ++static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, ++ u32 reg) ++{ ++ return readl(phy_priv->regs + reg); ++} ++ ++static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, ++ u32 reg, u32 mask, u32 value) ++{ ++ u32 data; ++ ++ data = phy_meson8b_usb2_read(phy_priv, reg); ++ data &= ~mask; ++ data |= (value & mask); ++ ++ writel(data, phy_priv->regs + reg); ++} ++ ++static int phy_meson8b_usb2_power_on(struct phy *phy) ++{ ++ struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); ++ int ret; ++ ++ if (!IS_ERR_OR_NULL(priv->reset)) { ++ ret = reset_control_reset(priv->reset); ++ if (ret) { ++ dev_err(&phy->dev, "Failed to trigger USB reset\n"); ++ return ret; ++ } ++ } ++ ++ ret = clk_prepare_enable(priv->clk_usb_general); ++ if (ret) { ++ dev_err(&phy->dev, "Failed to enable USB general clock\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(priv->clk_usb); ++ if (ret) { ++ dev_err(&phy->dev, "Failed to enable USB DDR clock\n"); ++ clk_disable_unprepare(priv->clk_usb_general); ++ return ret; ++ } ++ ++ phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, ++ REG_CONFIG_CLK_32k_ALTSEL); ++ ++ phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, ++ 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); ++ ++ phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, ++ 0x5 << REG_CTRL_FSEL_SHIFT); ++ ++ /* reset the PHY */ ++ phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, ++ REG_CTRL_POWER_ON_RESET); ++ udelay(RESET_COMPLETE_TIME); ++ phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); ++ udelay(RESET_COMPLETE_TIME); ++ ++ phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, ++ REG_CTRL_SOF_TOGGLE_OUT); ++ ++ if (priv->dr_mode == USB_DR_MODE_HOST) { ++ phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, ++ REG_ADP_BC_ACA_ENABLE, ++ REG_ADP_BC_ACA_ENABLE); ++ ++ udelay(ACA_ENABLE_COMPLETE_TIME); ++ ++ if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & ++ REG_ADP_BC_ACA_PIN_FLOAT) { ++ dev_warn(&phy->dev, "USB ID detect failed!\n"); ++ clk_disable_unprepare(priv->clk_usb); ++ clk_disable_unprepare(priv->clk_usb_general); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int phy_meson8b_usb2_power_off(struct phy *phy) ++{ ++ struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); ++ ++ clk_disable_unprepare(priv->clk_usb); ++ clk_disable_unprepare(priv->clk_usb_general); ++ ++ return 0; ++} ++ ++static const struct phy_ops phy_meson8b_usb2_ops = { ++ .power_on = phy_meson8b_usb2_power_on, ++ .power_off = phy_meson8b_usb2_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int phy_meson8b_usb2_probe(struct platform_device *pdev) ++{ ++ struct phy_meson8b_usb2_priv *priv; ++ struct resource *res; ++ struct phy *phy; ++ struct phy_provider *phy_provider; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->regs)) ++ return PTR_ERR(priv->regs); ++ ++ priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); ++ if (IS_ERR(priv->clk_usb_general)) ++ return PTR_ERR(priv->clk_usb_general); ++ ++ priv->clk_usb = devm_clk_get(&pdev->dev, "usb"); ++ if (IS_ERR(priv->clk_usb)) ++ return PTR_ERR(priv->clk_usb); ++ ++ priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL); ++ if (PTR_ERR(priv->reset) == -EPROBE_DEFER) ++ return PTR_ERR(priv->reset); ++ ++ priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); ++ if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { ++ dev_err(&pdev->dev, ++ "missing dual role configuration of the controller\n"); ++ return -EINVAL; ++ } ++ ++ phy = devm_phy_create(&pdev->dev, NULL, &phy_meson8b_usb2_ops); ++ if (IS_ERR(phy)) { ++ dev_err(&pdev->dev, "failed to create PHY\n"); ++ return PTR_ERR(phy); ++ } ++ ++ phy_set_drvdata(phy, priv); ++ ++ phy_provider = ++ devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id phy_meson8b_usb2_of_match[] = { ++ { .compatible = "amlogic,meson8b-usb2-phy", }, ++ { .compatible = "amlogic,meson-gxbb-usb2-phy", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); ++ ++static struct platform_driver phy_meson8b_usb2_driver = { ++ .probe = phy_meson8b_usb2_probe, ++ .driver = { ++ .name = "phy-meson-usb2", ++ .of_match_table = phy_meson8b_usb2_of_match, ++ }, ++}; ++module_platform_driver(phy_meson8b_usb2_driver); ++ ++MODULE_AUTHOR("Martin Blumenstingl "); ++MODULE_DESCRIPTION("Meson8b and GXBB USB2 PHY driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig +new file mode 100644 +index 000000000000..d2d99023ec50 +--- /dev/null ++++ b/drivers/phy/broadcom/Kconfig +@@ -0,0 +1,55 @@ ++# ++# Phy drivers for Broadcom platforms ++# ++config PHY_CYGNUS_PCIE ++ tristate "Broadcom Cygnus PCIe PHY driver" ++ depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) ++ select GENERIC_PHY ++ default ARCH_BCM_CYGNUS ++ help ++ Enable this to support the Broadcom Cygnus PCIe PHY. ++ If unsure, say N. ++ ++config BCM_KONA_USB2_PHY ++ tristate "Broadcom Kona USB2 PHY Driver" ++ depends on HAS_IOMEM ++ select GENERIC_PHY ++ help ++ Enable this to support the Broadcom Kona USB 2.0 PHY. ++ ++config PHY_BCM_NS_USB2 ++ tristate "Broadcom Northstar USB 2.0 PHY Driver" ++ depends on ARCH_BCM_IPROC || COMPILE_TEST ++ depends on HAS_IOMEM && OF ++ select GENERIC_PHY ++ help ++ Enable this to support Broadcom USB 2.0 PHY connected to the USB ++ controller on Northstar family. ++ ++config PHY_BCM_NS_USB3 ++ tristate "Broadcom Northstar USB 3.0 PHY Driver" ++ depends on ARCH_BCM_IPROC || COMPILE_TEST ++ depends on HAS_IOMEM && OF ++ select GENERIC_PHY ++ help ++ Enable this to support Broadcom USB 3.0 PHY connected to the USB ++ controller on Northstar family. ++ ++config PHY_NS2_PCIE ++ tristate "Broadcom Northstar2 PCIe PHY driver" ++ depends on OF && MDIO_BUS_MUX_BCM_IPROC ++ select GENERIC_PHY ++ default ARCH_BCM_IPROC ++ help ++ Enable this to support the Broadcom Northstar2 PCIe PHY. ++ If unsure, say N. ++ ++config PHY_BRCM_SATA ++ tristate "Broadcom SATA PHY driver" ++ depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST ++ depends on OF ++ select GENERIC_PHY ++ default ARCH_BCM_IPROC ++ help ++ Enable this to support the Broadcom SATA PHY. ++ If unsure, say N. +diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile +new file mode 100644 +index 000000000000..357a7d16529f +--- /dev/null ++++ b/drivers/phy/broadcom/Makefile +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o ++obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o ++obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o ++obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o ++obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o ++obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o +diff --git a/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c +new file mode 100644 +index 000000000000..0f4ac5d63cff +--- /dev/null ++++ b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c +@@ -0,0 +1,221 @@ ++/* ++ * Copyright (C) 2015 Broadcom Corporation ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation version 2. ++ * ++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PCIE_CFG_OFFSET 0x00 ++#define PCIE1_PHY_IDDQ_SHIFT 10 ++#define PCIE0_PHY_IDDQ_SHIFT 2 ++ ++enum cygnus_pcie_phy_id { ++ CYGNUS_PHY_PCIE0 = 0, ++ CYGNUS_PHY_PCIE1, ++ MAX_NUM_PHYS, ++}; ++ ++struct cygnus_pcie_phy_core; ++ ++/** ++ * struct cygnus_pcie_phy - Cygnus PCIe PHY device ++ * @core: pointer to the Cygnus PCIe PHY core control ++ * @id: internal ID to identify the Cygnus PCIe PHY ++ * @phy: pointer to the kernel PHY device ++ */ ++struct cygnus_pcie_phy { ++ struct cygnus_pcie_phy_core *core; ++ enum cygnus_pcie_phy_id id; ++ struct phy *phy; ++}; ++ ++/** ++ * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control ++ * @dev: pointer to device ++ * @base: base register ++ * @lock: mutex to protect access to individual PHYs ++ * @phys: pointer to Cygnus PHY device ++ */ ++struct cygnus_pcie_phy_core { ++ struct device *dev; ++ void __iomem *base; ++ struct mutex lock; ++ struct cygnus_pcie_phy phys[MAX_NUM_PHYS]; ++}; ++ ++static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable) ++{ ++ struct cygnus_pcie_phy_core *core = phy->core; ++ unsigned shift; ++ u32 val; ++ ++ mutex_lock(&core->lock); ++ ++ switch (phy->id) { ++ case CYGNUS_PHY_PCIE0: ++ shift = PCIE0_PHY_IDDQ_SHIFT; ++ break; ++ ++ case CYGNUS_PHY_PCIE1: ++ shift = PCIE1_PHY_IDDQ_SHIFT; ++ break; ++ ++ default: ++ mutex_unlock(&core->lock); ++ dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id); ++ return -EINVAL; ++ } ++ ++ if (enable) { ++ val = readl(core->base + PCIE_CFG_OFFSET); ++ val &= ~BIT(shift); ++ writel(val, core->base + PCIE_CFG_OFFSET); ++ /* ++ * Wait 50 ms for the PCIe Serdes to stabilize after the analog ++ * front end is brought up ++ */ ++ msleep(50); ++ } else { ++ val = readl(core->base + PCIE_CFG_OFFSET); ++ val |= BIT(shift); ++ writel(val, core->base + PCIE_CFG_OFFSET); ++ } ++ ++ mutex_unlock(&core->lock); ++ dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id, ++ enable ? "enabled" : "disabled"); ++ return 0; ++} ++ ++static int cygnus_pcie_phy_power_on(struct phy *p) ++{ ++ struct cygnus_pcie_phy *phy = phy_get_drvdata(p); ++ ++ return cygnus_pcie_power_config(phy, true); ++} ++ ++static int cygnus_pcie_phy_power_off(struct phy *p) ++{ ++ struct cygnus_pcie_phy *phy = phy_get_drvdata(p); ++ ++ return cygnus_pcie_power_config(phy, false); ++} ++ ++static const struct phy_ops cygnus_pcie_phy_ops = { ++ .power_on = cygnus_pcie_phy_power_on, ++ .power_off = cygnus_pcie_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int cygnus_pcie_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node, *child; ++ struct cygnus_pcie_phy_core *core; ++ struct phy_provider *provider; ++ struct resource *res; ++ unsigned cnt = 0; ++ int ret; ++ ++ if (of_get_child_count(node) == 0) { ++ dev_err(dev, "PHY no child node\n"); ++ return -ENODEV; ++ } ++ ++ core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); ++ if (!core) ++ return -ENOMEM; ++ ++ core->dev = dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ core->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(core->base)) ++ return PTR_ERR(core->base); ++ ++ mutex_init(&core->lock); ++ ++ for_each_available_child_of_node(node, child) { ++ unsigned int id; ++ struct cygnus_pcie_phy *p; ++ ++ if (of_property_read_u32(child, "reg", &id)) { ++ dev_err(dev, "missing reg property for %s\n", ++ child->name); ++ ret = -EINVAL; ++ goto put_child; ++ } ++ ++ if (id >= MAX_NUM_PHYS) { ++ dev_err(dev, "invalid PHY id: %u\n", id); ++ ret = -EINVAL; ++ goto put_child; ++ } ++ ++ if (core->phys[id].phy) { ++ dev_err(dev, "duplicated PHY id: %u\n", id); ++ ret = -EINVAL; ++ goto put_child; ++ } ++ ++ p = &core->phys[id]; ++ p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops); ++ if (IS_ERR(p->phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ ret = PTR_ERR(p->phy); ++ goto put_child; ++ } ++ ++ p->core = core; ++ p->id = id; ++ phy_set_drvdata(p->phy, p); ++ cnt++; ++ } ++ ++ dev_set_drvdata(dev, core); ++ ++ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(provider)) { ++ dev_err(dev, "failed to register PHY provider\n"); ++ return PTR_ERR(provider); ++ } ++ ++ dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt); ++ ++ return 0; ++put_child: ++ of_node_put(child); ++ return ret; ++} ++ ++static const struct of_device_id cygnus_pcie_phy_match_table[] = { ++ { .compatible = "brcm,cygnus-pcie-phy" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table); ++ ++static struct platform_driver cygnus_pcie_phy_driver = { ++ .driver = { ++ .name = "cygnus-pcie-phy", ++ .of_match_table = cygnus_pcie_phy_match_table, ++ }, ++ .probe = cygnus_pcie_phy_probe, ++}; ++module_platform_driver(cygnus_pcie_phy_driver); ++ ++MODULE_AUTHOR("Ray Jui "); ++MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/broadcom/phy-bcm-kona-usb2.c b/drivers/phy/broadcom/phy-bcm-kona-usb2.c +new file mode 100644 +index 000000000000..7b67fe49e30b +--- /dev/null ++++ b/drivers/phy/broadcom/phy-bcm-kona-usb2.c +@@ -0,0 +1,155 @@ ++/* ++ * phy-bcm-kona-usb2.c - Broadcom Kona USB2 Phy Driver ++ * ++ * Copyright (C) 2013 Linaro Limited ++ * Matt Porter ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define OTGCTL (0) ++#define OTGCTL_OTGSTAT2 BIT(31) ++#define OTGCTL_OTGSTAT1 BIT(30) ++#define OTGCTL_PRST_N_SW BIT(11) ++#define OTGCTL_HRESET_N BIT(10) ++#define OTGCTL_UTMI_LINE_STATE1 BIT(9) ++#define OTGCTL_UTMI_LINE_STATE0 BIT(8) ++ ++#define P1CTL (8) ++#define P1CTL_SOFT_RESET BIT(1) ++#define P1CTL_NON_DRIVING BIT(0) ++ ++struct bcm_kona_usb { ++ void __iomem *regs; ++}; ++ ++static void bcm_kona_usb_phy_power(struct bcm_kona_usb *phy, int on) ++{ ++ u32 val; ++ ++ val = readl(phy->regs + OTGCTL); ++ if (on) { ++ /* Configure and power PHY */ ++ val &= ~(OTGCTL_OTGSTAT2 | OTGCTL_OTGSTAT1 | ++ OTGCTL_UTMI_LINE_STATE1 | OTGCTL_UTMI_LINE_STATE0); ++ val |= OTGCTL_PRST_N_SW | OTGCTL_HRESET_N; ++ } else { ++ val &= ~(OTGCTL_PRST_N_SW | OTGCTL_HRESET_N); ++ } ++ writel(val, phy->regs + OTGCTL); ++} ++ ++static int bcm_kona_usb_phy_init(struct phy *gphy) ++{ ++ struct bcm_kona_usb *phy = phy_get_drvdata(gphy); ++ u32 val; ++ ++ /* Soft reset PHY */ ++ val = readl(phy->regs + P1CTL); ++ val &= ~P1CTL_NON_DRIVING; ++ val |= P1CTL_SOFT_RESET; ++ writel(val, phy->regs + P1CTL); ++ writel(val & ~P1CTL_SOFT_RESET, phy->regs + P1CTL); ++ /* Reset needs to be asserted for 2ms */ ++ mdelay(2); ++ writel(val | P1CTL_SOFT_RESET, phy->regs + P1CTL); ++ ++ return 0; ++} ++ ++static int bcm_kona_usb_phy_power_on(struct phy *gphy) ++{ ++ struct bcm_kona_usb *phy = phy_get_drvdata(gphy); ++ ++ bcm_kona_usb_phy_power(phy, 1); ++ ++ return 0; ++} ++ ++static int bcm_kona_usb_phy_power_off(struct phy *gphy) ++{ ++ struct bcm_kona_usb *phy = phy_get_drvdata(gphy); ++ ++ bcm_kona_usb_phy_power(phy, 0); ++ ++ return 0; ++} ++ ++static const struct phy_ops ops = { ++ .init = bcm_kona_usb_phy_init, ++ .power_on = bcm_kona_usb_phy_power_on, ++ .power_off = bcm_kona_usb_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int bcm_kona_usb2_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct bcm_kona_usb *phy; ++ struct resource *res; ++ struct phy *gphy; ++ struct phy_provider *phy_provider; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(phy->regs)) ++ return PTR_ERR(phy->regs); ++ ++ platform_set_drvdata(pdev, phy); ++ ++ gphy = devm_phy_create(dev, NULL, &ops); ++ if (IS_ERR(gphy)) ++ return PTR_ERR(gphy); ++ ++ /* The Kona PHY supports an 8-bit wide UTMI interface */ ++ phy_set_bus_width(gphy, 8); ++ ++ phy_set_drvdata(gphy, phy); ++ ++ phy_provider = devm_of_phy_provider_register(dev, ++ of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id bcm_kona_usb2_dt_ids[] = { ++ { .compatible = "brcm,kona-usb2-phy" }, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(of, bcm_kona_usb2_dt_ids); ++ ++static struct platform_driver bcm_kona_usb2_driver = { ++ .probe = bcm_kona_usb2_probe, ++ .driver = { ++ .name = "bcm-kona-usb2", ++ .of_match_table = bcm_kona_usb2_dt_ids, ++ }, ++}; ++ ++module_platform_driver(bcm_kona_usb2_driver); ++ ++MODULE_ALIAS("platform:bcm-kona-usb2"); ++MODULE_AUTHOR("Matt Porter "); ++MODULE_DESCRIPTION("BCM Kona USB 2.0 PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb2.c b/drivers/phy/broadcom/phy-bcm-ns-usb2.c +new file mode 100644 +index 000000000000..58dff80e9386 +--- /dev/null ++++ b/drivers/phy/broadcom/phy-bcm-ns-usb2.c +@@ -0,0 +1,137 @@ ++/* ++ * Broadcom Northstar USB 2.0 PHY Driver ++ * ++ * Copyright (C) 2016 RafaÅ‚ MiÅ‚ecki ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct bcm_ns_usb2 { ++ struct device *dev; ++ struct clk *ref_clk; ++ struct phy *phy; ++ void __iomem *dmu; ++}; ++ ++static int bcm_ns_usb2_phy_init(struct phy *phy) ++{ ++ struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy); ++ struct device *dev = usb2->dev; ++ void __iomem *dmu = usb2->dmu; ++ u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv; ++ int err = 0; ++ ++ err = clk_prepare_enable(usb2->ref_clk); ++ if (err < 0) { ++ dev_err(dev, "Failed to prepare ref clock: %d\n", err); ++ goto err_out; ++ } ++ ++ ref_clk_rate = clk_get_rate(usb2->ref_clk); ++ if (!ref_clk_rate) { ++ dev_err(dev, "Failed to get ref clock rate\n"); ++ err = -EINVAL; ++ goto err_clk_off; ++ } ++ ++ usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL); ++ ++ if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { ++ usb_pll_pdiv = usb2ctl; ++ usb_pll_pdiv &= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK; ++ usb_pll_pdiv >>= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT; ++ } else { ++ usb_pll_pdiv = 1 << 3; ++ } ++ ++ /* Calculate ndiv based on a solid 1920 MHz that is for USB2 PHY */ ++ usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; ++ ++ /* Unlock DMU PLL settings with some magic value */ ++ writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY); ++ ++ /* Write USB 2.0 PLL control setting */ ++ usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; ++ usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; ++ writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL); ++ ++ /* Lock DMU PLL settings */ ++ writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY); ++ ++err_clk_off: ++ clk_disable_unprepare(usb2->ref_clk); ++err_out: ++ return err; ++} ++ ++static const struct phy_ops ops = { ++ .init = bcm_ns_usb2_phy_init, ++ .owner = THIS_MODULE, ++}; ++ ++static int bcm_ns_usb2_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct bcm_ns_usb2 *usb2; ++ struct resource *res; ++ struct phy_provider *phy_provider; ++ ++ usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL); ++ if (!usb2) ++ return -ENOMEM; ++ usb2->dev = dev; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu"); ++ usb2->dmu = devm_ioremap_resource(dev, res); ++ if (IS_ERR(usb2->dmu)) { ++ dev_err(dev, "Failed to map DMU regs\n"); ++ return PTR_ERR(usb2->dmu); ++ } ++ ++ usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); ++ if (IS_ERR(usb2->ref_clk)) { ++ dev_err(dev, "Clock not defined\n"); ++ return PTR_ERR(usb2->ref_clk); ++ } ++ ++ usb2->phy = devm_phy_create(dev, NULL, &ops); ++ if (IS_ERR(usb2->phy)) ++ return PTR_ERR(usb2->phy); ++ ++ phy_set_drvdata(usb2->phy, usb2); ++ platform_set_drvdata(pdev, usb2); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id bcm_ns_usb2_id_table[] = { ++ { .compatible = "brcm,ns-usb2-phy", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table); ++ ++static struct platform_driver bcm_ns_usb2_driver = { ++ .probe = bcm_ns_usb2_probe, ++ .driver = { ++ .name = "bcm_ns_usb2", ++ .of_match_table = bcm_ns_usb2_id_table, ++ }, ++}; ++module_platform_driver(bcm_ns_usb2_driver); ++ ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c +new file mode 100644 +index 000000000000..22b5e7047fa6 +--- /dev/null ++++ b/drivers/phy/broadcom/phy-bcm-ns-usb3.c +@@ -0,0 +1,303 @@ ++/* ++ * Broadcom Northstar USB 3.0 PHY Driver ++ * ++ * Copyright (C) 2016 RafaÅ‚ MiÅ‚ecki ++ * Copyright (C) 2016 Broadcom ++ * ++ * All magic values used for initialization (and related comments) were obtained ++ * from Broadcom's SDK: ++ * Copyright (c) Broadcom Corp, 2012 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */ ++ ++#define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f ++#define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000 ++#define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040 ++#define BCM_NS_USB3_PHY_PIPE_BLOCK 0x8060 ++ ++/* Registers of PLL30 block */ ++#define BCM_NS_USB3_PLL_CONTROL 0x01 ++#define BCM_NS_USB3_PLLA_CONTROL0 0x0a ++#define BCM_NS_USB3_PLLA_CONTROL1 0x0b ++ ++/* Registers of TX PMD block */ ++#define BCM_NS_USB3_TX_PMD_CONTROL1 0x01 ++ ++/* Registers of PIPE block */ ++#define BCM_NS_USB3_LFPS_CMP 0x02 ++#define BCM_NS_USB3_LFPS_DEGLITCH 0x03 ++ ++enum bcm_ns_family { ++ BCM_NS_UNKNOWN, ++ BCM_NS_AX, ++ BCM_NS_BX, ++}; ++ ++struct bcm_ns_usb3 { ++ struct device *dev; ++ enum bcm_ns_family family; ++ void __iomem *dmp; ++ void __iomem *ccb_mii; ++ struct phy *phy; ++}; ++ ++static const struct of_device_id bcm_ns_usb3_id_table[] = { ++ { ++ .compatible = "brcm,ns-ax-usb3-phy", ++ .data = (int *)BCM_NS_AX, ++ }, ++ { ++ .compatible = "brcm,ns-bx-usb3-phy", ++ .data = (int *)BCM_NS_BX, ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); ++ ++static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr, ++ u32 mask, u32 value, unsigned long timeout) ++{ ++ unsigned long deadline = jiffies + timeout; ++ u32 val; ++ ++ do { ++ val = readl(addr); ++ if ((val & mask) == value) ++ return 0; ++ cpu_relax(); ++ udelay(10); ++ } while (!time_after_eq(jiffies, deadline)); ++ ++ dev_err(usb3->dev, "Timeout waiting for register %p\n", addr); ++ ++ return -EBUSY; ++} ++ ++static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3) ++{ ++ return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL, ++ 0x0100, 0x0000, ++ usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US)); ++} ++ ++static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, ++ u16 value) ++{ ++ u32 tmp = 0; ++ int err; ++ ++ err = bcm_ns_usb3_mii_mng_wait_idle(usb3); ++ if (err < 0) { ++ dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value); ++ return err; ++ } ++ ++ /* TODO: Use a proper MDIO bus layer */ ++ tmp |= 0x58020000; /* Magic value for MDIO PHY write */ ++ tmp |= reg << 18; ++ tmp |= value; ++ writel(tmp, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA); ++ ++ return 0; ++} ++ ++static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3) ++{ ++ int err; ++ ++ /* Enable MDIO. Setting MDCDIV as 26 */ ++ writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); ++ ++ /* Wait for MDIO? */ ++ udelay(2); ++ ++ /* USB3 PLL Block */ ++ err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, ++ BCM_NS_USB3_PHY_PLL30_BLOCK); ++ if (err < 0) ++ return err; ++ ++ /* Assert Ana_Pllseq start */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x1000); ++ ++ /* Assert CML Divider ratio to 26 */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); ++ ++ /* Asserting PLL Reset */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0xc000); ++ ++ /* Deaaserting PLL Reset */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0x8000); ++ ++ /* Waiting MII Mgt interface idle */ ++ bcm_ns_usb3_mii_mng_wait_idle(usb3); ++ ++ /* Deasserting USB3 system reset */ ++ writel(0, usb3->dmp + BCMA_RESET_CTL); ++ ++ /* PLL frequency monitor enable */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x9000); ++ ++ /* PIPE Block */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, ++ BCM_NS_USB3_PHY_PIPE_BLOCK); ++ ++ /* CMPMAX & CMPMINTH setting */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, 0xf30d); ++ ++ /* DEGLITCH MIN & MAX setting */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, 0x6302); ++ ++ /* TXPMD block */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, ++ BCM_NS_USB3_PHY_TX_PMD_BLOCK); ++ ++ /* Enabling SSC */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); ++ ++ /* Waiting MII Mgt interface idle */ ++ bcm_ns_usb3_mii_mng_wait_idle(usb3); ++ ++ return 0; ++} ++ ++static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3) ++{ ++ int err; ++ ++ /* Enable MDIO. Setting MDCDIV as 26 */ ++ writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); ++ ++ /* Wait for MDIO? */ ++ udelay(2); ++ ++ /* PLL30 block */ ++ err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, ++ BCM_NS_USB3_PHY_PLL30_BLOCK); ++ if (err < 0) ++ return err; ++ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); ++ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 0x80e0); ++ ++ bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x009c); ++ ++ /* Enable SSC */ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, ++ BCM_NS_USB3_PHY_TX_PMD_BLOCK); ++ ++ bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x21d3); ++ ++ bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); ++ ++ /* Waiting MII Mgt interface idle */ ++ bcm_ns_usb3_mii_mng_wait_idle(usb3); ++ ++ /* Deasserting USB3 system reset */ ++ writel(0, usb3->dmp + BCMA_RESET_CTL); ++ ++ return 0; ++} ++ ++static int bcm_ns_usb3_phy_init(struct phy *phy) ++{ ++ struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy); ++ int err; ++ ++ /* Perform USB3 system soft reset */ ++ writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL); ++ ++ switch (usb3->family) { ++ case BCM_NS_AX: ++ err = bcm_ns_usb3_phy_init_ns_ax(usb3); ++ break; ++ case BCM_NS_BX: ++ err = bcm_ns_usb3_phy_init_ns_bx(usb3); ++ break; ++ default: ++ WARN_ON(1); ++ err = -ENOTSUPP; ++ } ++ ++ return err; ++} ++ ++static const struct phy_ops ops = { ++ .init = bcm_ns_usb3_phy_init, ++ .owner = THIS_MODULE, ++}; ++ ++static int bcm_ns_usb3_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *of_id; ++ struct bcm_ns_usb3 *usb3; ++ struct resource *res; ++ struct phy_provider *phy_provider; ++ ++ usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL); ++ if (!usb3) ++ return -ENOMEM; ++ ++ usb3->dev = dev; ++ ++ of_id = of_match_device(bcm_ns_usb3_id_table, dev); ++ if (!of_id) ++ return -EINVAL; ++ usb3->family = (enum bcm_ns_family)of_id->data; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp"); ++ usb3->dmp = devm_ioremap_resource(dev, res); ++ if (IS_ERR(usb3->dmp)) { ++ dev_err(dev, "Failed to map DMP regs\n"); ++ return PTR_ERR(usb3->dmp); ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii"); ++ usb3->ccb_mii = devm_ioremap_resource(dev, res); ++ if (IS_ERR(usb3->ccb_mii)) { ++ dev_err(dev, "Failed to map ChipCommon B MII regs\n"); ++ return PTR_ERR(usb3->ccb_mii); ++ } ++ ++ usb3->phy = devm_phy_create(dev, NULL, &ops); ++ if (IS_ERR(usb3->phy)) { ++ dev_err(dev, "Failed to create PHY\n"); ++ return PTR_ERR(usb3->phy); ++ } ++ ++ phy_set_drvdata(usb3->phy, usb3); ++ platform_set_drvdata(pdev, usb3); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (!IS_ERR(phy_provider)) ++ dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n"); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static struct platform_driver bcm_ns_usb3_driver = { ++ .probe = bcm_ns_usb3_probe, ++ .driver = { ++ .name = "bcm_ns_usb3", ++ .of_match_table = bcm_ns_usb3_id_table, ++ }, ++}; ++module_platform_driver(bcm_ns_usb3_driver); ++ ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/broadcom/phy-bcm-ns2-pcie.c b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c +new file mode 100644 +index 000000000000..4c7d11d2b378 +--- /dev/null ++++ b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c +@@ -0,0 +1,101 @@ ++/* ++ * Copyright (C) 2016 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation version 2. ++ * ++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BLK_ADDR_REG_OFFSET 0x1f ++#define PLL_AFE1_100MHZ_BLK 0x2100 ++#define PLL_CLK_AMP_OFFSET 0x03 ++#define PLL_CLK_AMP_2P05V 0x2b18 ++ ++static int ns2_pci_phy_init(struct phy *p) ++{ ++ struct mdio_device *mdiodev = phy_get_drvdata(p); ++ int rc; ++ ++ /* select the AFE 100MHz block page */ ++ rc = mdiobus_write(mdiodev->bus, mdiodev->addr, ++ BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK); ++ if (rc) ++ goto err; ++ ++ /* set the 100 MHz reference clock amplitude to 2.05 v */ ++ rc = mdiobus_write(mdiodev->bus, mdiodev->addr, ++ PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V); ++ if (rc) ++ goto err; ++ ++ return 0; ++ ++err: ++ dev_err(&mdiodev->dev, "Error %d writing to phy\n", rc); ++ return rc; ++} ++ ++static const struct phy_ops ns2_pci_phy_ops = { ++ .init = ns2_pci_phy_init, ++ .owner = THIS_MODULE, ++}; ++ ++static int ns2_pci_phy_probe(struct mdio_device *mdiodev) ++{ ++ struct device *dev = &mdiodev->dev; ++ struct phy_provider *provider; ++ struct phy *phy; ++ ++ phy = devm_phy_create(dev, dev->of_node, &ns2_pci_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create Phy\n"); ++ return PTR_ERR(phy); ++ } ++ ++ phy_set_drvdata(phy, mdiodev); ++ ++ provider = devm_of_phy_provider_register(&phy->dev, ++ of_phy_simple_xlate); ++ if (IS_ERR(provider)) { ++ dev_err(dev, "failed to register Phy provider\n"); ++ return PTR_ERR(provider); ++ } ++ ++ dev_info(dev, "%s PHY registered\n", dev_name(dev)); ++ ++ return 0; ++} ++ ++static const struct of_device_id ns2_pci_phy_of_match[] = { ++ { .compatible = "brcm,ns2-pcie-phy", }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, ns2_pci_phy_of_match); ++ ++static struct mdio_driver ns2_pci_phy_driver = { ++ .mdiodrv = { ++ .driver = { ++ .name = "phy-bcm-ns2-pci", ++ .of_match_table = ns2_pci_phy_of_match, ++ }, ++ }, ++ .probe = ns2_pci_phy_probe, ++}; ++mdio_module_driver(ns2_pci_phy_driver); ++ ++MODULE_AUTHOR("Broadcom"); ++MODULE_DESCRIPTION("Broadcom Northstar2 PCI Phy driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:phy-bcm-ns2-pci"); +diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c +new file mode 100644 +index 000000000000..ccbc3d994998 +--- /dev/null ++++ b/drivers/phy/broadcom/phy-brcm-sata.c +@@ -0,0 +1,493 @@ ++/* ++ * Broadcom SATA3 AHCI Controller PHY Driver ++ * ++ * Copyright (C) 2016 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SATA_PCB_BANK_OFFSET 0x23c ++#define SATA_PCB_REG_OFFSET(ofs) ((ofs) * 4) ++ ++#define MAX_PORTS 2 ++ ++/* Register offset between PHYs in PCB space */ ++#define SATA_PCB_REG_28NM_SPACE_SIZE 0x1000 ++ ++/* The older SATA PHY registers duplicated per port registers within the map, ++ * rather than having a separate map per port. ++ */ ++#define SATA_PCB_REG_40NM_SPACE_SIZE 0x10 ++ ++/* Register offset between PHYs in PHY control space */ ++#define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8 ++ ++enum brcm_sata_phy_version { ++ BRCM_SATA_PHY_STB_28NM, ++ BRCM_SATA_PHY_STB_40NM, ++ BRCM_SATA_PHY_IPROC_NS2, ++ BRCM_SATA_PHY_IPROC_NSP, ++}; ++ ++struct brcm_sata_port { ++ int portnum; ++ struct phy *phy; ++ struct brcm_sata_phy *phy_priv; ++ bool ssc_en; ++}; ++ ++struct brcm_sata_phy { ++ struct device *dev; ++ void __iomem *phy_base; ++ void __iomem *ctrl_base; ++ enum brcm_sata_phy_version version; ++ ++ struct brcm_sata_port phys[MAX_PORTS]; ++}; ++ ++enum sata_phy_regs { ++ BLOCK0_REG_BANK = 0x000, ++ BLOCK0_XGXSSTATUS = 0x81, ++ BLOCK0_XGXSSTATUS_PLL_LOCK = BIT(12), ++ BLOCK0_SPARE = 0x8d, ++ BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3, ++ BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1, ++ ++ PLL_REG_BANK_0 = 0x050, ++ PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, ++ PLLCONTROL_0_FREQ_DET_RESTART = BIT(13), ++ PLLCONTROL_0_FREQ_MONITOR = BIT(12), ++ PLLCONTROL_0_SEQ_START = BIT(15), ++ PLL_CAP_CONTROL = 0x85, ++ PLL_ACTRL2 = 0x8b, ++ PLL_ACTRL2_SELDIV_MASK = 0x1f, ++ PLL_ACTRL2_SELDIV_SHIFT = 9, ++ ++ PLL1_REG_BANK = 0x060, ++ PLL1_ACTRL2 = 0x82, ++ PLL1_ACTRL3 = 0x83, ++ PLL1_ACTRL4 = 0x84, ++ ++ OOB_REG_BANK = 0x150, ++ OOB1_REG_BANK = 0x160, ++ OOB_CTRL1 = 0x80, ++ OOB_CTRL1_BURST_MAX_MASK = 0xf, ++ OOB_CTRL1_BURST_MAX_SHIFT = 12, ++ OOB_CTRL1_BURST_MIN_MASK = 0xf, ++ OOB_CTRL1_BURST_MIN_SHIFT = 8, ++ OOB_CTRL1_WAKE_IDLE_MAX_MASK = 0xf, ++ OOB_CTRL1_WAKE_IDLE_MAX_SHIFT = 4, ++ OOB_CTRL1_WAKE_IDLE_MIN_MASK = 0xf, ++ OOB_CTRL1_WAKE_IDLE_MIN_SHIFT = 0, ++ OOB_CTRL2 = 0x81, ++ OOB_CTRL2_SEL_ENA_SHIFT = 15, ++ OOB_CTRL2_SEL_ENA_RC_SHIFT = 14, ++ OOB_CTRL2_RESET_IDLE_MAX_MASK = 0x3f, ++ OOB_CTRL2_RESET_IDLE_MAX_SHIFT = 8, ++ OOB_CTRL2_BURST_CNT_MASK = 0x3, ++ OOB_CTRL2_BURST_CNT_SHIFT = 6, ++ OOB_CTRL2_RESET_IDLE_MIN_MASK = 0x3f, ++ OOB_CTRL2_RESET_IDLE_MIN_SHIFT = 0, ++ ++ TXPMD_REG_BANK = 0x1a0, ++ TXPMD_CONTROL1 = 0x81, ++ TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), ++ TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), ++ TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, ++ TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, ++ TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, ++ TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, ++ TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, ++}; ++ ++enum sata_phy_ctrl_regs { ++ PHY_CTRL_1 = 0x0, ++ PHY_CTRL_1_RESET = BIT(0), ++}; ++ ++static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port) ++{ ++ struct brcm_sata_phy *priv = port->phy_priv; ++ u32 size = 0; ++ ++ switch (priv->version) { ++ case BRCM_SATA_PHY_STB_28NM: ++ case BRCM_SATA_PHY_IPROC_NS2: ++ size = SATA_PCB_REG_28NM_SPACE_SIZE; ++ break; ++ case BRCM_SATA_PHY_STB_40NM: ++ size = SATA_PCB_REG_40NM_SPACE_SIZE; ++ break; ++ default: ++ dev_err(priv->dev, "invalid phy version\n"); ++ break; ++ } ++ ++ return priv->phy_base + (port->portnum * size); ++} ++ ++static inline void __iomem *brcm_sata_ctrl_base(struct brcm_sata_port *port) ++{ ++ struct brcm_sata_phy *priv = port->phy_priv; ++ u32 size = 0; ++ ++ switch (priv->version) { ++ case BRCM_SATA_PHY_IPROC_NS2: ++ size = SATA_PHY_CTRL_REG_28NM_SPACE_SIZE; ++ break; ++ default: ++ dev_err(priv->dev, "invalid phy version\n"); ++ break; ++ } ++ ++ return priv->ctrl_base + (port->portnum * size); ++} ++ ++static void brcm_sata_phy_wr(void __iomem *pcb_base, u32 bank, ++ u32 ofs, u32 msk, u32 value) ++{ ++ u32 tmp; ++ ++ writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); ++ tmp = readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); ++ tmp = (tmp & msk) | value; ++ writel(tmp, pcb_base + SATA_PCB_REG_OFFSET(ofs)); ++} ++ ++static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs) ++{ ++ writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); ++ return readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); ++} ++ ++/* These defaults were characterized by H/W group */ ++#define STB_FMIN_VAL_DEFAULT 0x3df ++#define STB_FMAX_VAL_DEFAULT 0x3df ++#define STB_FMAX_VAL_SSC 0x83 ++ ++static int brcm_stb_sata_init(struct brcm_sata_port *port) ++{ ++ void __iomem *base = brcm_sata_pcb_base(port); ++ struct brcm_sata_phy *priv = port->phy_priv; ++ u32 tmp; ++ ++ /* override the TX spread spectrum setting */ ++ tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; ++ brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); ++ ++ /* set fixed min freq */ ++ brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, ++ ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, ++ STB_FMIN_VAL_DEFAULT); ++ ++ /* set fixed max freq depending on SSC config */ ++ if (port->ssc_en) { ++ dev_info(priv->dev, "enabling SSC on port%d\n", port->portnum); ++ tmp = STB_FMAX_VAL_SSC; ++ } else { ++ tmp = STB_FMAX_VAL_DEFAULT; ++ } ++ ++ brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, ++ ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); ++ ++ return 0; ++} ++ ++/* NS2 SATA PLL1 defaults were characterized by H/W group */ ++#define NS2_PLL1_ACTRL2_MAGIC 0x1df8 ++#define NS2_PLL1_ACTRL3_MAGIC 0x2b00 ++#define NS2_PLL1_ACTRL4_MAGIC 0x8824 ++ ++static int brcm_ns2_sata_init(struct brcm_sata_port *port) ++{ ++ int try; ++ unsigned int val; ++ void __iomem *base = brcm_sata_pcb_base(port); ++ void __iomem *ctrl_base = brcm_sata_ctrl_base(port); ++ struct device *dev = port->phy_priv->dev; ++ ++ /* Configure OOB control */ ++ val = 0x0; ++ val |= (0xc << OOB_CTRL1_BURST_MAX_SHIFT); ++ val |= (0x4 << OOB_CTRL1_BURST_MIN_SHIFT); ++ val |= (0x9 << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); ++ val |= (0x3 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); ++ brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL1, 0x0, val); ++ val = 0x0; ++ val |= (0x1b << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); ++ val |= (0x2 << OOB_CTRL2_BURST_CNT_SHIFT); ++ val |= (0x9 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); ++ brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL2, 0x0, val); ++ ++ /* Configure PHY PLL register bank 1 */ ++ val = NS2_PLL1_ACTRL2_MAGIC; ++ brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL2, 0x0, val); ++ val = NS2_PLL1_ACTRL3_MAGIC; ++ brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL3, 0x0, val); ++ val = NS2_PLL1_ACTRL4_MAGIC; ++ brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL4, 0x0, val); ++ ++ /* Configure PHY BLOCK0 register bank */ ++ /* Set oob_clk_sel to refclk/2 */ ++ brcm_sata_phy_wr(base, BLOCK0_REG_BANK, BLOCK0_SPARE, ++ ~BLOCK0_SPARE_OOB_CLK_SEL_MASK, ++ BLOCK0_SPARE_OOB_CLK_SEL_REFBY2); ++ ++ /* Strobe PHY reset using PHY control register */ ++ writel(PHY_CTRL_1_RESET, ctrl_base + PHY_CTRL_1); ++ mdelay(1); ++ writel(0x0, ctrl_base + PHY_CTRL_1); ++ mdelay(1); ++ ++ /* Wait for PHY PLL lock by polling pll_lock bit */ ++ try = 50; ++ while (try) { ++ val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, ++ BLOCK0_XGXSSTATUS); ++ if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) ++ break; ++ msleep(20); ++ try--; ++ } ++ if (!try) { ++ /* PLL did not lock; give up */ ++ dev_err(dev, "port%d PLL did not lock\n", port->portnum); ++ return -ETIMEDOUT; ++ } ++ ++ dev_dbg(dev, "port%d initialized\n", port->portnum); ++ ++ return 0; ++} ++ ++static int brcm_nsp_sata_init(struct brcm_sata_port *port) ++{ ++ struct brcm_sata_phy *priv = port->phy_priv; ++ struct device *dev = port->phy_priv->dev; ++ void __iomem *base = priv->phy_base; ++ unsigned int oob_bank; ++ unsigned int val, try; ++ ++ /* Configure OOB control */ ++ if (port->portnum == 0) ++ oob_bank = OOB_REG_BANK; ++ else if (port->portnum == 1) ++ oob_bank = OOB1_REG_BANK; ++ else ++ return -EINVAL; ++ ++ val = 0x0; ++ val |= (0x0f << OOB_CTRL1_BURST_MAX_SHIFT); ++ val |= (0x06 << OOB_CTRL1_BURST_MIN_SHIFT); ++ val |= (0x0f << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); ++ val |= (0x06 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); ++ brcm_sata_phy_wr(base, oob_bank, OOB_CTRL1, 0x0, val); ++ ++ val = 0x0; ++ val |= (0x2e << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); ++ val |= (0x02 << OOB_CTRL2_BURST_CNT_SHIFT); ++ val |= (0x16 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); ++ brcm_sata_phy_wr(base, oob_bank, OOB_CTRL2, 0x0, val); ++ ++ ++ brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_ACTRL2, ++ ~(PLL_ACTRL2_SELDIV_MASK << PLL_ACTRL2_SELDIV_SHIFT), ++ 0x0c << PLL_ACTRL2_SELDIV_SHIFT); ++ ++ brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_CAP_CONTROL, ++ 0xff0, 0x4f0); ++ ++ val = PLLCONTROL_0_FREQ_DET_RESTART | PLLCONTROL_0_FREQ_MONITOR; ++ brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, ++ ~val, val); ++ val = PLLCONTROL_0_SEQ_START; ++ brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, ++ ~val, 0); ++ mdelay(10); ++ brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, ++ ~val, val); ++ ++ /* Wait for pll_seq_done bit */ ++ try = 50; ++ while (try--) { ++ val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, ++ BLOCK0_XGXSSTATUS); ++ if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) ++ break; ++ msleep(20); ++ } ++ if (!try) { ++ /* PLL did not lock; give up */ ++ dev_err(dev, "port%d PLL did not lock\n", port->portnum); ++ return -ETIMEDOUT; ++ } ++ ++ dev_dbg(dev, "port%d initialized\n", port->portnum); ++ ++ return 0; ++} ++ ++static int brcm_sata_phy_init(struct phy *phy) ++{ ++ int rc; ++ struct brcm_sata_port *port = phy_get_drvdata(phy); ++ ++ switch (port->phy_priv->version) { ++ case BRCM_SATA_PHY_STB_28NM: ++ case BRCM_SATA_PHY_STB_40NM: ++ rc = brcm_stb_sata_init(port); ++ break; ++ case BRCM_SATA_PHY_IPROC_NS2: ++ rc = brcm_ns2_sata_init(port); ++ break; ++ case BRCM_SATA_PHY_IPROC_NSP: ++ rc = brcm_nsp_sata_init(port); ++ break; ++ default: ++ rc = -ENODEV; ++ } ++ ++ return rc; ++} ++ ++static const struct phy_ops phy_ops = { ++ .init = brcm_sata_phy_init, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct of_device_id brcm_sata_phy_of_match[] = { ++ { .compatible = "brcm,bcm7445-sata-phy", ++ .data = (void *)BRCM_SATA_PHY_STB_28NM }, ++ { .compatible = "brcm,bcm7425-sata-phy", ++ .data = (void *)BRCM_SATA_PHY_STB_40NM }, ++ { .compatible = "brcm,iproc-ns2-sata-phy", ++ .data = (void *)BRCM_SATA_PHY_IPROC_NS2 }, ++ { .compatible = "brcm,iproc-nsp-sata-phy", ++ .data = (void *)BRCM_SATA_PHY_IPROC_NSP }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); ++ ++static int brcm_sata_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *dn = dev->of_node, *child; ++ const struct of_device_id *of_id; ++ struct brcm_sata_phy *priv; ++ struct resource *res; ++ struct phy_provider *provider; ++ int ret, count = 0; ++ ++ if (of_get_child_count(dn) == 0) ++ return -ENODEV; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ dev_set_drvdata(dev, priv); ++ priv->dev = dev; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); ++ priv->phy_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->phy_base)) ++ return PTR_ERR(priv->phy_base); ++ ++ of_id = of_match_node(brcm_sata_phy_of_match, dn); ++ if (of_id) ++ priv->version = (enum brcm_sata_phy_version)of_id->data; ++ else ++ priv->version = BRCM_SATA_PHY_STB_28NM; ++ ++ if (priv->version == BRCM_SATA_PHY_IPROC_NS2) { ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "phy-ctrl"); ++ priv->ctrl_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(priv->ctrl_base)) ++ return PTR_ERR(priv->ctrl_base); ++ } ++ ++ for_each_available_child_of_node(dn, child) { ++ unsigned int id; ++ struct brcm_sata_port *port; ++ ++ if (of_property_read_u32(child, "reg", &id)) { ++ dev_err(dev, "missing reg property in node %s\n", ++ child->name); ++ ret = -EINVAL; ++ goto put_child; ++ } ++ ++ if (id >= MAX_PORTS) { ++ dev_err(dev, "invalid reg: %u\n", id); ++ ret = -EINVAL; ++ goto put_child; ++ } ++ if (priv->phys[id].phy) { ++ dev_err(dev, "already registered port %u\n", id); ++ ret = -EINVAL; ++ goto put_child; ++ } ++ ++ port = &priv->phys[id]; ++ port->portnum = id; ++ port->phy_priv = priv; ++ port->phy = devm_phy_create(dev, child, &phy_ops); ++ port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); ++ if (IS_ERR(port->phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ ret = PTR_ERR(port->phy); ++ goto put_child; ++ } ++ ++ phy_set_drvdata(port->phy, port); ++ count++; ++ } ++ ++ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(provider)) { ++ dev_err(dev, "could not register PHY provider\n"); ++ return PTR_ERR(provider); ++ } ++ ++ dev_info(dev, "registered %d port(s)\n", count); ++ ++ return 0; ++put_child: ++ of_node_put(child); ++ return ret; ++} ++ ++static struct platform_driver brcm_sata_phy_driver = { ++ .probe = brcm_sata_phy_probe, ++ .driver = { ++ .of_match_table = brcm_sata_phy_of_match, ++ .name = "brcm-sata-phy", ++ } ++}; ++module_platform_driver(brcm_sata_phy_driver); ++ ++MODULE_DESCRIPTION("Broadcom SATA PHY driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Marc Carino"); ++MODULE_AUTHOR("Brian Norris"); ++MODULE_ALIAS("platform:phy-brcm-sata"); +diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig +new file mode 100644 +index 000000000000..6164c4cd0f65 +--- /dev/null ++++ b/drivers/phy/hisilicon/Kconfig +@@ -0,0 +1,20 @@ ++# ++# Phy drivers for Hisilicon platforms ++# ++config PHY_HI6220_USB ++ tristate "hi6220 USB PHY support" ++ depends on (ARCH_HISI && ARM64) || COMPILE_TEST ++ select GENERIC_PHY ++ select MFD_SYSCON ++ help ++ Enable this to support the HISILICON HI6220 USB PHY. ++ ++ To compile this driver as a module, choose M here. ++ ++config PHY_HIX5HD2_SATA ++ tristate "HIX5HD2 SATA PHY Driver" ++ depends on ARCH_HIX5HD2 && OF && HAS_IOMEM ++ select GENERIC_PHY ++ select MFD_SYSCON ++ help ++ Support for SATA PHY on Hisilicon hix5hd2 Soc. +diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile +new file mode 100644 +index 000000000000..541b348187a8 +--- /dev/null ++++ b/drivers/phy/hisilicon/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o ++obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o +diff --git a/drivers/phy/hisilicon/phy-hi6220-usb.c b/drivers/phy/hisilicon/phy-hi6220-usb.c +new file mode 100644 +index 000000000000..398c1021deec +--- /dev/null ++++ b/drivers/phy/hisilicon/phy-hi6220-usb.c +@@ -0,0 +1,168 @@ ++/* ++ * Copyright (c) 2015 Linaro Ltd. ++ * Copyright (c) 2015 Hisilicon Limited. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define SC_PERIPH_CTRL4 0x00c ++ ++#define CTRL4_PICO_SIDDQ BIT(6) ++#define CTRL4_PICO_OGDISABLE BIT(8) ++#define CTRL4_PICO_VBUSVLDEXT BIT(10) ++#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11) ++#define CTRL4_OTG_PHY_SEL BIT(21) ++ ++#define SC_PERIPH_CTRL5 0x010 ++ ++#define CTRL5_USBOTG_RES_SEL BIT(3) ++#define CTRL5_PICOPHY_ACAENB BIT(4) ++#define CTRL5_PICOPHY_BC_MODE BIT(5) ++#define CTRL5_PICOPHY_CHRGSEL BIT(6) ++#define CTRL5_PICOPHY_VDATSRCEND BIT(7) ++#define CTRL5_PICOPHY_VDATDETENB BIT(8) ++#define CTRL5_PICOPHY_DCDENB BIT(9) ++#define CTRL5_PICOPHY_IDDIG BIT(10) ++ ++#define SC_PERIPH_CTRL8 0x018 ++#define SC_PERIPH_RSTEN0 0x300 ++#define SC_PERIPH_RSTDIS0 0x304 ++ ++#define RST0_USBOTG_BUS BIT(4) ++#define RST0_POR_PICOPHY BIT(5) ++#define RST0_USBOTG BIT(6) ++#define RST0_USBOTG_32K BIT(7) ++ ++#define EYE_PATTERN_PARA 0x7053348c ++ ++struct hi6220_priv { ++ struct regmap *reg; ++ struct device *dev; ++}; ++ ++static void hi6220_phy_init(struct hi6220_priv *priv) ++{ ++ struct regmap *reg = priv->reg; ++ u32 val, mask; ++ ++ val = RST0_USBOTG_BUS | RST0_POR_PICOPHY | ++ RST0_USBOTG | RST0_USBOTG_32K; ++ mask = val; ++ regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val); ++ regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val); ++} ++ ++static int hi6220_phy_setup(struct hi6220_priv *priv, bool on) ++{ ++ struct regmap *reg = priv->reg; ++ u32 val, mask; ++ int ret; ++ ++ if (on) { ++ val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB; ++ mask = val | CTRL5_PICOPHY_BC_MODE; ++ ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val); ++ if (ret) ++ goto out; ++ ++ val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL | ++ CTRL4_OTG_PHY_SEL; ++ mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE; ++ ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); ++ if (ret) ++ goto out; ++ ++ ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA); ++ if (ret) ++ goto out; ++ } else { ++ val = CTRL4_PICO_SIDDQ; ++ mask = val; ++ ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); ++ if (ret) ++ goto out; ++ } ++ ++ return 0; ++out: ++ dev_err(priv->dev, "failed to setup phy ret: %d\n", ret); ++ return ret; ++} ++ ++static int hi6220_phy_start(struct phy *phy) ++{ ++ struct hi6220_priv *priv = phy_get_drvdata(phy); ++ ++ return hi6220_phy_setup(priv, true); ++} ++ ++static int hi6220_phy_exit(struct phy *phy) ++{ ++ struct hi6220_priv *priv = phy_get_drvdata(phy); ++ ++ return hi6220_phy_setup(priv, false); ++} ++ ++static const struct phy_ops hi6220_phy_ops = { ++ .init = hi6220_phy_start, ++ .exit = hi6220_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int hi6220_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct device *dev = &pdev->dev; ++ struct phy *phy; ++ struct hi6220_priv *priv; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->dev = dev; ++ priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "hisilicon,peripheral-syscon"); ++ if (IS_ERR(priv->reg)) { ++ dev_err(dev, "no hisilicon,peripheral-syscon\n"); ++ return PTR_ERR(priv->reg); ++ } ++ ++ hi6220_phy_init(priv); ++ ++ phy = devm_phy_create(dev, NULL, &hi6220_phy_ops); ++ if (IS_ERR(phy)) ++ return PTR_ERR(phy); ++ ++ phy_set_drvdata(phy, priv); ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id hi6220_phy_of_match[] = { ++ {.compatible = "hisilicon,hi6220-usb-phy",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, hi6220_phy_of_match); ++ ++static struct platform_driver hi6220_phy_driver = { ++ .probe = hi6220_phy_probe, ++ .driver = { ++ .name = "hi6220-usb-phy", ++ .of_match_table = hi6220_phy_of_match, ++ } ++}; ++module_platform_driver(hi6220_phy_driver); ++ ++MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver"); ++MODULE_ALIAS("platform:hi6220-usb-phy"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/hisilicon/phy-hix5hd2-sata.c b/drivers/phy/hisilicon/phy-hix5hd2-sata.c +new file mode 100644 +index 000000000000..e5ab3aa78b9d +--- /dev/null ++++ b/drivers/phy/hisilicon/phy-hix5hd2-sata.c +@@ -0,0 +1,191 @@ ++/* ++ * Copyright (c) 2014 Linaro Ltd. ++ * Copyright (c) 2014 Hisilicon Limited. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SATA_PHY0_CTLL 0xa0 ++#define MPLL_MULTIPLIER_SHIFT 1 ++#define MPLL_MULTIPLIER_MASK 0xfe ++#define MPLL_MULTIPLIER_50M 0x3c ++#define MPLL_MULTIPLIER_100M 0x1e ++#define PHY_RESET BIT(0) ++#define REF_SSP_EN BIT(9) ++#define SSC_EN BIT(10) ++#define REF_USE_PAD BIT(23) ++ ++#define SATA_PORT_PHYCTL 0x174 ++#define SPEED_MODE_MASK 0x6f0000 ++#define HALF_RATE_SHIFT 16 ++#define PHY_CONFIG_SHIFT 18 ++#define GEN2_EN_SHIFT 21 ++#define SPEED_CTRL BIT(20) ++ ++#define SATA_PORT_PHYCTL1 0x148 ++#define AMPLITUDE_MASK 0x3ffffe ++#define AMPLITUDE_GEN3 0x68 ++#define AMPLITUDE_GEN3_SHIFT 15 ++#define AMPLITUDE_GEN2 0x56 ++#define AMPLITUDE_GEN2_SHIFT 8 ++#define AMPLITUDE_GEN1 0x56 ++#define AMPLITUDE_GEN1_SHIFT 1 ++ ++#define SATA_PORT_PHYCTL2 0x14c ++#define PREEMPH_MASK 0x3ffff ++#define PREEMPH_GEN3 0x20 ++#define PREEMPH_GEN3_SHIFT 12 ++#define PREEMPH_GEN2 0x15 ++#define PREEMPH_GEN2_SHIFT 6 ++#define PREEMPH_GEN1 0x5 ++#define PREEMPH_GEN1_SHIFT 0 ++ ++struct hix5hd2_priv { ++ void __iomem *base; ++ struct regmap *peri_ctrl; ++}; ++ ++enum phy_speed_mode { ++ SPEED_MODE_GEN1 = 0, ++ SPEED_MODE_GEN2 = 1, ++ SPEED_MODE_GEN3 = 2, ++}; ++ ++static int hix5hd2_sata_phy_init(struct phy *phy) ++{ ++ struct hix5hd2_priv *priv = phy_get_drvdata(phy); ++ u32 val, data[2]; ++ int ret; ++ ++ if (priv->peri_ctrl) { ++ ret = of_property_read_u32_array(phy->dev.of_node, ++ "hisilicon,power-reg", ++ &data[0], 2); ++ if (ret) { ++ dev_err(&phy->dev, "Fail read hisilicon,power-reg\n"); ++ return ret; ++ } ++ ++ regmap_update_bits(priv->peri_ctrl, data[0], ++ BIT(data[1]), BIT(data[1])); ++ } ++ ++ /* reset phy */ ++ val = readl_relaxed(priv->base + SATA_PHY0_CTLL); ++ val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD); ++ val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT | ++ REF_SSP_EN | PHY_RESET; ++ writel_relaxed(val, priv->base + SATA_PHY0_CTLL); ++ msleep(20); ++ val &= ~PHY_RESET; ++ writel_relaxed(val, priv->base + SATA_PHY0_CTLL); ++ ++ val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1); ++ val &= ~AMPLITUDE_MASK; ++ val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT | ++ AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT | ++ AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT; ++ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1); ++ ++ val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2); ++ val &= ~PREEMPH_MASK; ++ val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT | ++ PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT | ++ PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT; ++ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2); ++ ++ /* ensure PHYCTRL setting takes effect */ ++ val = readl_relaxed(priv->base + SATA_PORT_PHYCTL); ++ val &= ~SPEED_MODE_MASK; ++ val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT | ++ SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT | ++ SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL; ++ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); ++ ++ msleep(20); ++ val &= ~SPEED_MODE_MASK; ++ val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT | ++ SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT | ++ SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL; ++ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); ++ ++ val &= ~(SPEED_MODE_MASK | SPEED_CTRL); ++ val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT | ++ SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT | ++ SPEED_MODE_GEN2 << GEN2_EN_SHIFT; ++ writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); ++ ++ return 0; ++} ++ ++static const struct phy_ops hix5hd2_sata_phy_ops = { ++ .init = hix5hd2_sata_phy_init, ++ .owner = THIS_MODULE, ++}; ++ ++static int hix5hd2_sata_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ struct phy *phy; ++ struct hix5hd2_priv *priv; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ ++ priv->base = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!priv->base) ++ return -ENOMEM; ++ ++ priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "hisilicon,peripheral-syscon"); ++ if (IS_ERR(priv->peri_ctrl)) ++ priv->peri_ctrl = NULL; ++ ++ phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ return PTR_ERR(phy); ++ } ++ ++ phy_set_drvdata(phy, priv); ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id hix5hd2_sata_phy_of_match[] = { ++ {.compatible = "hisilicon,hix5hd2-sata-phy",}, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match); ++ ++static struct platform_driver hix5hd2_sata_phy_driver = { ++ .probe = hix5hd2_sata_phy_probe, ++ .driver = { ++ .name = "hix5hd2-sata-phy", ++ .of_match_table = hix5hd2_sata_phy_of_match, ++ } ++}; ++module_platform_driver(hix5hd2_sata_phy_driver); ++ ++MODULE_AUTHOR("Jiancheng Xue "); ++MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver"); ++MODULE_ALIAS("platform:hix5hd2-sata-phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig +new file mode 100644 +index 000000000000..048d8893bc2e +--- /dev/null ++++ b/drivers/phy/marvell/Kconfig +@@ -0,0 +1,50 @@ ++# ++# Phy drivers for Marvell platforms ++# ++config ARMADA375_USBCLUSTER_PHY ++ def_bool y ++ depends on MACH_ARMADA_375 || COMPILE_TEST ++ depends on OF && HAS_IOMEM ++ select GENERIC_PHY ++ ++config PHY_BERLIN_SATA ++ tristate "Marvell Berlin SATA PHY driver" ++ depends on ARCH_BERLIN && HAS_IOMEM && OF ++ select GENERIC_PHY ++ help ++ Enable this to support the SATA PHY on Marvell Berlin SoCs. ++ ++config PHY_BERLIN_USB ++ tristate "Marvell Berlin USB PHY Driver" ++ depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF ++ select GENERIC_PHY ++ help ++ Enable this to support the USB PHY on Marvell Berlin SoCs. ++ ++config PHY_MVEBU_SATA ++ def_bool y ++ depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD ++ depends on OF ++ select GENERIC_PHY ++ ++config PHY_PXA_28NM_HSIC ++ tristate "Marvell USB HSIC 28nm PHY Driver" ++ depends on HAS_IOMEM ++ select GENERIC_PHY ++ help ++ Enable this to support Marvell USB HSIC PHY driver for Marvell ++ SoC. This driver will do the PHY initialization and shutdown. ++ The PHY driver will be used by Marvell ehci driver. ++ ++ To compile this driver as a module, choose M here. ++ ++config PHY_PXA_28NM_USB2 ++ tristate "Marvell USB 2.0 28nm PHY Driver" ++ depends on HAS_IOMEM ++ select GENERIC_PHY ++ help ++ Enable this to support Marvell USB 2.0 PHY driver for Marvell ++ SoC. This driver will do the PHY initialization and shutdown. ++ The PHY driver will be used by Marvell udc/ehci/otg driver. ++ ++ To compile this driver as a module, choose M here. +diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile +new file mode 100644 +index 000000000000..3fc188f59118 +--- /dev/null ++++ b/drivers/phy/marvell/Makefile +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o ++obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o ++obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o ++obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o ++obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o ++obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o +diff --git a/drivers/phy/marvell/phy-armada375-usb2.c b/drivers/phy/marvell/phy-armada375-usb2.c +new file mode 100644 +index 000000000000..1a3db288c0a9 +--- /dev/null ++++ b/drivers/phy/marvell/phy-armada375-usb2.c +@@ -0,0 +1,158 @@ ++/* ++ * USB cluster support for Armada 375 platform. ++ * ++ * Copyright (C) 2014 Marvell ++ * ++ * Gregory CLEMENT ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2 or later. This program is licensed "as is" ++ * without any warranty of any kind, whether express or implied. ++ * ++ * Armada 375 comes with an USB2 host and device controller and an ++ * USB3 controller. The USB cluster control register allows to manage ++ * common features of both USB controllers. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define USB2_PHY_CONFIG_DISABLE BIT(0) ++ ++struct armada375_cluster_phy { ++ struct phy *phy; ++ void __iomem *reg; ++ bool use_usb3; ++ int phy_provided; ++}; ++ ++static int armada375_usb_phy_init(struct phy *phy) ++{ ++ struct armada375_cluster_phy *cluster_phy; ++ u32 reg; ++ ++ cluster_phy = phy_get_drvdata(phy); ++ if (!cluster_phy) ++ return -ENODEV; ++ ++ reg = readl(cluster_phy->reg); ++ if (cluster_phy->use_usb3) ++ reg |= USB2_PHY_CONFIG_DISABLE; ++ else ++ reg &= ~USB2_PHY_CONFIG_DISABLE; ++ writel(reg, cluster_phy->reg); ++ ++ return 0; ++} ++ ++static const struct phy_ops armada375_usb_phy_ops = { ++ .init = armada375_usb_phy_init, ++ .owner = THIS_MODULE, ++}; ++ ++/* ++ * Only one controller can use this PHY. We shouldn't have the case ++ * when two controllers want to use this PHY. But if this case occurs ++ * then we provide a phy to the first one and return an error for the ++ * next one. This error has also to be an error returned by ++ * devm_phy_optional_get() so different from ENODEV for USB2. In the ++ * USB3 case it still optional and we use ENODEV. ++ */ ++static struct phy *armada375_usb_phy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev); ++ ++ if (!cluster_phy) ++ return ERR_PTR(-ENODEV); ++ ++ /* ++ * Either the phy had never been requested and then the first ++ * usb claiming it can get it, or it had already been ++ * requested in this case, we only allow to use it with the ++ * same configuration. ++ */ ++ if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) && ++ (cluster_phy->phy_provided != args->args[0]))) { ++ dev_err(dev, "This PHY has already been provided!\n"); ++ dev_err(dev, "Check your device tree, only one controller can use it\n."); ++ if (args->args[0] == PHY_TYPE_USB2) ++ return ERR_PTR(-EBUSY); ++ else ++ return ERR_PTR(-ENODEV); ++ } ++ ++ if (args->args[0] == PHY_TYPE_USB2) ++ cluster_phy->use_usb3 = false; ++ else if (args->args[0] == PHY_TYPE_USB3) ++ cluster_phy->use_usb3 = true; ++ else { ++ dev_err(dev, "Invalid PHY mode\n"); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ /* Store which phy mode is used for next test */ ++ cluster_phy->phy_provided = args->args[0]; ++ ++ return cluster_phy->phy; ++} ++ ++static int armada375_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *phy; ++ struct phy_provider *phy_provider; ++ void __iomem *usb_cluster_base; ++ struct resource *res; ++ struct armada375_cluster_phy *cluster_phy; ++ ++ cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL); ++ if (!cluster_phy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ usb_cluster_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(usb_cluster_base)) ++ return PTR_ERR(usb_cluster_base); ++ ++ phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ return PTR_ERR(phy); ++ } ++ ++ cluster_phy->phy = phy; ++ cluster_phy->reg = usb_cluster_base; ++ ++ dev_set_drvdata(dev, cluster_phy); ++ phy_set_drvdata(phy, cluster_phy); ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, ++ armada375_usb_phy_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id of_usb_cluster_table[] = { ++ { .compatible = "marvell,armada-375-usb-cluster", }, ++ { /* end of list */ }, ++}; ++MODULE_DEVICE_TABLE(of, of_usb_cluster_table); ++ ++static struct platform_driver armada375_usb_phy_driver = { ++ .probe = armada375_usb_phy_probe, ++ .driver = { ++ .of_match_table = of_usb_cluster_table, ++ .name = "armada-375-usb-cluster", ++ } ++}; ++module_platform_driver(armada375_usb_phy_driver); ++ ++MODULE_DESCRIPTION("Armada 375 USB cluster driver"); ++MODULE_AUTHOR("Gregory CLEMENT "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/marvell/phy-berlin-sata.c b/drivers/phy/marvell/phy-berlin-sata.c +new file mode 100644 +index 000000000000..2c7a57f2d595 +--- /dev/null ++++ b/drivers/phy/marvell/phy-berlin-sata.c +@@ -0,0 +1,299 @@ ++/* ++ * Marvell Berlin SATA PHY driver ++ * ++ * Copyright (C) 2014 Marvell Technology Group Ltd. ++ * ++ * Antoine Ténart ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define HOST_VSA_ADDR 0x0 ++#define HOST_VSA_DATA 0x4 ++#define PORT_SCR_CTL 0x2c ++#define PORT_VSR_ADDR 0x78 ++#define PORT_VSR_DATA 0x7c ++ ++#define CONTROL_REGISTER 0x0 ++#define MBUS_SIZE_CONTROL 0x4 ++ ++#define POWER_DOWN_PHY0 BIT(6) ++#define POWER_DOWN_PHY1 BIT(14) ++#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) ++#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) ++ ++#define BG2_PHY_BASE 0x080 ++#define BG2Q_PHY_BASE 0x200 ++ ++/* register 0x01 */ ++#define REF_FREF_SEL_25 BIT(0) ++#define PHY_MODE_SATA (0x0 << 5) ++ ++/* register 0x02 */ ++#define USE_MAX_PLL_RATE BIT(12) ++ ++/* register 0x23 */ ++#define DATA_BIT_WIDTH_10 (0x0 << 10) ++#define DATA_BIT_WIDTH_20 (0x1 << 10) ++#define DATA_BIT_WIDTH_40 (0x2 << 10) ++ ++/* register 0x25 */ ++#define PHY_GEN_MAX_1_5 (0x0 << 10) ++#define PHY_GEN_MAX_3_0 (0x1 << 10) ++#define PHY_GEN_MAX_6_0 (0x2 << 10) ++ ++struct phy_berlin_desc { ++ struct phy *phy; ++ u32 power_bit; ++ unsigned index; ++}; ++ ++struct phy_berlin_priv { ++ void __iomem *base; ++ spinlock_t lock; ++ struct clk *clk; ++ struct phy_berlin_desc **phys; ++ unsigned nphys; ++ u32 phy_base; ++}; ++ ++static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, ++ u32 phy_base, u32 reg, u32 mask, u32 val) ++{ ++ u32 regval; ++ ++ /* select register */ ++ writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR); ++ ++ /* set bits */ ++ regval = readl(ctrl_reg + PORT_VSR_DATA); ++ regval &= ~mask; ++ regval |= val; ++ writel(regval, ctrl_reg + PORT_VSR_DATA); ++} ++ ++static int phy_berlin_sata_power_on(struct phy *phy) ++{ ++ struct phy_berlin_desc *desc = phy_get_drvdata(phy); ++ struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); ++ void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); ++ u32 regval; ++ ++ clk_prepare_enable(priv->clk); ++ ++ spin_lock(&priv->lock); ++ ++ /* Power on PHY */ ++ writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); ++ regval = readl(priv->base + HOST_VSA_DATA); ++ regval &= ~desc->power_bit; ++ writel(regval, priv->base + HOST_VSA_DATA); ++ ++ /* Configure MBus */ ++ writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR); ++ regval = readl(priv->base + HOST_VSA_DATA); ++ regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; ++ writel(regval, priv->base + HOST_VSA_DATA); ++ ++ /* set PHY mode and ref freq to 25 MHz */ ++ phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01, ++ 0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA); ++ ++ /* set PHY up to 6 Gbps */ ++ phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25, ++ 0x0c00, PHY_GEN_MAX_6_0); ++ ++ /* set 40 bits width */ ++ phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23, ++ 0x0c00, DATA_BIT_WIDTH_40); ++ ++ /* use max pll rate */ ++ phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02, ++ 0x0000, USE_MAX_PLL_RATE); ++ ++ /* set Gen3 controller speed */ ++ regval = readl(ctrl_reg + PORT_SCR_CTL); ++ regval &= ~GENMASK(7, 4); ++ regval |= 0x30; ++ writel(regval, ctrl_reg + PORT_SCR_CTL); ++ ++ spin_unlock(&priv->lock); ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++static int phy_berlin_sata_power_off(struct phy *phy) ++{ ++ struct phy_berlin_desc *desc = phy_get_drvdata(phy); ++ struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); ++ u32 regval; ++ ++ clk_prepare_enable(priv->clk); ++ ++ spin_lock(&priv->lock); ++ ++ /* Power down PHY */ ++ writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); ++ regval = readl(priv->base + HOST_VSA_DATA); ++ regval |= desc->power_bit; ++ writel(regval, priv->base + HOST_VSA_DATA); ++ ++ spin_unlock(&priv->lock); ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct phy_berlin_priv *priv = dev_get_drvdata(dev); ++ int i; ++ ++ if (WARN_ON(args->args[0] >= priv->nphys)) ++ return ERR_PTR(-ENODEV); ++ ++ for (i = 0; i < priv->nphys; i++) { ++ if (priv->phys[i]->index == args->args[0]) ++ break; ++ } ++ ++ if (i == priv->nphys) ++ return ERR_PTR(-ENODEV); ++ ++ return priv->phys[i]->phy; ++} ++ ++static const struct phy_ops phy_berlin_sata_ops = { ++ .power_on = phy_berlin_sata_power_on, ++ .power_off = phy_berlin_sata_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static u32 phy_berlin_power_down_bits[] = { ++ POWER_DOWN_PHY0, ++ POWER_DOWN_PHY1, ++}; ++ ++static int phy_berlin_sata_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *child; ++ struct phy *phy; ++ struct phy_provider *phy_provider; ++ struct phy_berlin_priv *priv; ++ struct resource *res; ++ int ret, i = 0; ++ u32 phy_id; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ ++ priv->base = devm_ioremap(dev, res->start, resource_size(res)); ++ if (!priv->base) ++ return -ENOMEM; ++ ++ priv->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(priv->clk)) ++ return PTR_ERR(priv->clk); ++ ++ priv->nphys = of_get_child_count(dev->of_node); ++ if (priv->nphys == 0) ++ return -ENODEV; ++ ++ priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys), ++ GFP_KERNEL); ++ if (!priv->phys) ++ return -ENOMEM; ++ ++ if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy")) ++ priv->phy_base = BG2_PHY_BASE; ++ else ++ priv->phy_base = BG2Q_PHY_BASE; ++ ++ dev_set_drvdata(dev, priv); ++ spin_lock_init(&priv->lock); ++ ++ for_each_available_child_of_node(dev->of_node, child) { ++ struct phy_berlin_desc *phy_desc; ++ ++ if (of_property_read_u32(child, "reg", &phy_id)) { ++ dev_err(dev, "missing reg property in node %s\n", ++ child->name); ++ ret = -EINVAL; ++ goto put_child; ++ } ++ ++ if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { ++ dev_err(dev, "invalid reg in node %s\n", child->name); ++ ret = -EINVAL; ++ goto put_child; ++ } ++ ++ phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL); ++ if (!phy_desc) { ++ ret = -ENOMEM; ++ goto put_child; ++ } ++ ++ phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create PHY %d\n", phy_id); ++ ret = PTR_ERR(phy); ++ goto put_child; ++ } ++ ++ phy_desc->phy = phy; ++ phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; ++ phy_desc->index = phy_id; ++ phy_set_drvdata(phy, phy_desc); ++ ++ priv->phys[i++] = phy_desc; ++ ++ /* Make sure the PHY is off */ ++ phy_berlin_sata_power_off(phy); ++ } ++ ++ phy_provider = ++ devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++put_child: ++ of_node_put(child); ++ return ret; ++} ++ ++static const struct of_device_id phy_berlin_sata_of_match[] = { ++ { .compatible = "marvell,berlin2-sata-phy" }, ++ { .compatible = "marvell,berlin2q-sata-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); ++ ++static struct platform_driver phy_berlin_sata_driver = { ++ .probe = phy_berlin_sata_probe, ++ .driver = { ++ .name = "phy-berlin-sata", ++ .of_match_table = phy_berlin_sata_of_match, ++ }, ++}; ++module_platform_driver(phy_berlin_sata_driver); ++ ++MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver"); ++MODULE_AUTHOR("Antoine Ténart "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/marvell/phy-berlin-usb.c b/drivers/phy/marvell/phy-berlin-usb.c +new file mode 100644 +index 000000000000..2017751ede26 +--- /dev/null ++++ b/drivers/phy/marvell/phy-berlin-usb.c +@@ -0,0 +1,214 @@ ++/* ++ * Copyright (C) 2014 Marvell Technology Group Ltd. ++ * ++ * Antoine Tenart ++ * Jisheng Zhang ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define USB_PHY_PLL 0x04 ++#define USB_PHY_PLL_CONTROL 0x08 ++#define USB_PHY_TX_CTRL0 0x10 ++#define USB_PHY_TX_CTRL1 0x14 ++#define USB_PHY_TX_CTRL2 0x18 ++#define USB_PHY_RX_CTRL 0x20 ++#define USB_PHY_ANALOG 0x34 ++ ++/* USB_PHY_PLL */ ++#define CLK_REF_DIV(x) ((x) << 4) ++#define FEEDBACK_CLK_DIV(x) ((x) << 8) ++ ++/* USB_PHY_PLL_CONTROL */ ++#define CLK_STABLE BIT(0) ++#define PLL_CTRL_PIN BIT(1) ++#define PLL_CTRL_REG BIT(2) ++#define PLL_ON BIT(3) ++#define PHASE_OFF_TOL_125 (0x0 << 5) ++#define PHASE_OFF_TOL_250 BIT(5) ++#define KVC0_CALIB (0x0 << 9) ++#define KVC0_REG_CTRL BIT(9) ++#define KVC0_HIGH (0x0 << 10) ++#define KVC0_LOW (0x3 << 10) ++#define CLK_BLK_EN BIT(13) ++ ++/* USB_PHY_TX_CTRL0 */ ++#define EXT_HS_RCAL_EN BIT(3) ++#define EXT_FS_RCAL_EN BIT(4) ++#define IMPCAL_VTH_DIV(x) ((x) << 5) ++#define EXT_RS_RCAL_DIV(x) ((x) << 8) ++#define EXT_FS_RCAL_DIV(x) ((x) << 12) ++ ++/* USB_PHY_TX_CTRL1 */ ++#define TX_VDD15_14 (0x0 << 4) ++#define TX_VDD15_15 BIT(4) ++#define TX_VDD15_16 (0x2 << 4) ++#define TX_VDD15_17 (0x3 << 4) ++#define TX_VDD12_VDD (0x0 << 6) ++#define TX_VDD12_11 BIT(6) ++#define TX_VDD12_12 (0x2 << 6) ++#define TX_VDD12_13 (0x3 << 6) ++#define LOW_VDD_EN BIT(8) ++#define TX_OUT_AMP(x) ((x) << 9) ++ ++/* USB_PHY_TX_CTRL2 */ ++#define TX_CHAN_CTRL_REG(x) ((x) << 0) ++#define DRV_SLEWRATE(x) ((x) << 4) ++#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6) ++#define IMP_CAL_FS_HS_DLY_1 BIT(6) ++#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6) ++#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6) ++#define FS_DRV_EN_MASK(x) ((x) << 8) ++#define HS_DRV_EN_MASK(x) ((x) << 12) ++ ++/* USB_PHY_RX_CTRL */ ++#define PHASE_FREEZE_DLY_2_CL (0x0 << 0) ++#define PHASE_FREEZE_DLY_4_CL BIT(0) ++#define ACK_LENGTH_8_CL (0x0 << 2) ++#define ACK_LENGTH_12_CL BIT(2) ++#define ACK_LENGTH_16_CL (0x2 << 2) ++#define ACK_LENGTH_20_CL (0x3 << 2) ++#define SQ_LENGTH_3 (0x0 << 4) ++#define SQ_LENGTH_6 BIT(4) ++#define SQ_LENGTH_9 (0x2 << 4) ++#define SQ_LENGTH_12 (0x3 << 4) ++#define DISCON_THRESHOLD_260 (0x0 << 6) ++#define DISCON_THRESHOLD_270 BIT(6) ++#define DISCON_THRESHOLD_280 (0x2 << 6) ++#define DISCON_THRESHOLD_290 (0x3 << 6) ++#define SQ_THRESHOLD(x) ((x) << 8) ++#define LPF_COEF(x) ((x) << 12) ++#define INTPL_CUR_10 (0x0 << 14) ++#define INTPL_CUR_20 BIT(14) ++#define INTPL_CUR_30 (0x2 << 14) ++#define INTPL_CUR_40 (0x3 << 14) ++ ++/* USB_PHY_ANALOG */ ++#define ANA_PWR_UP BIT(1) ++#define ANA_PWR_DOWN BIT(2) ++#define V2I_VCO_RATIO(x) ((x) << 7) ++#define R_ROTATE_90 (0x0 << 10) ++#define R_ROTATE_0 BIT(10) ++#define MODE_TEST_EN BIT(11) ++#define ANA_TEST_DC_CTRL(x) ((x) << 12) ++ ++static const u32 phy_berlin_pll_dividers[] = { ++ /* Berlin 2 */ ++ CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), ++ /* Berlin 2CD/Q */ ++ CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), ++}; ++ ++struct phy_berlin_usb_priv { ++ void __iomem *base; ++ struct reset_control *rst_ctrl; ++ u32 pll_divider; ++}; ++ ++static int phy_berlin_usb_power_on(struct phy *phy) ++{ ++ struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy); ++ ++ reset_control_reset(priv->rst_ctrl); ++ ++ writel(priv->pll_divider, ++ priv->base + USB_PHY_PLL); ++ writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL | ++ CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL); ++ writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), ++ priv->base + USB_PHY_ANALOG); ++ writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | ++ DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | ++ INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); ++ ++ writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); ++ writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), ++ priv->base + USB_PHY_TX_CTRL0); ++ ++ writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) | ++ EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0); ++ ++ writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), ++ priv->base + USB_PHY_TX_CTRL0); ++ writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 | ++ FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2); ++ ++ return 0; ++} ++ ++static const struct phy_ops phy_berlin_usb_ops = { ++ .power_on = phy_berlin_usb_power_on, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct of_device_id phy_berlin_usb_of_match[] = { ++ { ++ .compatible = "marvell,berlin2-usb-phy", ++ .data = &phy_berlin_pll_dividers[0], ++ }, ++ { ++ .compatible = "marvell,berlin2cd-usb-phy", ++ .data = &phy_berlin_pll_dividers[1], ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, phy_berlin_usb_of_match); ++ ++static int phy_berlin_usb_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *match = ++ of_match_device(phy_berlin_usb_of_match, &pdev->dev); ++ struct phy_berlin_usb_priv *priv; ++ struct resource *res; ++ struct phy *phy; ++ struct phy_provider *phy_provider; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->rst_ctrl)) ++ return PTR_ERR(priv->rst_ctrl); ++ ++ priv->pll_divider = *((u32 *)match->data); ++ ++ phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); ++ if (IS_ERR(phy)) { ++ dev_err(&pdev->dev, "failed to create PHY\n"); ++ return PTR_ERR(phy); ++ } ++ ++ phy_set_drvdata(phy, priv); ++ ++ phy_provider = ++ devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static struct platform_driver phy_berlin_usb_driver = { ++ .probe = phy_berlin_usb_probe, ++ .driver = { ++ .name = "phy-berlin-usb", ++ .of_match_table = phy_berlin_usb_of_match, ++ }, ++}; ++module_platform_driver(phy_berlin_usb_driver); ++ ++MODULE_AUTHOR("Antoine Tenart "); ++MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/marvell/phy-mvebu-sata.c b/drivers/phy/marvell/phy-mvebu-sata.c +new file mode 100644 +index 000000000000..768ce92e81ce +--- /dev/null ++++ b/drivers/phy/marvell/phy-mvebu-sata.c +@@ -0,0 +1,138 @@ ++/* ++ * phy-mvebu-sata.c: SATA Phy driver for the Marvell mvebu SoCs. ++ * ++ * Copyright (C) 2013 Andrew Lunn ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct priv { ++ struct clk *clk; ++ void __iomem *base; ++}; ++ ++#define SATA_PHY_MODE_2 0x0330 ++#define MODE_2_FORCE_PU_TX BIT(0) ++#define MODE_2_FORCE_PU_RX BIT(1) ++#define MODE_2_PU_PLL BIT(2) ++#define MODE_2_PU_IVREF BIT(3) ++#define SATA_IF_CTRL 0x0050 ++#define CTRL_PHY_SHUTDOWN BIT(9) ++ ++static int phy_mvebu_sata_power_on(struct phy *phy) ++{ ++ struct priv *priv = phy_get_drvdata(phy); ++ u32 reg; ++ ++ clk_prepare_enable(priv->clk); ++ ++ /* Enable PLL and IVREF */ ++ reg = readl(priv->base + SATA_PHY_MODE_2); ++ reg |= (MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | ++ MODE_2_PU_PLL | MODE_2_PU_IVREF); ++ writel(reg , priv->base + SATA_PHY_MODE_2); ++ ++ /* Enable PHY */ ++ reg = readl(priv->base + SATA_IF_CTRL); ++ reg &= ~CTRL_PHY_SHUTDOWN; ++ writel(reg, priv->base + SATA_IF_CTRL); ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++static int phy_mvebu_sata_power_off(struct phy *phy) ++{ ++ struct priv *priv = phy_get_drvdata(phy); ++ u32 reg; ++ ++ clk_prepare_enable(priv->clk); ++ ++ /* Disable PLL and IVREF */ ++ reg = readl(priv->base + SATA_PHY_MODE_2); ++ reg &= ~(MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | ++ MODE_2_PU_PLL | MODE_2_PU_IVREF); ++ writel(reg, priv->base + SATA_PHY_MODE_2); ++ ++ /* Disable PHY */ ++ reg = readl(priv->base + SATA_IF_CTRL); ++ reg |= CTRL_PHY_SHUTDOWN; ++ writel(reg, priv->base + SATA_IF_CTRL); ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++static const struct phy_ops phy_mvebu_sata_ops = { ++ .power_on = phy_mvebu_sata_power_on, ++ .power_off = phy_mvebu_sata_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int phy_mvebu_sata_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ struct priv *priv; ++ struct phy *phy; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ priv->clk = devm_clk_get(&pdev->dev, "sata"); ++ if (IS_ERR(priv->clk)) ++ return PTR_ERR(priv->clk); ++ ++ phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops); ++ if (IS_ERR(phy)) ++ return PTR_ERR(phy); ++ ++ phy_set_drvdata(phy, priv); ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, ++ of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ /* The boot loader may of left it on. Turn it off. */ ++ phy_mvebu_sata_power_off(phy); ++ ++ return 0; ++} ++ ++static const struct of_device_id phy_mvebu_sata_of_match[] = { ++ { .compatible = "marvell,mvebu-sata-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match); ++ ++static struct platform_driver phy_mvebu_sata_driver = { ++ .probe = phy_mvebu_sata_probe, ++ .driver = { ++ .name = "phy-mvebu-sata", ++ .of_match_table = phy_mvebu_sata_of_match, ++ } ++}; ++module_platform_driver(phy_mvebu_sata_driver); ++ ++MODULE_AUTHOR("Andrew Lunn "); ++MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/marvell/phy-pxa-28nm-hsic.c b/drivers/phy/marvell/phy-pxa-28nm-hsic.c +new file mode 100644 +index 000000000000..234aacf4db20 +--- /dev/null ++++ b/drivers/phy/marvell/phy-pxa-28nm-hsic.c +@@ -0,0 +1,220 @@ ++/* ++ * Copyright (C) 2015 Linaro, Ltd. ++ * Rob Herring ++ * ++ * Based on vendor driver: ++ * Copyright (C) 2013 Marvell Inc. ++ * Author: Chao Xie ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PHY_28NM_HSIC_CTRL 0x08 ++#define PHY_28NM_HSIC_IMPCAL_CAL 0x18 ++#define PHY_28NM_HSIC_PLL_CTRL01 0x1c ++#define PHY_28NM_HSIC_PLL_CTRL2 0x20 ++#define PHY_28NM_HSIC_INT 0x28 ++ ++#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 ++#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 ++#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 ++ ++#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10) ++#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15) ++#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7) ++#define S2H_DRV_SE0_4RESUME BIT(14) ++#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27) ++ ++#define PHY_28NM_HSIC_CONNECT_INT BIT(1) ++#define PHY_28NM_HSIC_HS_READY_INT BIT(2) ++ ++struct mv_hsic_phy { ++ struct phy *phy; ++ struct platform_device *pdev; ++ void __iomem *base; ++ struct clk *clk; ++}; ++ ++static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) ++{ ++ timeout += jiffies; ++ while (time_is_after_eq_jiffies(timeout)) { ++ if ((readl(reg) & mask) == mask) ++ return true; ++ msleep(1); ++ } ++ return false; ++} ++ ++static int mv_hsic_phy_init(struct phy *phy) ++{ ++ struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); ++ struct platform_device *pdev = mv_phy->pdev; ++ void __iomem *base = mv_phy->base; ++ ++ clk_prepare_enable(mv_phy->clk); ++ ++ /* Set reference clock */ ++ writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT | ++ 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT | ++ 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT, ++ base + PHY_28NM_HSIC_PLL_CTRL01); ++ ++ /* Turn on PLL */ ++ writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | ++ PHY_28NM_HSIC_S2H_PU_PLL, ++ base + PHY_28NM_HSIC_PLL_CTRL2); ++ ++ /* Make sure PHY PLL is locked */ ++ if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, ++ PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { ++ dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); ++ clk_disable_unprepare(mv_phy->clk); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static int mv_hsic_phy_power_on(struct phy *phy) ++{ ++ struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); ++ struct platform_device *pdev = mv_phy->pdev; ++ void __iomem *base = mv_phy->base; ++ u32 reg; ++ ++ reg = readl(base + PHY_28NM_HSIC_CTRL); ++ /* Avoid SE0 state when resume for some device will take it as reset */ ++ reg &= ~S2H_DRV_SE0_4RESUME; ++ reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */ ++ writel(reg, base + PHY_28NM_HSIC_CTRL); ++ ++ /* ++ * Calibration Timing ++ * ____________________________ ++ * CAL START ___| ++ * ____________________ ++ * CAL_DONE ___________| ++ * | 400us | ++ */ ++ ++ /* Make sure PHY Calibration is ready */ ++ if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, ++ PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { ++ dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); ++ return -ETIMEDOUT; ++ } ++ ++ /* Waiting for HSIC connect int*/ ++ if (!wait_for_reg(base + PHY_28NM_HSIC_INT, ++ PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { ++ dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static int mv_hsic_phy_power_off(struct phy *phy) ++{ ++ struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); ++ void __iomem *base = mv_phy->base; ++ ++ writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN, ++ base + PHY_28NM_HSIC_CTRL); ++ ++ return 0; ++} ++ ++static int mv_hsic_phy_exit(struct phy *phy) ++{ ++ struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); ++ void __iomem *base = mv_phy->base; ++ ++ /* Turn off PLL */ ++ writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & ++ ~PHY_28NM_HSIC_S2H_PU_PLL, ++ base + PHY_28NM_HSIC_PLL_CTRL2); ++ ++ clk_disable_unprepare(mv_phy->clk); ++ return 0; ++} ++ ++ ++static const struct phy_ops hsic_ops = { ++ .init = mv_hsic_phy_init, ++ .power_on = mv_hsic_phy_power_on, ++ .power_off = mv_hsic_phy_power_off, ++ .exit = mv_hsic_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv_hsic_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct mv_hsic_phy *mv_phy; ++ struct resource *r; ++ ++ mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); ++ if (!mv_phy) ++ return -ENOMEM; ++ ++ mv_phy->pdev = pdev; ++ ++ mv_phy->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(mv_phy->clk)) { ++ dev_err(&pdev->dev, "failed to get clock.\n"); ++ return PTR_ERR(mv_phy->clk); ++ } ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ mv_phy->base = devm_ioremap_resource(&pdev->dev, r); ++ if (IS_ERR(mv_phy->base)) ++ return PTR_ERR(mv_phy->base); ++ ++ mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops); ++ if (IS_ERR(mv_phy->phy)) ++ return PTR_ERR(mv_phy->phy); ++ ++ phy_set_drvdata(mv_phy->phy, mv_phy); ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id mv_hsic_phy_dt_match[] = { ++ { .compatible = "marvell,pxa1928-hsic-phy", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); ++ ++static struct platform_driver mv_hsic_phy_driver = { ++ .probe = mv_hsic_phy_probe, ++ .driver = { ++ .name = "mv-hsic-phy", ++ .of_match_table = of_match_ptr(mv_hsic_phy_dt_match), ++ }, ++}; ++module_platform_driver(mv_hsic_phy_driver); ++ ++MODULE_AUTHOR("Rob Herring "); ++MODULE_DESCRIPTION("Marvell HSIC phy driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/marvell/phy-pxa-28nm-usb2.c b/drivers/phy/marvell/phy-pxa-28nm-usb2.c +new file mode 100644 +index 000000000000..37e9c8ca4983 +--- /dev/null ++++ b/drivers/phy/marvell/phy-pxa-28nm-usb2.c +@@ -0,0 +1,355 @@ ++/* ++ * Copyright (C) 2015 Linaro, Ltd. ++ * Rob Herring ++ * ++ * Based on vendor driver: ++ * Copyright (C) 2013 Marvell Inc. ++ * Author: Chao Xie ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* USB PXA1928 PHY mapping */ ++#define PHY_28NM_PLL_REG0 0x0 ++#define PHY_28NM_PLL_REG1 0x4 ++#define PHY_28NM_CAL_REG 0x8 ++#define PHY_28NM_TX_REG0 0x0c ++#define PHY_28NM_TX_REG1 0x10 ++#define PHY_28NM_RX_REG0 0x14 ++#define PHY_28NM_RX_REG1 0x18 ++#define PHY_28NM_DIG_REG0 0x1c ++#define PHY_28NM_DIG_REG1 0x20 ++#define PHY_28NM_TEST_REG0 0x24 ++#define PHY_28NM_TEST_REG1 0x28 ++#define PHY_28NM_MOC_REG 0x2c ++#define PHY_28NM_PHY_RESERVE 0x30 ++#define PHY_28NM_OTG_REG 0x34 ++#define PHY_28NM_CHRG_DET 0x38 ++#define PHY_28NM_CTRL_REG0 0xc4 ++#define PHY_28NM_CTRL_REG1 0xc8 ++#define PHY_28NM_CTRL_REG2 0xd4 ++#define PHY_28NM_CTRL_REG3 0xdc ++ ++/* PHY_28NM_PLL_REG0 */ ++#define PHY_28NM_PLL_READY BIT(31) ++ ++#define PHY_28NM_PLL_SELLPFR_SHIFT 28 ++#define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28) ++ ++#define PHY_28NM_PLL_FBDIV_SHIFT 16 ++#define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16) ++ ++#define PHY_28NM_PLL_ICP_SHIFT 8 ++#define PHY_28NM_PLL_ICP_MASK (0x7 << 8) ++ ++#define PHY_28NM_PLL_REFDIV_SHIFT 0 ++#define PHY_28NM_PLL_REFDIV_MASK 0x7f ++ ++/* PHY_28NM_PLL_REG1 */ ++#define PHY_28NM_PLL_PU_BY_REG BIT(1) ++ ++#define PHY_28NM_PLL_PU_PLL BIT(0) ++ ++/* PHY_28NM_CAL_REG */ ++#define PHY_28NM_PLL_PLLCAL_DONE BIT(31) ++ ++#define PHY_28NM_PLL_IMPCAL_DONE BIT(23) ++ ++#define PHY_28NM_PLL_KVCO_SHIFT 16 ++#define PHY_28NM_PLL_KVCO_MASK (0x7 << 16) ++ ++#define PHY_28NM_PLL_CAL12_SHIFT 20 ++#define PHY_28NM_PLL_CAL12_MASK (0x3 << 20) ++ ++#define PHY_28NM_IMPCAL_VTH_SHIFT 8 ++#define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8) ++ ++#define PHY_28NM_PLLCAL_START_SHIFT 22 ++#define PHY_28NM_IMPCAL_START_SHIFT 13 ++ ++/* PHY_28NM_TX_REG0 */ ++#define PHY_28NM_TX_PU_BY_REG BIT(25) ++ ++#define PHY_28NM_TX_PU_ANA BIT(24) ++ ++#define PHY_28NM_TX_AMP_SHIFT 20 ++#define PHY_28NM_TX_AMP_MASK (0x7 << 20) ++ ++/* PHY_28NM_RX_REG0 */ ++#define PHY_28NM_RX_SQ_THRESH_SHIFT 0 ++#define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0) ++ ++/* PHY_28NM_RX_REG1 */ ++#define PHY_28NM_RX_SQCAL_DONE BIT(31) ++ ++/* PHY_28NM_DIG_REG0 */ ++#define PHY_28NM_DIG_BITSTAFFING_ERR BIT(31) ++#define PHY_28NM_DIG_SYNC_ERR BIT(30) ++ ++#define PHY_28NM_DIG_SQ_FILT_SHIFT 16 ++#define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16) ++ ++#define PHY_28NM_DIG_SQ_BLK_SHIFT 12 ++#define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12) ++ ++#define PHY_28NM_DIG_SYNC_NUM_SHIFT 0 ++#define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0) ++ ++#define PHY_28NM_PLL_LOCK_BYPASS BIT(7) ++ ++/* PHY_28NM_OTG_REG */ ++#define PHY_28NM_OTG_CONTROL_BY_PIN BIT(5) ++#define PHY_28NM_OTG_PU_OTG BIT(4) ++ ++#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13 ++#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12 ++#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10 ++#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8 ++#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7 ++#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6 ++#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5 ++#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4 ++#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3 ++#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2 ++#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0 ++ ++#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4 ++#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2 ++ ++#define PHY_28NM_CTRL3_OVERWRITE BIT(0) ++#define PHY_28NM_CTRL3_VBUS_VALID BIT(4) ++#define PHY_28NM_CTRL3_AVALID BIT(5) ++#define PHY_28NM_CTRL3_BVALID BIT(6) ++ ++struct mv_usb2_phy { ++ struct phy *phy; ++ struct platform_device *pdev; ++ void __iomem *base; ++ struct clk *clk; ++}; ++ ++static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) ++{ ++ timeout += jiffies; ++ while (time_is_after_eq_jiffies(timeout)) { ++ if ((readl(reg) & mask) == mask) ++ return true; ++ msleep(1); ++ } ++ return false; ++} ++ ++static int mv_usb2_phy_28nm_init(struct phy *phy) ++{ ++ struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); ++ struct platform_device *pdev = mv_phy->pdev; ++ void __iomem *base = mv_phy->base; ++ u32 reg; ++ int ret; ++ ++ clk_prepare_enable(mv_phy->clk); ++ ++ /* PHY_28NM_PLL_REG0 */ ++ reg = readl(base + PHY_28NM_PLL_REG0) & ++ ~(PHY_28NM_PLL_SELLPFR_MASK | PHY_28NM_PLL_FBDIV_MASK ++ | PHY_28NM_PLL_ICP_MASK | PHY_28NM_PLL_REFDIV_MASK); ++ writel(reg | (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT ++ | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT ++ | 0x3 << PHY_28NM_PLL_ICP_SHIFT ++ | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), ++ base + PHY_28NM_PLL_REG0); ++ ++ /* PHY_28NM_PLL_REG1 */ ++ reg = readl(base + PHY_28NM_PLL_REG1); ++ writel(reg | PHY_28NM_PLL_PU_PLL | PHY_28NM_PLL_PU_BY_REG, ++ base + PHY_28NM_PLL_REG1); ++ ++ /* PHY_28NM_TX_REG0 */ ++ reg = readl(base + PHY_28NM_TX_REG0) & ~PHY_28NM_TX_AMP_MASK; ++ writel(reg | PHY_28NM_TX_PU_BY_REG | 0x3 << PHY_28NM_TX_AMP_SHIFT | ++ PHY_28NM_TX_PU_ANA, ++ base + PHY_28NM_TX_REG0); ++ ++ /* PHY_28NM_RX_REG0 */ ++ reg = readl(base + PHY_28NM_RX_REG0) & ~PHY_28NM_RX_SQ_THRESH_MASK; ++ writel(reg | 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT, ++ base + PHY_28NM_RX_REG0); ++ ++ /* PHY_28NM_DIG_REG0 */ ++ reg = readl(base + PHY_28NM_DIG_REG0) & ++ ~(PHY_28NM_DIG_BITSTAFFING_ERR | PHY_28NM_DIG_SYNC_ERR | ++ PHY_28NM_DIG_SQ_FILT_MASK | PHY_28NM_DIG_SQ_BLK_MASK | ++ PHY_28NM_DIG_SYNC_NUM_MASK); ++ writel(reg | (0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT | ++ PHY_28NM_PLL_LOCK_BYPASS), ++ base + PHY_28NM_DIG_REG0); ++ ++ /* PHY_28NM_OTG_REG */ ++ reg = readl(base + PHY_28NM_OTG_REG) | PHY_28NM_OTG_PU_OTG; ++ writel(reg & ~PHY_28NM_OTG_CONTROL_BY_PIN, base + PHY_28NM_OTG_REG); ++ ++ /* ++ * Calibration Timing ++ * ____________________________ ++ * CAL START ___| ++ * ____________________ ++ * CAL_DONE ___________| ++ * | 400us | ++ */ ++ ++ /* Make sure PHY Calibration is ready */ ++ if (!wait_for_reg(base + PHY_28NM_CAL_REG, ++ PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, ++ HZ / 10)) { ++ dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS."); ++ ret = -ETIMEDOUT; ++ goto err_clk; ++ } ++ if (!wait_for_reg(base + PHY_28NM_RX_REG1, ++ PHY_28NM_RX_SQCAL_DONE, HZ / 10)) { ++ dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS."); ++ ret = -ETIMEDOUT; ++ goto err_clk; ++ } ++ /* Make sure PHY PLL is ready */ ++ if (!wait_for_reg(base + PHY_28NM_PLL_REG0, ++ PHY_28NM_PLL_READY, HZ / 10)) { ++ dev_warn(&pdev->dev, "PLL_READY not set after 100mS."); ++ ret = -ETIMEDOUT; ++ goto err_clk; ++ } ++ ++ return 0; ++err_clk: ++ clk_disable_unprepare(mv_phy->clk); ++ return ret; ++} ++ ++static int mv_usb2_phy_28nm_power_on(struct phy *phy) ++{ ++ struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); ++ void __iomem *base = mv_phy->base; ++ ++ writel(readl(base + PHY_28NM_CTRL_REG3) | ++ (PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID | ++ PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), ++ base + PHY_28NM_CTRL_REG3); ++ ++ return 0; ++} ++ ++static int mv_usb2_phy_28nm_power_off(struct phy *phy) ++{ ++ struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); ++ void __iomem *base = mv_phy->base; ++ ++ writel(readl(base + PHY_28NM_CTRL_REG3) | ++ ~(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID ++ | PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), ++ base + PHY_28NM_CTRL_REG3); ++ ++ return 0; ++} ++ ++static int mv_usb2_phy_28nm_exit(struct phy *phy) ++{ ++ struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); ++ void __iomem *base = mv_phy->base; ++ unsigned int val; ++ ++ val = readw(base + PHY_28NM_PLL_REG1); ++ val &= ~PHY_28NM_PLL_PU_PLL; ++ writew(val, base + PHY_28NM_PLL_REG1); ++ ++ /* power down PHY Analog part */ ++ val = readw(base + PHY_28NM_TX_REG0); ++ val &= ~PHY_28NM_TX_PU_ANA; ++ writew(val, base + PHY_28NM_TX_REG0); ++ ++ /* power down PHY OTG part */ ++ val = readw(base + PHY_28NM_OTG_REG); ++ val &= ~PHY_28NM_OTG_PU_OTG; ++ writew(val, base + PHY_28NM_OTG_REG); ++ ++ clk_disable_unprepare(mv_phy->clk); ++ return 0; ++} ++ ++static const struct phy_ops usb_ops = { ++ .init = mv_usb2_phy_28nm_init, ++ .power_on = mv_usb2_phy_28nm_power_on, ++ .power_off = mv_usb2_phy_28nm_power_off, ++ .exit = mv_usb2_phy_28nm_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int mv_usb2_phy_probe(struct platform_device *pdev) ++{ ++ struct phy_provider *phy_provider; ++ struct mv_usb2_phy *mv_phy; ++ struct resource *r; ++ ++ mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); ++ if (!mv_phy) ++ return -ENOMEM; ++ ++ mv_phy->pdev = pdev; ++ ++ mv_phy->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(mv_phy->clk)) { ++ dev_err(&pdev->dev, "failed to get clock.\n"); ++ return PTR_ERR(mv_phy->clk); ++ } ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ mv_phy->base = devm_ioremap_resource(&pdev->dev, r); ++ if (IS_ERR(mv_phy->base)) ++ return PTR_ERR(mv_phy->base); ++ ++ mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &usb_ops); ++ if (IS_ERR(mv_phy->phy)) ++ return PTR_ERR(mv_phy->phy); ++ ++ phy_set_drvdata(mv_phy->phy, mv_phy); ++ ++ phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id mv_usbphy_dt_match[] = { ++ { .compatible = "marvell,pxa1928-usb-phy", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match); ++ ++static struct platform_driver mv_usb2_phy_driver = { ++ .probe = mv_usb2_phy_probe, ++ .driver = { ++ .name = "mv-usb2-phy", ++ .of_match_table = of_match_ptr(mv_usbphy_dt_match), ++ }, ++}; ++module_platform_driver(mv_usb2_phy_driver); ++ ++MODULE_AUTHOR("Rob Herring "); ++MODULE_DESCRIPTION("Marvell USB2 phy driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c +deleted file mode 100644 +index 1a3db288c0a9..000000000000 +--- a/drivers/phy/phy-armada375-usb2.c ++++ /dev/null +@@ -1,158 +0,0 @@ +-/* +- * USB cluster support for Armada 375 platform. +- * +- * Copyright (C) 2014 Marvell +- * +- * Gregory CLEMENT +- * +- * This file is licensed under the terms of the GNU General Public +- * License version 2 or later. This program is licensed "as is" +- * without any warranty of any kind, whether express or implied. +- * +- * Armada 375 comes with an USB2 host and device controller and an +- * USB3 controller. The USB cluster control register allows to manage +- * common features of both USB controllers. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define USB2_PHY_CONFIG_DISABLE BIT(0) +- +-struct armada375_cluster_phy { +- struct phy *phy; +- void __iomem *reg; +- bool use_usb3; +- int phy_provided; +-}; +- +-static int armada375_usb_phy_init(struct phy *phy) +-{ +- struct armada375_cluster_phy *cluster_phy; +- u32 reg; +- +- cluster_phy = phy_get_drvdata(phy); +- if (!cluster_phy) +- return -ENODEV; +- +- reg = readl(cluster_phy->reg); +- if (cluster_phy->use_usb3) +- reg |= USB2_PHY_CONFIG_DISABLE; +- else +- reg &= ~USB2_PHY_CONFIG_DISABLE; +- writel(reg, cluster_phy->reg); +- +- return 0; +-} +- +-static const struct phy_ops armada375_usb_phy_ops = { +- .init = armada375_usb_phy_init, +- .owner = THIS_MODULE, +-}; +- +-/* +- * Only one controller can use this PHY. We shouldn't have the case +- * when two controllers want to use this PHY. But if this case occurs +- * then we provide a phy to the first one and return an error for the +- * next one. This error has also to be an error returned by +- * devm_phy_optional_get() so different from ENODEV for USB2. In the +- * USB3 case it still optional and we use ENODEV. +- */ +-static struct phy *armada375_usb_phy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev); +- +- if (!cluster_phy) +- return ERR_PTR(-ENODEV); +- +- /* +- * Either the phy had never been requested and then the first +- * usb claiming it can get it, or it had already been +- * requested in this case, we only allow to use it with the +- * same configuration. +- */ +- if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) && +- (cluster_phy->phy_provided != args->args[0]))) { +- dev_err(dev, "This PHY has already been provided!\n"); +- dev_err(dev, "Check your device tree, only one controller can use it\n."); +- if (args->args[0] == PHY_TYPE_USB2) +- return ERR_PTR(-EBUSY); +- else +- return ERR_PTR(-ENODEV); +- } +- +- if (args->args[0] == PHY_TYPE_USB2) +- cluster_phy->use_usb3 = false; +- else if (args->args[0] == PHY_TYPE_USB3) +- cluster_phy->use_usb3 = true; +- else { +- dev_err(dev, "Invalid PHY mode\n"); +- return ERR_PTR(-ENODEV); +- } +- +- /* Store which phy mode is used for next test */ +- cluster_phy->phy_provided = args->args[0]; +- +- return cluster_phy->phy; +-} +- +-static int armada375_usb_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct phy *phy; +- struct phy_provider *phy_provider; +- void __iomem *usb_cluster_base; +- struct resource *res; +- struct armada375_cluster_phy *cluster_phy; +- +- cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL); +- if (!cluster_phy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- usb_cluster_base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(usb_cluster_base)) +- return PTR_ERR(usb_cluster_base); +- +- phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create PHY\n"); +- return PTR_ERR(phy); +- } +- +- cluster_phy->phy = phy; +- cluster_phy->reg = usb_cluster_base; +- +- dev_set_drvdata(dev, cluster_phy); +- phy_set_drvdata(phy, cluster_phy); +- +- phy_provider = devm_of_phy_provider_register(&pdev->dev, +- armada375_usb_phy_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id of_usb_cluster_table[] = { +- { .compatible = "marvell,armada-375-usb-cluster", }, +- { /* end of list */ }, +-}; +-MODULE_DEVICE_TABLE(of, of_usb_cluster_table); +- +-static struct platform_driver armada375_usb_phy_driver = { +- .probe = armada375_usb_phy_probe, +- .driver = { +- .of_match_table = of_usb_cluster_table, +- .name = "armada-375-usb-cluster", +- } +-}; +-module_platform_driver(armada375_usb_phy_driver); +- +-MODULE_DESCRIPTION("Armada 375 USB cluster driver"); +-MODULE_AUTHOR("Gregory CLEMENT "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/phy-bcm-cygnus-pcie.c b/drivers/phy/phy-bcm-cygnus-pcie.c +deleted file mode 100644 +index 0f4ac5d63cff..000000000000 +--- a/drivers/phy/phy-bcm-cygnus-pcie.c ++++ /dev/null +@@ -1,221 +0,0 @@ +-/* +- * Copyright (C) 2015 Broadcom Corporation +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation version 2. +- * +- * This program is distributed "as is" WITHOUT ANY WARRANTY of any +- * kind, whether express or implied; without even the implied warranty +- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define PCIE_CFG_OFFSET 0x00 +-#define PCIE1_PHY_IDDQ_SHIFT 10 +-#define PCIE0_PHY_IDDQ_SHIFT 2 +- +-enum cygnus_pcie_phy_id { +- CYGNUS_PHY_PCIE0 = 0, +- CYGNUS_PHY_PCIE1, +- MAX_NUM_PHYS, +-}; +- +-struct cygnus_pcie_phy_core; +- +-/** +- * struct cygnus_pcie_phy - Cygnus PCIe PHY device +- * @core: pointer to the Cygnus PCIe PHY core control +- * @id: internal ID to identify the Cygnus PCIe PHY +- * @phy: pointer to the kernel PHY device +- */ +-struct cygnus_pcie_phy { +- struct cygnus_pcie_phy_core *core; +- enum cygnus_pcie_phy_id id; +- struct phy *phy; +-}; +- +-/** +- * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control +- * @dev: pointer to device +- * @base: base register +- * @lock: mutex to protect access to individual PHYs +- * @phys: pointer to Cygnus PHY device +- */ +-struct cygnus_pcie_phy_core { +- struct device *dev; +- void __iomem *base; +- struct mutex lock; +- struct cygnus_pcie_phy phys[MAX_NUM_PHYS]; +-}; +- +-static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable) +-{ +- struct cygnus_pcie_phy_core *core = phy->core; +- unsigned shift; +- u32 val; +- +- mutex_lock(&core->lock); +- +- switch (phy->id) { +- case CYGNUS_PHY_PCIE0: +- shift = PCIE0_PHY_IDDQ_SHIFT; +- break; +- +- case CYGNUS_PHY_PCIE1: +- shift = PCIE1_PHY_IDDQ_SHIFT; +- break; +- +- default: +- mutex_unlock(&core->lock); +- dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id); +- return -EINVAL; +- } +- +- if (enable) { +- val = readl(core->base + PCIE_CFG_OFFSET); +- val &= ~BIT(shift); +- writel(val, core->base + PCIE_CFG_OFFSET); +- /* +- * Wait 50 ms for the PCIe Serdes to stabilize after the analog +- * front end is brought up +- */ +- msleep(50); +- } else { +- val = readl(core->base + PCIE_CFG_OFFSET); +- val |= BIT(shift); +- writel(val, core->base + PCIE_CFG_OFFSET); +- } +- +- mutex_unlock(&core->lock); +- dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id, +- enable ? "enabled" : "disabled"); +- return 0; +-} +- +-static int cygnus_pcie_phy_power_on(struct phy *p) +-{ +- struct cygnus_pcie_phy *phy = phy_get_drvdata(p); +- +- return cygnus_pcie_power_config(phy, true); +-} +- +-static int cygnus_pcie_phy_power_off(struct phy *p) +-{ +- struct cygnus_pcie_phy *phy = phy_get_drvdata(p); +- +- return cygnus_pcie_power_config(phy, false); +-} +- +-static const struct phy_ops cygnus_pcie_phy_ops = { +- .power_on = cygnus_pcie_phy_power_on, +- .power_off = cygnus_pcie_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int cygnus_pcie_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct device_node *node = dev->of_node, *child; +- struct cygnus_pcie_phy_core *core; +- struct phy_provider *provider; +- struct resource *res; +- unsigned cnt = 0; +- int ret; +- +- if (of_get_child_count(node) == 0) { +- dev_err(dev, "PHY no child node\n"); +- return -ENODEV; +- } +- +- core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); +- if (!core) +- return -ENOMEM; +- +- core->dev = dev; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- core->base = devm_ioremap_resource(dev, res); +- if (IS_ERR(core->base)) +- return PTR_ERR(core->base); +- +- mutex_init(&core->lock); +- +- for_each_available_child_of_node(node, child) { +- unsigned int id; +- struct cygnus_pcie_phy *p; +- +- if (of_property_read_u32(child, "reg", &id)) { +- dev_err(dev, "missing reg property for %s\n", +- child->name); +- ret = -EINVAL; +- goto put_child; +- } +- +- if (id >= MAX_NUM_PHYS) { +- dev_err(dev, "invalid PHY id: %u\n", id); +- ret = -EINVAL; +- goto put_child; +- } +- +- if (core->phys[id].phy) { +- dev_err(dev, "duplicated PHY id: %u\n", id); +- ret = -EINVAL; +- goto put_child; +- } +- +- p = &core->phys[id]; +- p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops); +- if (IS_ERR(p->phy)) { +- dev_err(dev, "failed to create PHY\n"); +- ret = PTR_ERR(p->phy); +- goto put_child; +- } +- +- p->core = core; +- p->id = id; +- phy_set_drvdata(p->phy, p); +- cnt++; +- } +- +- dev_set_drvdata(dev, core); +- +- provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (IS_ERR(provider)) { +- dev_err(dev, "failed to register PHY provider\n"); +- return PTR_ERR(provider); +- } +- +- dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt); +- +- return 0; +-put_child: +- of_node_put(child); +- return ret; +-} +- +-static const struct of_device_id cygnus_pcie_phy_match_table[] = { +- { .compatible = "brcm,cygnus-pcie-phy" }, +- { /* sentinel */ } +-}; +-MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table); +- +-static struct platform_driver cygnus_pcie_phy_driver = { +- .driver = { +- .name = "cygnus-pcie-phy", +- .of_match_table = cygnus_pcie_phy_match_table, +- }, +- .probe = cygnus_pcie_phy_probe, +-}; +-module_platform_driver(cygnus_pcie_phy_driver); +- +-MODULE_AUTHOR("Ray Jui "); +-MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c +deleted file mode 100644 +index 7b67fe49e30b..000000000000 +--- a/drivers/phy/phy-bcm-kona-usb2.c ++++ /dev/null +@@ -1,155 +0,0 @@ +-/* +- * phy-bcm-kona-usb2.c - Broadcom Kona USB2 Phy Driver +- * +- * Copyright (C) 2013 Linaro Limited +- * Matt Porter +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define OTGCTL (0) +-#define OTGCTL_OTGSTAT2 BIT(31) +-#define OTGCTL_OTGSTAT1 BIT(30) +-#define OTGCTL_PRST_N_SW BIT(11) +-#define OTGCTL_HRESET_N BIT(10) +-#define OTGCTL_UTMI_LINE_STATE1 BIT(9) +-#define OTGCTL_UTMI_LINE_STATE0 BIT(8) +- +-#define P1CTL (8) +-#define P1CTL_SOFT_RESET BIT(1) +-#define P1CTL_NON_DRIVING BIT(0) +- +-struct bcm_kona_usb { +- void __iomem *regs; +-}; +- +-static void bcm_kona_usb_phy_power(struct bcm_kona_usb *phy, int on) +-{ +- u32 val; +- +- val = readl(phy->regs + OTGCTL); +- if (on) { +- /* Configure and power PHY */ +- val &= ~(OTGCTL_OTGSTAT2 | OTGCTL_OTGSTAT1 | +- OTGCTL_UTMI_LINE_STATE1 | OTGCTL_UTMI_LINE_STATE0); +- val |= OTGCTL_PRST_N_SW | OTGCTL_HRESET_N; +- } else { +- val &= ~(OTGCTL_PRST_N_SW | OTGCTL_HRESET_N); +- } +- writel(val, phy->regs + OTGCTL); +-} +- +-static int bcm_kona_usb_phy_init(struct phy *gphy) +-{ +- struct bcm_kona_usb *phy = phy_get_drvdata(gphy); +- u32 val; +- +- /* Soft reset PHY */ +- val = readl(phy->regs + P1CTL); +- val &= ~P1CTL_NON_DRIVING; +- val |= P1CTL_SOFT_RESET; +- writel(val, phy->regs + P1CTL); +- writel(val & ~P1CTL_SOFT_RESET, phy->regs + P1CTL); +- /* Reset needs to be asserted for 2ms */ +- mdelay(2); +- writel(val | P1CTL_SOFT_RESET, phy->regs + P1CTL); +- +- return 0; +-} +- +-static int bcm_kona_usb_phy_power_on(struct phy *gphy) +-{ +- struct bcm_kona_usb *phy = phy_get_drvdata(gphy); +- +- bcm_kona_usb_phy_power(phy, 1); +- +- return 0; +-} +- +-static int bcm_kona_usb_phy_power_off(struct phy *gphy) +-{ +- struct bcm_kona_usb *phy = phy_get_drvdata(gphy); +- +- bcm_kona_usb_phy_power(phy, 0); +- +- return 0; +-} +- +-static const struct phy_ops ops = { +- .init = bcm_kona_usb_phy_init, +- .power_on = bcm_kona_usb_phy_power_on, +- .power_off = bcm_kona_usb_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int bcm_kona_usb2_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct bcm_kona_usb *phy; +- struct resource *res; +- struct phy *gphy; +- struct phy_provider *phy_provider; +- +- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- phy->regs = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(phy->regs)) +- return PTR_ERR(phy->regs); +- +- platform_set_drvdata(pdev, phy); +- +- gphy = devm_phy_create(dev, NULL, &ops); +- if (IS_ERR(gphy)) +- return PTR_ERR(gphy); +- +- /* The Kona PHY supports an 8-bit wide UTMI interface */ +- phy_set_bus_width(gphy, 8); +- +- phy_set_drvdata(gphy, phy); +- +- phy_provider = devm_of_phy_provider_register(dev, +- of_phy_simple_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id bcm_kona_usb2_dt_ids[] = { +- { .compatible = "brcm,kona-usb2-phy" }, +- { /* sentinel */ } +-}; +- +-MODULE_DEVICE_TABLE(of, bcm_kona_usb2_dt_ids); +- +-static struct platform_driver bcm_kona_usb2_driver = { +- .probe = bcm_kona_usb2_probe, +- .driver = { +- .name = "bcm-kona-usb2", +- .of_match_table = bcm_kona_usb2_dt_ids, +- }, +-}; +- +-module_platform_driver(bcm_kona_usb2_driver); +- +-MODULE_ALIAS("platform:bcm-kona-usb2"); +-MODULE_AUTHOR("Matt Porter "); +-MODULE_DESCRIPTION("BCM Kona USB 2.0 PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-bcm-ns-usb2.c b/drivers/phy/phy-bcm-ns-usb2.c +deleted file mode 100644 +index 58dff80e9386..000000000000 +--- a/drivers/phy/phy-bcm-ns-usb2.c ++++ /dev/null +@@ -1,137 +0,0 @@ +-/* +- * Broadcom Northstar USB 2.0 PHY Driver +- * +- * Copyright (C) 2016 RafaÅ‚ MiÅ‚ecki +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-struct bcm_ns_usb2 { +- struct device *dev; +- struct clk *ref_clk; +- struct phy *phy; +- void __iomem *dmu; +-}; +- +-static int bcm_ns_usb2_phy_init(struct phy *phy) +-{ +- struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy); +- struct device *dev = usb2->dev; +- void __iomem *dmu = usb2->dmu; +- u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv; +- int err = 0; +- +- err = clk_prepare_enable(usb2->ref_clk); +- if (err < 0) { +- dev_err(dev, "Failed to prepare ref clock: %d\n", err); +- goto err_out; +- } +- +- ref_clk_rate = clk_get_rate(usb2->ref_clk); +- if (!ref_clk_rate) { +- dev_err(dev, "Failed to get ref clock rate\n"); +- err = -EINVAL; +- goto err_clk_off; +- } +- +- usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL); +- +- if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { +- usb_pll_pdiv = usb2ctl; +- usb_pll_pdiv &= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK; +- usb_pll_pdiv >>= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT; +- } else { +- usb_pll_pdiv = 1 << 3; +- } +- +- /* Calculate ndiv based on a solid 1920 MHz that is for USB2 PHY */ +- usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; +- +- /* Unlock DMU PLL settings with some magic value */ +- writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY); +- +- /* Write USB 2.0 PLL control setting */ +- usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; +- usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; +- writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL); +- +- /* Lock DMU PLL settings */ +- writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY); +- +-err_clk_off: +- clk_disable_unprepare(usb2->ref_clk); +-err_out: +- return err; +-} +- +-static const struct phy_ops ops = { +- .init = bcm_ns_usb2_phy_init, +- .owner = THIS_MODULE, +-}; +- +-static int bcm_ns_usb2_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct bcm_ns_usb2 *usb2; +- struct resource *res; +- struct phy_provider *phy_provider; +- +- usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL); +- if (!usb2) +- return -ENOMEM; +- usb2->dev = dev; +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu"); +- usb2->dmu = devm_ioremap_resource(dev, res); +- if (IS_ERR(usb2->dmu)) { +- dev_err(dev, "Failed to map DMU regs\n"); +- return PTR_ERR(usb2->dmu); +- } +- +- usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); +- if (IS_ERR(usb2->ref_clk)) { +- dev_err(dev, "Clock not defined\n"); +- return PTR_ERR(usb2->ref_clk); +- } +- +- usb2->phy = devm_phy_create(dev, NULL, &ops); +- if (IS_ERR(usb2->phy)) +- return PTR_ERR(usb2->phy); +- +- phy_set_drvdata(usb2->phy, usb2); +- platform_set_drvdata(pdev, usb2); +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id bcm_ns_usb2_id_table[] = { +- { .compatible = "brcm,ns-usb2-phy", }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table); +- +-static struct platform_driver bcm_ns_usb2_driver = { +- .probe = bcm_ns_usb2_probe, +- .driver = { +- .name = "bcm_ns_usb2", +- .of_match_table = bcm_ns_usb2_id_table, +- }, +-}; +-module_platform_driver(bcm_ns_usb2_driver); +- +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-bcm-ns-usb3.c b/drivers/phy/phy-bcm-ns-usb3.c +deleted file mode 100644 +index 22b5e7047fa6..000000000000 +--- a/drivers/phy/phy-bcm-ns-usb3.c ++++ /dev/null +@@ -1,303 +0,0 @@ +-/* +- * Broadcom Northstar USB 3.0 PHY Driver +- * +- * Copyright (C) 2016 RafaÅ‚ MiÅ‚ecki +- * Copyright (C) 2016 Broadcom +- * +- * All magic values used for initialization (and related comments) were obtained +- * from Broadcom's SDK: +- * Copyright (c) Broadcom Corp, 2012 +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */ +- +-#define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f +-#define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000 +-#define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040 +-#define BCM_NS_USB3_PHY_PIPE_BLOCK 0x8060 +- +-/* Registers of PLL30 block */ +-#define BCM_NS_USB3_PLL_CONTROL 0x01 +-#define BCM_NS_USB3_PLLA_CONTROL0 0x0a +-#define BCM_NS_USB3_PLLA_CONTROL1 0x0b +- +-/* Registers of TX PMD block */ +-#define BCM_NS_USB3_TX_PMD_CONTROL1 0x01 +- +-/* Registers of PIPE block */ +-#define BCM_NS_USB3_LFPS_CMP 0x02 +-#define BCM_NS_USB3_LFPS_DEGLITCH 0x03 +- +-enum bcm_ns_family { +- BCM_NS_UNKNOWN, +- BCM_NS_AX, +- BCM_NS_BX, +-}; +- +-struct bcm_ns_usb3 { +- struct device *dev; +- enum bcm_ns_family family; +- void __iomem *dmp; +- void __iomem *ccb_mii; +- struct phy *phy; +-}; +- +-static const struct of_device_id bcm_ns_usb3_id_table[] = { +- { +- .compatible = "brcm,ns-ax-usb3-phy", +- .data = (int *)BCM_NS_AX, +- }, +- { +- .compatible = "brcm,ns-bx-usb3-phy", +- .data = (int *)BCM_NS_BX, +- }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); +- +-static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr, +- u32 mask, u32 value, unsigned long timeout) +-{ +- unsigned long deadline = jiffies + timeout; +- u32 val; +- +- do { +- val = readl(addr); +- if ((val & mask) == value) +- return 0; +- cpu_relax(); +- udelay(10); +- } while (!time_after_eq(jiffies, deadline)); +- +- dev_err(usb3->dev, "Timeout waiting for register %p\n", addr); +- +- return -EBUSY; +-} +- +-static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3) +-{ +- return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL, +- 0x0100, 0x0000, +- usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US)); +-} +- +-static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, +- u16 value) +-{ +- u32 tmp = 0; +- int err; +- +- err = bcm_ns_usb3_mii_mng_wait_idle(usb3); +- if (err < 0) { +- dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value); +- return err; +- } +- +- /* TODO: Use a proper MDIO bus layer */ +- tmp |= 0x58020000; /* Magic value for MDIO PHY write */ +- tmp |= reg << 18; +- tmp |= value; +- writel(tmp, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA); +- +- return 0; +-} +- +-static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3) +-{ +- int err; +- +- /* Enable MDIO. Setting MDCDIV as 26 */ +- writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); +- +- /* Wait for MDIO? */ +- udelay(2); +- +- /* USB3 PLL Block */ +- err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, +- BCM_NS_USB3_PHY_PLL30_BLOCK); +- if (err < 0) +- return err; +- +- /* Assert Ana_Pllseq start */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x1000); +- +- /* Assert CML Divider ratio to 26 */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); +- +- /* Asserting PLL Reset */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0xc000); +- +- /* Deaaserting PLL Reset */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0x8000); +- +- /* Waiting MII Mgt interface idle */ +- bcm_ns_usb3_mii_mng_wait_idle(usb3); +- +- /* Deasserting USB3 system reset */ +- writel(0, usb3->dmp + BCMA_RESET_CTL); +- +- /* PLL frequency monitor enable */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x9000); +- +- /* PIPE Block */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, +- BCM_NS_USB3_PHY_PIPE_BLOCK); +- +- /* CMPMAX & CMPMINTH setting */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, 0xf30d); +- +- /* DEGLITCH MIN & MAX setting */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, 0x6302); +- +- /* TXPMD block */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, +- BCM_NS_USB3_PHY_TX_PMD_BLOCK); +- +- /* Enabling SSC */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); +- +- /* Waiting MII Mgt interface idle */ +- bcm_ns_usb3_mii_mng_wait_idle(usb3); +- +- return 0; +-} +- +-static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3) +-{ +- int err; +- +- /* Enable MDIO. Setting MDCDIV as 26 */ +- writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); +- +- /* Wait for MDIO? */ +- udelay(2); +- +- /* PLL30 block */ +- err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, +- BCM_NS_USB3_PHY_PLL30_BLOCK); +- if (err < 0) +- return err; +- +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); +- +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 0x80e0); +- +- bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x009c); +- +- /* Enable SSC */ +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, +- BCM_NS_USB3_PHY_TX_PMD_BLOCK); +- +- bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x21d3); +- +- bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); +- +- /* Waiting MII Mgt interface idle */ +- bcm_ns_usb3_mii_mng_wait_idle(usb3); +- +- /* Deasserting USB3 system reset */ +- writel(0, usb3->dmp + BCMA_RESET_CTL); +- +- return 0; +-} +- +-static int bcm_ns_usb3_phy_init(struct phy *phy) +-{ +- struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy); +- int err; +- +- /* Perform USB3 system soft reset */ +- writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL); +- +- switch (usb3->family) { +- case BCM_NS_AX: +- err = bcm_ns_usb3_phy_init_ns_ax(usb3); +- break; +- case BCM_NS_BX: +- err = bcm_ns_usb3_phy_init_ns_bx(usb3); +- break; +- default: +- WARN_ON(1); +- err = -ENOTSUPP; +- } +- +- return err; +-} +- +-static const struct phy_ops ops = { +- .init = bcm_ns_usb3_phy_init, +- .owner = THIS_MODULE, +-}; +- +-static int bcm_ns_usb3_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- const struct of_device_id *of_id; +- struct bcm_ns_usb3 *usb3; +- struct resource *res; +- struct phy_provider *phy_provider; +- +- usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL); +- if (!usb3) +- return -ENOMEM; +- +- usb3->dev = dev; +- +- of_id = of_match_device(bcm_ns_usb3_id_table, dev); +- if (!of_id) +- return -EINVAL; +- usb3->family = (enum bcm_ns_family)of_id->data; +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp"); +- usb3->dmp = devm_ioremap_resource(dev, res); +- if (IS_ERR(usb3->dmp)) { +- dev_err(dev, "Failed to map DMP regs\n"); +- return PTR_ERR(usb3->dmp); +- } +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii"); +- usb3->ccb_mii = devm_ioremap_resource(dev, res); +- if (IS_ERR(usb3->ccb_mii)) { +- dev_err(dev, "Failed to map ChipCommon B MII regs\n"); +- return PTR_ERR(usb3->ccb_mii); +- } +- +- usb3->phy = devm_phy_create(dev, NULL, &ops); +- if (IS_ERR(usb3->phy)) { +- dev_err(dev, "Failed to create PHY\n"); +- return PTR_ERR(usb3->phy); +- } +- +- phy_set_drvdata(usb3->phy, usb3); +- platform_set_drvdata(pdev, usb3); +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (!IS_ERR(phy_provider)) +- dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n"); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static struct platform_driver bcm_ns_usb3_driver = { +- .probe = bcm_ns_usb3_probe, +- .driver = { +- .name = "bcm_ns_usb3", +- .of_match_table = bcm_ns_usb3_id_table, +- }, +-}; +-module_platform_driver(bcm_ns_usb3_driver); +- +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-bcm-ns2-pcie.c b/drivers/phy/phy-bcm-ns2-pcie.c +deleted file mode 100644 +index 4c7d11d2b378..000000000000 +--- a/drivers/phy/phy-bcm-ns2-pcie.c ++++ /dev/null +@@ -1,101 +0,0 @@ +-/* +- * Copyright (C) 2016 Broadcom +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation version 2. +- * +- * This program is distributed "as is" WITHOUT ANY WARRANTY of any +- * kind, whether express or implied; without even the implied warranty +- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define BLK_ADDR_REG_OFFSET 0x1f +-#define PLL_AFE1_100MHZ_BLK 0x2100 +-#define PLL_CLK_AMP_OFFSET 0x03 +-#define PLL_CLK_AMP_2P05V 0x2b18 +- +-static int ns2_pci_phy_init(struct phy *p) +-{ +- struct mdio_device *mdiodev = phy_get_drvdata(p); +- int rc; +- +- /* select the AFE 100MHz block page */ +- rc = mdiobus_write(mdiodev->bus, mdiodev->addr, +- BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK); +- if (rc) +- goto err; +- +- /* set the 100 MHz reference clock amplitude to 2.05 v */ +- rc = mdiobus_write(mdiodev->bus, mdiodev->addr, +- PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V); +- if (rc) +- goto err; +- +- return 0; +- +-err: +- dev_err(&mdiodev->dev, "Error %d writing to phy\n", rc); +- return rc; +-} +- +-static const struct phy_ops ns2_pci_phy_ops = { +- .init = ns2_pci_phy_init, +- .owner = THIS_MODULE, +-}; +- +-static int ns2_pci_phy_probe(struct mdio_device *mdiodev) +-{ +- struct device *dev = &mdiodev->dev; +- struct phy_provider *provider; +- struct phy *phy; +- +- phy = devm_phy_create(dev, dev->of_node, &ns2_pci_phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create Phy\n"); +- return PTR_ERR(phy); +- } +- +- phy_set_drvdata(phy, mdiodev); +- +- provider = devm_of_phy_provider_register(&phy->dev, +- of_phy_simple_xlate); +- if (IS_ERR(provider)) { +- dev_err(dev, "failed to register Phy provider\n"); +- return PTR_ERR(provider); +- } +- +- dev_info(dev, "%s PHY registered\n", dev_name(dev)); +- +- return 0; +-} +- +-static const struct of_device_id ns2_pci_phy_of_match[] = { +- { .compatible = "brcm,ns2-pcie-phy", }, +- { /* sentinel */ }, +-}; +-MODULE_DEVICE_TABLE(of, ns2_pci_phy_of_match); +- +-static struct mdio_driver ns2_pci_phy_driver = { +- .mdiodrv = { +- .driver = { +- .name = "phy-bcm-ns2-pci", +- .of_match_table = ns2_pci_phy_of_match, +- }, +- }, +- .probe = ns2_pci_phy_probe, +-}; +-mdio_module_driver(ns2_pci_phy_driver); +- +-MODULE_AUTHOR("Broadcom"); +-MODULE_DESCRIPTION("Broadcom Northstar2 PCI Phy driver"); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:phy-bcm-ns2-pci"); +diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c +deleted file mode 100644 +index 2c7a57f2d595..000000000000 +--- a/drivers/phy/phy-berlin-sata.c ++++ /dev/null +@@ -1,299 +0,0 @@ +-/* +- * Marvell Berlin SATA PHY driver +- * +- * Copyright (C) 2014 Marvell Technology Group Ltd. +- * +- * Antoine Ténart +- * +- * This file is licensed under the terms of the GNU General Public +- * License version 2. This program is licensed "as is" without any +- * warranty of any kind, whether express or implied. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#define HOST_VSA_ADDR 0x0 +-#define HOST_VSA_DATA 0x4 +-#define PORT_SCR_CTL 0x2c +-#define PORT_VSR_ADDR 0x78 +-#define PORT_VSR_DATA 0x7c +- +-#define CONTROL_REGISTER 0x0 +-#define MBUS_SIZE_CONTROL 0x4 +- +-#define POWER_DOWN_PHY0 BIT(6) +-#define POWER_DOWN_PHY1 BIT(14) +-#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) +-#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) +- +-#define BG2_PHY_BASE 0x080 +-#define BG2Q_PHY_BASE 0x200 +- +-/* register 0x01 */ +-#define REF_FREF_SEL_25 BIT(0) +-#define PHY_MODE_SATA (0x0 << 5) +- +-/* register 0x02 */ +-#define USE_MAX_PLL_RATE BIT(12) +- +-/* register 0x23 */ +-#define DATA_BIT_WIDTH_10 (0x0 << 10) +-#define DATA_BIT_WIDTH_20 (0x1 << 10) +-#define DATA_BIT_WIDTH_40 (0x2 << 10) +- +-/* register 0x25 */ +-#define PHY_GEN_MAX_1_5 (0x0 << 10) +-#define PHY_GEN_MAX_3_0 (0x1 << 10) +-#define PHY_GEN_MAX_6_0 (0x2 << 10) +- +-struct phy_berlin_desc { +- struct phy *phy; +- u32 power_bit; +- unsigned index; +-}; +- +-struct phy_berlin_priv { +- void __iomem *base; +- spinlock_t lock; +- struct clk *clk; +- struct phy_berlin_desc **phys; +- unsigned nphys; +- u32 phy_base; +-}; +- +-static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, +- u32 phy_base, u32 reg, u32 mask, u32 val) +-{ +- u32 regval; +- +- /* select register */ +- writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR); +- +- /* set bits */ +- regval = readl(ctrl_reg + PORT_VSR_DATA); +- regval &= ~mask; +- regval |= val; +- writel(regval, ctrl_reg + PORT_VSR_DATA); +-} +- +-static int phy_berlin_sata_power_on(struct phy *phy) +-{ +- struct phy_berlin_desc *desc = phy_get_drvdata(phy); +- struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); +- void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); +- u32 regval; +- +- clk_prepare_enable(priv->clk); +- +- spin_lock(&priv->lock); +- +- /* Power on PHY */ +- writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); +- regval = readl(priv->base + HOST_VSA_DATA); +- regval &= ~desc->power_bit; +- writel(regval, priv->base + HOST_VSA_DATA); +- +- /* Configure MBus */ +- writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR); +- regval = readl(priv->base + HOST_VSA_DATA); +- regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; +- writel(regval, priv->base + HOST_VSA_DATA); +- +- /* set PHY mode and ref freq to 25 MHz */ +- phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01, +- 0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA); +- +- /* set PHY up to 6 Gbps */ +- phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25, +- 0x0c00, PHY_GEN_MAX_6_0); +- +- /* set 40 bits width */ +- phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23, +- 0x0c00, DATA_BIT_WIDTH_40); +- +- /* use max pll rate */ +- phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02, +- 0x0000, USE_MAX_PLL_RATE); +- +- /* set Gen3 controller speed */ +- regval = readl(ctrl_reg + PORT_SCR_CTL); +- regval &= ~GENMASK(7, 4); +- regval |= 0x30; +- writel(regval, ctrl_reg + PORT_SCR_CTL); +- +- spin_unlock(&priv->lock); +- +- clk_disable_unprepare(priv->clk); +- +- return 0; +-} +- +-static int phy_berlin_sata_power_off(struct phy *phy) +-{ +- struct phy_berlin_desc *desc = phy_get_drvdata(phy); +- struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); +- u32 regval; +- +- clk_prepare_enable(priv->clk); +- +- spin_lock(&priv->lock); +- +- /* Power down PHY */ +- writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); +- regval = readl(priv->base + HOST_VSA_DATA); +- regval |= desc->power_bit; +- writel(regval, priv->base + HOST_VSA_DATA); +- +- spin_unlock(&priv->lock); +- +- clk_disable_unprepare(priv->clk); +- +- return 0; +-} +- +-static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct phy_berlin_priv *priv = dev_get_drvdata(dev); +- int i; +- +- if (WARN_ON(args->args[0] >= priv->nphys)) +- return ERR_PTR(-ENODEV); +- +- for (i = 0; i < priv->nphys; i++) { +- if (priv->phys[i]->index == args->args[0]) +- break; +- } +- +- if (i == priv->nphys) +- return ERR_PTR(-ENODEV); +- +- return priv->phys[i]->phy; +-} +- +-static const struct phy_ops phy_berlin_sata_ops = { +- .power_on = phy_berlin_sata_power_on, +- .power_off = phy_berlin_sata_power_off, +- .owner = THIS_MODULE, +-}; +- +-static u32 phy_berlin_power_down_bits[] = { +- POWER_DOWN_PHY0, +- POWER_DOWN_PHY1, +-}; +- +-static int phy_berlin_sata_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct device_node *child; +- struct phy *phy; +- struct phy_provider *phy_provider; +- struct phy_berlin_priv *priv; +- struct resource *res; +- int ret, i = 0; +- u32 phy_id; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) +- return -EINVAL; +- +- priv->base = devm_ioremap(dev, res->start, resource_size(res)); +- if (!priv->base) +- return -ENOMEM; +- +- priv->clk = devm_clk_get(dev, NULL); +- if (IS_ERR(priv->clk)) +- return PTR_ERR(priv->clk); +- +- priv->nphys = of_get_child_count(dev->of_node); +- if (priv->nphys == 0) +- return -ENODEV; +- +- priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys), +- GFP_KERNEL); +- if (!priv->phys) +- return -ENOMEM; +- +- if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy")) +- priv->phy_base = BG2_PHY_BASE; +- else +- priv->phy_base = BG2Q_PHY_BASE; +- +- dev_set_drvdata(dev, priv); +- spin_lock_init(&priv->lock); +- +- for_each_available_child_of_node(dev->of_node, child) { +- struct phy_berlin_desc *phy_desc; +- +- if (of_property_read_u32(child, "reg", &phy_id)) { +- dev_err(dev, "missing reg property in node %s\n", +- child->name); +- ret = -EINVAL; +- goto put_child; +- } +- +- if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { +- dev_err(dev, "invalid reg in node %s\n", child->name); +- ret = -EINVAL; +- goto put_child; +- } +- +- phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL); +- if (!phy_desc) { +- ret = -ENOMEM; +- goto put_child; +- } +- +- phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create PHY %d\n", phy_id); +- ret = PTR_ERR(phy); +- goto put_child; +- } +- +- phy_desc->phy = phy; +- phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; +- phy_desc->index = phy_id; +- phy_set_drvdata(phy, phy_desc); +- +- priv->phys[i++] = phy_desc; +- +- /* Make sure the PHY is off */ +- phy_berlin_sata_power_off(phy); +- } +- +- phy_provider = +- devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-put_child: +- of_node_put(child); +- return ret; +-} +- +-static const struct of_device_id phy_berlin_sata_of_match[] = { +- { .compatible = "marvell,berlin2-sata-phy" }, +- { .compatible = "marvell,berlin2q-sata-phy" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); +- +-static struct platform_driver phy_berlin_sata_driver = { +- .probe = phy_berlin_sata_probe, +- .driver = { +- .name = "phy-berlin-sata", +- .of_match_table = phy_berlin_sata_of_match, +- }, +-}; +-module_platform_driver(phy_berlin_sata_driver); +- +-MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver"); +-MODULE_AUTHOR("Antoine Ténart "); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c +deleted file mode 100644 +index 2017751ede26..000000000000 +--- a/drivers/phy/phy-berlin-usb.c ++++ /dev/null +@@ -1,214 +0,0 @@ +-/* +- * Copyright (C) 2014 Marvell Technology Group Ltd. +- * +- * Antoine Tenart +- * Jisheng Zhang +- * +- * This file is licensed under the terms of the GNU General Public +- * License version 2. This program is licensed "as is" without any +- * warranty of any kind, whether express or implied. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define USB_PHY_PLL 0x04 +-#define USB_PHY_PLL_CONTROL 0x08 +-#define USB_PHY_TX_CTRL0 0x10 +-#define USB_PHY_TX_CTRL1 0x14 +-#define USB_PHY_TX_CTRL2 0x18 +-#define USB_PHY_RX_CTRL 0x20 +-#define USB_PHY_ANALOG 0x34 +- +-/* USB_PHY_PLL */ +-#define CLK_REF_DIV(x) ((x) << 4) +-#define FEEDBACK_CLK_DIV(x) ((x) << 8) +- +-/* USB_PHY_PLL_CONTROL */ +-#define CLK_STABLE BIT(0) +-#define PLL_CTRL_PIN BIT(1) +-#define PLL_CTRL_REG BIT(2) +-#define PLL_ON BIT(3) +-#define PHASE_OFF_TOL_125 (0x0 << 5) +-#define PHASE_OFF_TOL_250 BIT(5) +-#define KVC0_CALIB (0x0 << 9) +-#define KVC0_REG_CTRL BIT(9) +-#define KVC0_HIGH (0x0 << 10) +-#define KVC0_LOW (0x3 << 10) +-#define CLK_BLK_EN BIT(13) +- +-/* USB_PHY_TX_CTRL0 */ +-#define EXT_HS_RCAL_EN BIT(3) +-#define EXT_FS_RCAL_EN BIT(4) +-#define IMPCAL_VTH_DIV(x) ((x) << 5) +-#define EXT_RS_RCAL_DIV(x) ((x) << 8) +-#define EXT_FS_RCAL_DIV(x) ((x) << 12) +- +-/* USB_PHY_TX_CTRL1 */ +-#define TX_VDD15_14 (0x0 << 4) +-#define TX_VDD15_15 BIT(4) +-#define TX_VDD15_16 (0x2 << 4) +-#define TX_VDD15_17 (0x3 << 4) +-#define TX_VDD12_VDD (0x0 << 6) +-#define TX_VDD12_11 BIT(6) +-#define TX_VDD12_12 (0x2 << 6) +-#define TX_VDD12_13 (0x3 << 6) +-#define LOW_VDD_EN BIT(8) +-#define TX_OUT_AMP(x) ((x) << 9) +- +-/* USB_PHY_TX_CTRL2 */ +-#define TX_CHAN_CTRL_REG(x) ((x) << 0) +-#define DRV_SLEWRATE(x) ((x) << 4) +-#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6) +-#define IMP_CAL_FS_HS_DLY_1 BIT(6) +-#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6) +-#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6) +-#define FS_DRV_EN_MASK(x) ((x) << 8) +-#define HS_DRV_EN_MASK(x) ((x) << 12) +- +-/* USB_PHY_RX_CTRL */ +-#define PHASE_FREEZE_DLY_2_CL (0x0 << 0) +-#define PHASE_FREEZE_DLY_4_CL BIT(0) +-#define ACK_LENGTH_8_CL (0x0 << 2) +-#define ACK_LENGTH_12_CL BIT(2) +-#define ACK_LENGTH_16_CL (0x2 << 2) +-#define ACK_LENGTH_20_CL (0x3 << 2) +-#define SQ_LENGTH_3 (0x0 << 4) +-#define SQ_LENGTH_6 BIT(4) +-#define SQ_LENGTH_9 (0x2 << 4) +-#define SQ_LENGTH_12 (0x3 << 4) +-#define DISCON_THRESHOLD_260 (0x0 << 6) +-#define DISCON_THRESHOLD_270 BIT(6) +-#define DISCON_THRESHOLD_280 (0x2 << 6) +-#define DISCON_THRESHOLD_290 (0x3 << 6) +-#define SQ_THRESHOLD(x) ((x) << 8) +-#define LPF_COEF(x) ((x) << 12) +-#define INTPL_CUR_10 (0x0 << 14) +-#define INTPL_CUR_20 BIT(14) +-#define INTPL_CUR_30 (0x2 << 14) +-#define INTPL_CUR_40 (0x3 << 14) +- +-/* USB_PHY_ANALOG */ +-#define ANA_PWR_UP BIT(1) +-#define ANA_PWR_DOWN BIT(2) +-#define V2I_VCO_RATIO(x) ((x) << 7) +-#define R_ROTATE_90 (0x0 << 10) +-#define R_ROTATE_0 BIT(10) +-#define MODE_TEST_EN BIT(11) +-#define ANA_TEST_DC_CTRL(x) ((x) << 12) +- +-static const u32 phy_berlin_pll_dividers[] = { +- /* Berlin 2 */ +- CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), +- /* Berlin 2CD/Q */ +- CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), +-}; +- +-struct phy_berlin_usb_priv { +- void __iomem *base; +- struct reset_control *rst_ctrl; +- u32 pll_divider; +-}; +- +-static int phy_berlin_usb_power_on(struct phy *phy) +-{ +- struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy); +- +- reset_control_reset(priv->rst_ctrl); +- +- writel(priv->pll_divider, +- priv->base + USB_PHY_PLL); +- writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL | +- CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL); +- writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), +- priv->base + USB_PHY_ANALOG); +- writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | +- DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | +- INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); +- +- writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); +- writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), +- priv->base + USB_PHY_TX_CTRL0); +- +- writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) | +- EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0); +- +- writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), +- priv->base + USB_PHY_TX_CTRL0); +- writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 | +- FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2); +- +- return 0; +-} +- +-static const struct phy_ops phy_berlin_usb_ops = { +- .power_on = phy_berlin_usb_power_on, +- .owner = THIS_MODULE, +-}; +- +-static const struct of_device_id phy_berlin_usb_of_match[] = { +- { +- .compatible = "marvell,berlin2-usb-phy", +- .data = &phy_berlin_pll_dividers[0], +- }, +- { +- .compatible = "marvell,berlin2cd-usb-phy", +- .data = &phy_berlin_pll_dividers[1], +- }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, phy_berlin_usb_of_match); +- +-static int phy_berlin_usb_probe(struct platform_device *pdev) +-{ +- const struct of_device_id *match = +- of_match_device(phy_berlin_usb_of_match, &pdev->dev); +- struct phy_berlin_usb_priv *priv; +- struct resource *res; +- struct phy *phy; +- struct phy_provider *phy_provider; +- +- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(priv->base)) +- return PTR_ERR(priv->base); +- +- priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL); +- if (IS_ERR(priv->rst_ctrl)) +- return PTR_ERR(priv->rst_ctrl); +- +- priv->pll_divider = *((u32 *)match->data); +- +- phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); +- if (IS_ERR(phy)) { +- dev_err(&pdev->dev, "failed to create PHY\n"); +- return PTR_ERR(phy); +- } +- +- phy_set_drvdata(phy, priv); +- +- phy_provider = +- devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static struct platform_driver phy_berlin_usb_driver = { +- .probe = phy_berlin_usb_probe, +- .driver = { +- .name = "phy-berlin-usb", +- .of_match_table = phy_berlin_usb_of_match, +- }, +-}; +-module_platform_driver(phy_berlin_usb_driver); +- +-MODULE_AUTHOR("Antoine Tenart "); +-MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/phy-brcm-sata.c b/drivers/phy/phy-brcm-sata.c +deleted file mode 100644 +index ccbc3d994998..000000000000 +--- a/drivers/phy/phy-brcm-sata.c ++++ /dev/null +@@ -1,493 +0,0 @@ +-/* +- * Broadcom SATA3 AHCI Controller PHY Driver +- * +- * Copyright (C) 2016 Broadcom +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2, or (at your option) +- * any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define SATA_PCB_BANK_OFFSET 0x23c +-#define SATA_PCB_REG_OFFSET(ofs) ((ofs) * 4) +- +-#define MAX_PORTS 2 +- +-/* Register offset between PHYs in PCB space */ +-#define SATA_PCB_REG_28NM_SPACE_SIZE 0x1000 +- +-/* The older SATA PHY registers duplicated per port registers within the map, +- * rather than having a separate map per port. +- */ +-#define SATA_PCB_REG_40NM_SPACE_SIZE 0x10 +- +-/* Register offset between PHYs in PHY control space */ +-#define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8 +- +-enum brcm_sata_phy_version { +- BRCM_SATA_PHY_STB_28NM, +- BRCM_SATA_PHY_STB_40NM, +- BRCM_SATA_PHY_IPROC_NS2, +- BRCM_SATA_PHY_IPROC_NSP, +-}; +- +-struct brcm_sata_port { +- int portnum; +- struct phy *phy; +- struct brcm_sata_phy *phy_priv; +- bool ssc_en; +-}; +- +-struct brcm_sata_phy { +- struct device *dev; +- void __iomem *phy_base; +- void __iomem *ctrl_base; +- enum brcm_sata_phy_version version; +- +- struct brcm_sata_port phys[MAX_PORTS]; +-}; +- +-enum sata_phy_regs { +- BLOCK0_REG_BANK = 0x000, +- BLOCK0_XGXSSTATUS = 0x81, +- BLOCK0_XGXSSTATUS_PLL_LOCK = BIT(12), +- BLOCK0_SPARE = 0x8d, +- BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3, +- BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1, +- +- PLL_REG_BANK_0 = 0x050, +- PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, +- PLLCONTROL_0_FREQ_DET_RESTART = BIT(13), +- PLLCONTROL_0_FREQ_MONITOR = BIT(12), +- PLLCONTROL_0_SEQ_START = BIT(15), +- PLL_CAP_CONTROL = 0x85, +- PLL_ACTRL2 = 0x8b, +- PLL_ACTRL2_SELDIV_MASK = 0x1f, +- PLL_ACTRL2_SELDIV_SHIFT = 9, +- +- PLL1_REG_BANK = 0x060, +- PLL1_ACTRL2 = 0x82, +- PLL1_ACTRL3 = 0x83, +- PLL1_ACTRL4 = 0x84, +- +- OOB_REG_BANK = 0x150, +- OOB1_REG_BANK = 0x160, +- OOB_CTRL1 = 0x80, +- OOB_CTRL1_BURST_MAX_MASK = 0xf, +- OOB_CTRL1_BURST_MAX_SHIFT = 12, +- OOB_CTRL1_BURST_MIN_MASK = 0xf, +- OOB_CTRL1_BURST_MIN_SHIFT = 8, +- OOB_CTRL1_WAKE_IDLE_MAX_MASK = 0xf, +- OOB_CTRL1_WAKE_IDLE_MAX_SHIFT = 4, +- OOB_CTRL1_WAKE_IDLE_MIN_MASK = 0xf, +- OOB_CTRL1_WAKE_IDLE_MIN_SHIFT = 0, +- OOB_CTRL2 = 0x81, +- OOB_CTRL2_SEL_ENA_SHIFT = 15, +- OOB_CTRL2_SEL_ENA_RC_SHIFT = 14, +- OOB_CTRL2_RESET_IDLE_MAX_MASK = 0x3f, +- OOB_CTRL2_RESET_IDLE_MAX_SHIFT = 8, +- OOB_CTRL2_BURST_CNT_MASK = 0x3, +- OOB_CTRL2_BURST_CNT_SHIFT = 6, +- OOB_CTRL2_RESET_IDLE_MIN_MASK = 0x3f, +- OOB_CTRL2_RESET_IDLE_MIN_SHIFT = 0, +- +- TXPMD_REG_BANK = 0x1a0, +- TXPMD_CONTROL1 = 0x81, +- TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), +- TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), +- TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, +- TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, +- TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, +- TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, +- TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, +-}; +- +-enum sata_phy_ctrl_regs { +- PHY_CTRL_1 = 0x0, +- PHY_CTRL_1_RESET = BIT(0), +-}; +- +-static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port) +-{ +- struct brcm_sata_phy *priv = port->phy_priv; +- u32 size = 0; +- +- switch (priv->version) { +- case BRCM_SATA_PHY_STB_28NM: +- case BRCM_SATA_PHY_IPROC_NS2: +- size = SATA_PCB_REG_28NM_SPACE_SIZE; +- break; +- case BRCM_SATA_PHY_STB_40NM: +- size = SATA_PCB_REG_40NM_SPACE_SIZE; +- break; +- default: +- dev_err(priv->dev, "invalid phy version\n"); +- break; +- } +- +- return priv->phy_base + (port->portnum * size); +-} +- +-static inline void __iomem *brcm_sata_ctrl_base(struct brcm_sata_port *port) +-{ +- struct brcm_sata_phy *priv = port->phy_priv; +- u32 size = 0; +- +- switch (priv->version) { +- case BRCM_SATA_PHY_IPROC_NS2: +- size = SATA_PHY_CTRL_REG_28NM_SPACE_SIZE; +- break; +- default: +- dev_err(priv->dev, "invalid phy version\n"); +- break; +- } +- +- return priv->ctrl_base + (port->portnum * size); +-} +- +-static void brcm_sata_phy_wr(void __iomem *pcb_base, u32 bank, +- u32 ofs, u32 msk, u32 value) +-{ +- u32 tmp; +- +- writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); +- tmp = readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); +- tmp = (tmp & msk) | value; +- writel(tmp, pcb_base + SATA_PCB_REG_OFFSET(ofs)); +-} +- +-static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs) +-{ +- writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); +- return readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); +-} +- +-/* These defaults were characterized by H/W group */ +-#define STB_FMIN_VAL_DEFAULT 0x3df +-#define STB_FMAX_VAL_DEFAULT 0x3df +-#define STB_FMAX_VAL_SSC 0x83 +- +-static int brcm_stb_sata_init(struct brcm_sata_port *port) +-{ +- void __iomem *base = brcm_sata_pcb_base(port); +- struct brcm_sata_phy *priv = port->phy_priv; +- u32 tmp; +- +- /* override the TX spread spectrum setting */ +- tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; +- brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); +- +- /* set fixed min freq */ +- brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, +- ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, +- STB_FMIN_VAL_DEFAULT); +- +- /* set fixed max freq depending on SSC config */ +- if (port->ssc_en) { +- dev_info(priv->dev, "enabling SSC on port%d\n", port->portnum); +- tmp = STB_FMAX_VAL_SSC; +- } else { +- tmp = STB_FMAX_VAL_DEFAULT; +- } +- +- brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, +- ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); +- +- return 0; +-} +- +-/* NS2 SATA PLL1 defaults were characterized by H/W group */ +-#define NS2_PLL1_ACTRL2_MAGIC 0x1df8 +-#define NS2_PLL1_ACTRL3_MAGIC 0x2b00 +-#define NS2_PLL1_ACTRL4_MAGIC 0x8824 +- +-static int brcm_ns2_sata_init(struct brcm_sata_port *port) +-{ +- int try; +- unsigned int val; +- void __iomem *base = brcm_sata_pcb_base(port); +- void __iomem *ctrl_base = brcm_sata_ctrl_base(port); +- struct device *dev = port->phy_priv->dev; +- +- /* Configure OOB control */ +- val = 0x0; +- val |= (0xc << OOB_CTRL1_BURST_MAX_SHIFT); +- val |= (0x4 << OOB_CTRL1_BURST_MIN_SHIFT); +- val |= (0x9 << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); +- val |= (0x3 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); +- brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL1, 0x0, val); +- val = 0x0; +- val |= (0x1b << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); +- val |= (0x2 << OOB_CTRL2_BURST_CNT_SHIFT); +- val |= (0x9 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); +- brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL2, 0x0, val); +- +- /* Configure PHY PLL register bank 1 */ +- val = NS2_PLL1_ACTRL2_MAGIC; +- brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL2, 0x0, val); +- val = NS2_PLL1_ACTRL3_MAGIC; +- brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL3, 0x0, val); +- val = NS2_PLL1_ACTRL4_MAGIC; +- brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL4, 0x0, val); +- +- /* Configure PHY BLOCK0 register bank */ +- /* Set oob_clk_sel to refclk/2 */ +- brcm_sata_phy_wr(base, BLOCK0_REG_BANK, BLOCK0_SPARE, +- ~BLOCK0_SPARE_OOB_CLK_SEL_MASK, +- BLOCK0_SPARE_OOB_CLK_SEL_REFBY2); +- +- /* Strobe PHY reset using PHY control register */ +- writel(PHY_CTRL_1_RESET, ctrl_base + PHY_CTRL_1); +- mdelay(1); +- writel(0x0, ctrl_base + PHY_CTRL_1); +- mdelay(1); +- +- /* Wait for PHY PLL lock by polling pll_lock bit */ +- try = 50; +- while (try) { +- val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, +- BLOCK0_XGXSSTATUS); +- if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) +- break; +- msleep(20); +- try--; +- } +- if (!try) { +- /* PLL did not lock; give up */ +- dev_err(dev, "port%d PLL did not lock\n", port->portnum); +- return -ETIMEDOUT; +- } +- +- dev_dbg(dev, "port%d initialized\n", port->portnum); +- +- return 0; +-} +- +-static int brcm_nsp_sata_init(struct brcm_sata_port *port) +-{ +- struct brcm_sata_phy *priv = port->phy_priv; +- struct device *dev = port->phy_priv->dev; +- void __iomem *base = priv->phy_base; +- unsigned int oob_bank; +- unsigned int val, try; +- +- /* Configure OOB control */ +- if (port->portnum == 0) +- oob_bank = OOB_REG_BANK; +- else if (port->portnum == 1) +- oob_bank = OOB1_REG_BANK; +- else +- return -EINVAL; +- +- val = 0x0; +- val |= (0x0f << OOB_CTRL1_BURST_MAX_SHIFT); +- val |= (0x06 << OOB_CTRL1_BURST_MIN_SHIFT); +- val |= (0x0f << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); +- val |= (0x06 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); +- brcm_sata_phy_wr(base, oob_bank, OOB_CTRL1, 0x0, val); +- +- val = 0x0; +- val |= (0x2e << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); +- val |= (0x02 << OOB_CTRL2_BURST_CNT_SHIFT); +- val |= (0x16 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); +- brcm_sata_phy_wr(base, oob_bank, OOB_CTRL2, 0x0, val); +- +- +- brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_ACTRL2, +- ~(PLL_ACTRL2_SELDIV_MASK << PLL_ACTRL2_SELDIV_SHIFT), +- 0x0c << PLL_ACTRL2_SELDIV_SHIFT); +- +- brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_CAP_CONTROL, +- 0xff0, 0x4f0); +- +- val = PLLCONTROL_0_FREQ_DET_RESTART | PLLCONTROL_0_FREQ_MONITOR; +- brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, +- ~val, val); +- val = PLLCONTROL_0_SEQ_START; +- brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, +- ~val, 0); +- mdelay(10); +- brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, +- ~val, val); +- +- /* Wait for pll_seq_done bit */ +- try = 50; +- while (try--) { +- val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, +- BLOCK0_XGXSSTATUS); +- if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) +- break; +- msleep(20); +- } +- if (!try) { +- /* PLL did not lock; give up */ +- dev_err(dev, "port%d PLL did not lock\n", port->portnum); +- return -ETIMEDOUT; +- } +- +- dev_dbg(dev, "port%d initialized\n", port->portnum); +- +- return 0; +-} +- +-static int brcm_sata_phy_init(struct phy *phy) +-{ +- int rc; +- struct brcm_sata_port *port = phy_get_drvdata(phy); +- +- switch (port->phy_priv->version) { +- case BRCM_SATA_PHY_STB_28NM: +- case BRCM_SATA_PHY_STB_40NM: +- rc = brcm_stb_sata_init(port); +- break; +- case BRCM_SATA_PHY_IPROC_NS2: +- rc = brcm_ns2_sata_init(port); +- break; +- case BRCM_SATA_PHY_IPROC_NSP: +- rc = brcm_nsp_sata_init(port); +- break; +- default: +- rc = -ENODEV; +- } +- +- return rc; +-} +- +-static const struct phy_ops phy_ops = { +- .init = brcm_sata_phy_init, +- .owner = THIS_MODULE, +-}; +- +-static const struct of_device_id brcm_sata_phy_of_match[] = { +- { .compatible = "brcm,bcm7445-sata-phy", +- .data = (void *)BRCM_SATA_PHY_STB_28NM }, +- { .compatible = "brcm,bcm7425-sata-phy", +- .data = (void *)BRCM_SATA_PHY_STB_40NM }, +- { .compatible = "brcm,iproc-ns2-sata-phy", +- .data = (void *)BRCM_SATA_PHY_IPROC_NS2 }, +- { .compatible = "brcm,iproc-nsp-sata-phy", +- .data = (void *)BRCM_SATA_PHY_IPROC_NSP }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); +- +-static int brcm_sata_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct device_node *dn = dev->of_node, *child; +- const struct of_device_id *of_id; +- struct brcm_sata_phy *priv; +- struct resource *res; +- struct phy_provider *provider; +- int ret, count = 0; +- +- if (of_get_child_count(dn) == 0) +- return -ENODEV; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- dev_set_drvdata(dev, priv); +- priv->dev = dev; +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); +- priv->phy_base = devm_ioremap_resource(dev, res); +- if (IS_ERR(priv->phy_base)) +- return PTR_ERR(priv->phy_base); +- +- of_id = of_match_node(brcm_sata_phy_of_match, dn); +- if (of_id) +- priv->version = (enum brcm_sata_phy_version)of_id->data; +- else +- priv->version = BRCM_SATA_PHY_STB_28NM; +- +- if (priv->version == BRCM_SATA_PHY_IPROC_NS2) { +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +- "phy-ctrl"); +- priv->ctrl_base = devm_ioremap_resource(dev, res); +- if (IS_ERR(priv->ctrl_base)) +- return PTR_ERR(priv->ctrl_base); +- } +- +- for_each_available_child_of_node(dn, child) { +- unsigned int id; +- struct brcm_sata_port *port; +- +- if (of_property_read_u32(child, "reg", &id)) { +- dev_err(dev, "missing reg property in node %s\n", +- child->name); +- ret = -EINVAL; +- goto put_child; +- } +- +- if (id >= MAX_PORTS) { +- dev_err(dev, "invalid reg: %u\n", id); +- ret = -EINVAL; +- goto put_child; +- } +- if (priv->phys[id].phy) { +- dev_err(dev, "already registered port %u\n", id); +- ret = -EINVAL; +- goto put_child; +- } +- +- port = &priv->phys[id]; +- port->portnum = id; +- port->phy_priv = priv; +- port->phy = devm_phy_create(dev, child, &phy_ops); +- port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); +- if (IS_ERR(port->phy)) { +- dev_err(dev, "failed to create PHY\n"); +- ret = PTR_ERR(port->phy); +- goto put_child; +- } +- +- phy_set_drvdata(port->phy, port); +- count++; +- } +- +- provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (IS_ERR(provider)) { +- dev_err(dev, "could not register PHY provider\n"); +- return PTR_ERR(provider); +- } +- +- dev_info(dev, "registered %d port(s)\n", count); +- +- return 0; +-put_child: +- of_node_put(child); +- return ret; +-} +- +-static struct platform_driver brcm_sata_phy_driver = { +- .probe = brcm_sata_phy_probe, +- .driver = { +- .of_match_table = brcm_sata_phy_of_match, +- .name = "brcm-sata-phy", +- } +-}; +-module_platform_driver(brcm_sata_phy_driver); +- +-MODULE_DESCRIPTION("Broadcom SATA PHY driver"); +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Marc Carino"); +-MODULE_AUTHOR("Brian Norris"); +-MODULE_ALIAS("platform:phy-brcm-sata"); +diff --git a/drivers/phy/phy-da8xx-usb.c b/drivers/phy/phy-da8xx-usb.c +deleted file mode 100644 +index 1b82bff6330f..000000000000 +--- a/drivers/phy/phy-da8xx-usb.c ++++ /dev/null +@@ -1,251 +0,0 @@ +-/* +- * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver +- * +- * Copyright (C) 2016 David Lechner +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; version 2 of the License. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define PHY_INIT_BITS (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN) +- +-struct da8xx_usb_phy { +- struct phy_provider *phy_provider; +- struct phy *usb11_phy; +- struct phy *usb20_phy; +- struct clk *usb11_clk; +- struct clk *usb20_clk; +- struct regmap *regmap; +-}; +- +-static int da8xx_usb11_phy_power_on(struct phy *phy) +-{ +- struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); +- int ret; +- +- ret = clk_prepare_enable(d_phy->usb11_clk); +- if (ret) +- return ret; +- +- regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, +- CFGCHIP2_USB1SUSPENDM); +- +- return 0; +-} +- +-static int da8xx_usb11_phy_power_off(struct phy *phy) +-{ +- struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); +- +- regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0); +- +- clk_disable_unprepare(d_phy->usb11_clk); +- +- return 0; +-} +- +-static const struct phy_ops da8xx_usb11_phy_ops = { +- .power_on = da8xx_usb11_phy_power_on, +- .power_off = da8xx_usb11_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int da8xx_usb20_phy_power_on(struct phy *phy) +-{ +- struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); +- int ret; +- +- ret = clk_prepare_enable(d_phy->usb20_clk); +- if (ret) +- return ret; +- +- regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0); +- +- return 0; +-} +- +-static int da8xx_usb20_phy_power_off(struct phy *phy) +-{ +- struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); +- +- regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, +- CFGCHIP2_OTGPWRDN); +- +- clk_disable_unprepare(d_phy->usb20_clk); +- +- return 0; +-} +- +-static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode) +-{ +- struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); +- u32 val; +- +- switch (mode) { +- case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */ +- val = CFGCHIP2_OTGMODE_FORCE_HOST; +- break; +- case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */ +- val = CFGCHIP2_OTGMODE_FORCE_DEVICE; +- break; +- case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */ +- val = CFGCHIP2_OTGMODE_NO_OVERRIDE; +- break; +- default: +- return -EINVAL; +- } +- +- regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK, +- val); +- +- return 0; +-} +- +-static const struct phy_ops da8xx_usb20_phy_ops = { +- .power_on = da8xx_usb20_phy_power_on, +- .power_off = da8xx_usb20_phy_power_off, +- .set_mode = da8xx_usb20_phy_set_mode, +- .owner = THIS_MODULE, +-}; +- +-static struct phy *da8xx_usb_phy_of_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev); +- +- if (!d_phy) +- return ERR_PTR(-ENODEV); +- +- switch (args->args[0]) { +- case 0: +- return d_phy->usb20_phy; +- case 1: +- return d_phy->usb11_phy; +- default: +- return ERR_PTR(-EINVAL); +- } +-} +- +-static int da8xx_usb_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct device_node *node = dev->of_node; +- struct da8xx_usb_phy *d_phy; +- +- d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL); +- if (!d_phy) +- return -ENOMEM; +- +- if (node) +- d_phy->regmap = syscon_regmap_lookup_by_compatible( +- "ti,da830-cfgchip"); +- else +- d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon"); +- if (IS_ERR(d_phy->regmap)) { +- dev_err(dev, "Failed to get syscon\n"); +- return PTR_ERR(d_phy->regmap); +- } +- +- d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy"); +- if (IS_ERR(d_phy->usb11_clk)) { +- dev_err(dev, "Failed to get usb11_phy clock\n"); +- return PTR_ERR(d_phy->usb11_clk); +- } +- +- d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy"); +- if (IS_ERR(d_phy->usb20_clk)) { +- dev_err(dev, "Failed to get usb20_phy clock\n"); +- return PTR_ERR(d_phy->usb20_clk); +- } +- +- d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops); +- if (IS_ERR(d_phy->usb11_phy)) { +- dev_err(dev, "Failed to create usb11 phy\n"); +- return PTR_ERR(d_phy->usb11_phy); +- } +- +- d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops); +- if (IS_ERR(d_phy->usb20_phy)) { +- dev_err(dev, "Failed to create usb20 phy\n"); +- return PTR_ERR(d_phy->usb20_phy); +- } +- +- platform_set_drvdata(pdev, d_phy); +- phy_set_drvdata(d_phy->usb11_phy, d_phy); +- phy_set_drvdata(d_phy->usb20_phy, d_phy); +- +- if (node) { +- d_phy->phy_provider = devm_of_phy_provider_register(dev, +- da8xx_usb_phy_of_xlate); +- if (IS_ERR(d_phy->phy_provider)) { +- dev_err(dev, "Failed to create phy provider\n"); +- return PTR_ERR(d_phy->phy_provider); +- } +- } else { +- int ret; +- +- ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", +- "ohci-da8xx"); +- if (ret) +- dev_warn(dev, "Failed to create usb11 phy lookup\n"); +- ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy", +- "musb-da8xx"); +- if (ret) +- dev_warn(dev, "Failed to create usb20 phy lookup\n"); +- } +- +- regmap_write_bits(d_phy->regmap, CFGCHIP(2), +- PHY_INIT_BITS, PHY_INIT_BITS); +- +- return 0; +-} +- +-static int da8xx_usb_phy_remove(struct platform_device *pdev) +-{ +- struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev); +- +- if (!pdev->dev.of_node) { +- phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx"); +- phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx"); +- } +- +- return 0; +-} +- +-static const struct of_device_id da8xx_usb_phy_ids[] = { +- { .compatible = "ti,da830-usb-phy" }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids); +- +-static struct platform_driver da8xx_usb_phy_driver = { +- .probe = da8xx_usb_phy_probe, +- .remove = da8xx_usb_phy_remove, +- .driver = { +- .name = "da8xx-usb-phy", +- .of_match_table = da8xx_usb_phy_ids, +- }, +-}; +- +-module_platform_driver(da8xx_usb_phy_driver); +- +-MODULE_ALIAS("platform:da8xx-usb-phy"); +-MODULE_AUTHOR("David Lechner "); +-MODULE_DESCRIPTION("TI DA8xx USB PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-dm816x-usb.c b/drivers/phy/phy-dm816x-usb.c +deleted file mode 100644 +index cbcce7cf0028..000000000000 +--- a/drivers/phy/phy-dm816x-usb.c ++++ /dev/null +@@ -1,290 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation version 2. +- * +- * This program is distributed "as is" WITHOUT ANY WARRANTY of any +- * kind, whether express or implied; without even the implied warranty +- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-/* +- * TRM has two sets of USB_CTRL registers.. The correct register bits +- * are in TRM section 24.9.8.2 USB_CTRL Register. The TRM documents the +- * phy as being SR70LX Synopsys USB 2.0 OTG nanoPHY. It also seems at +- * least dm816x rev c ignores writes to USB_CTRL register, but the TI +- * kernel is writing to those so it's possible that later revisions +- * have worknig USB_CTRL register. +- * +- * Also note that At least USB_CTRL register seems to be dm816x specific +- * according to the TRM. It's possible that USBPHY_CTRL is more generic, +- * but that would have to be checked against the SR70LX documentation +- * which does not seem to be publicly available. +- * +- * Finally, the phy on dm814x and am335x is different from dm816x. +- */ +-#define DM816X_USB_CTRL_PHYCLKSRC BIT(8) /* 1 = PLL ref clock */ +-#define DM816X_USB_CTRL_PHYSLEEP1 BIT(1) /* Enable the first phy */ +-#define DM816X_USB_CTRL_PHYSLEEP0 BIT(0) /* Enable the second phy */ +- +-#define DM816X_USBPHY_CTRL_TXRISETUNE 1 +-#define DM816X_USBPHY_CTRL_TXVREFTUNE 0xc +-#define DM816X_USBPHY_CTRL_TXPREEMTUNE 0x2 +- +-struct dm816x_usb_phy { +- struct regmap *syscon; +- struct device *dev; +- unsigned int instance; +- struct clk *refclk; +- struct usb_phy phy; +- unsigned int usb_ctrl; /* Shared between phy0 and phy1 */ +- unsigned int usbphy_ctrl; +-}; +- +-static int dm816x_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) +-{ +- otg->host = host; +- if (!host) +- otg->state = OTG_STATE_UNDEFINED; +- +- return 0; +-} +- +-static int dm816x_usb_phy_set_peripheral(struct usb_otg *otg, +- struct usb_gadget *gadget) +-{ +- otg->gadget = gadget; +- if (!gadget) +- otg->state = OTG_STATE_UNDEFINED; +- +- return 0; +-} +- +-static int dm816x_usb_phy_init(struct phy *x) +-{ +- struct dm816x_usb_phy *phy = phy_get_drvdata(x); +- unsigned int val; +- int error; +- +- if (clk_get_rate(phy->refclk) != 24000000) +- dev_warn(phy->dev, "nonstandard phy refclk\n"); +- +- /* Set PLL ref clock and put phys to sleep */ +- error = regmap_update_bits(phy->syscon, phy->usb_ctrl, +- DM816X_USB_CTRL_PHYCLKSRC | +- DM816X_USB_CTRL_PHYSLEEP1 | +- DM816X_USB_CTRL_PHYSLEEP0, +- 0); +- regmap_read(phy->syscon, phy->usb_ctrl, &val); +- if ((val & 3) != 0) +- dev_info(phy->dev, +- "Working dm816x USB_CTRL! (0x%08x)\n", +- val); +- +- /* +- * TI kernel sets these values for "symmetrical eye diagram and +- * better signal quality" so let's assume somebody checked the +- * values with a scope and set them here too. +- */ +- regmap_read(phy->syscon, phy->usbphy_ctrl, &val); +- val |= DM816X_USBPHY_CTRL_TXRISETUNE | +- DM816X_USBPHY_CTRL_TXVREFTUNE | +- DM816X_USBPHY_CTRL_TXPREEMTUNE; +- regmap_write(phy->syscon, phy->usbphy_ctrl, val); +- +- return 0; +-} +- +-static const struct phy_ops ops = { +- .init = dm816x_usb_phy_init, +- .owner = THIS_MODULE, +-}; +- +-static int __maybe_unused dm816x_usb_phy_runtime_suspend(struct device *dev) +-{ +- struct dm816x_usb_phy *phy = dev_get_drvdata(dev); +- unsigned int mask, val; +- int error = 0; +- +- mask = BIT(phy->instance); +- val = ~BIT(phy->instance); +- error = regmap_update_bits(phy->syscon, phy->usb_ctrl, +- mask, val); +- if (error) +- dev_err(phy->dev, "phy%i failed to power off\n", +- phy->instance); +- clk_disable(phy->refclk); +- +- return 0; +-} +- +-static int __maybe_unused dm816x_usb_phy_runtime_resume(struct device *dev) +-{ +- struct dm816x_usb_phy *phy = dev_get_drvdata(dev); +- unsigned int mask, val; +- int error; +- +- error = clk_enable(phy->refclk); +- if (error) +- return error; +- +- /* +- * Note that at least dm816x rev c does not seem to do +- * anything with the USB_CTRL register. But let's follow +- * what the TI tree is doing in case later revisions use +- * USB_CTRL. +- */ +- mask = BIT(phy->instance); +- val = BIT(phy->instance); +- error = regmap_update_bits(phy->syscon, phy->usb_ctrl, +- mask, val); +- if (error) { +- dev_err(phy->dev, "phy%i failed to power on\n", +- phy->instance); +- clk_disable(phy->refclk); +- return error; +- } +- +- return 0; +-} +- +-static UNIVERSAL_DEV_PM_OPS(dm816x_usb_phy_pm_ops, +- dm816x_usb_phy_runtime_suspend, +- dm816x_usb_phy_runtime_resume, +- NULL); +- +-#ifdef CONFIG_OF +-static const struct of_device_id dm816x_usb_phy_id_table[] = { +- { +- .compatible = "ti,dm8168-usb-phy", +- }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, dm816x_usb_phy_id_table); +-#endif +- +-static int dm816x_usb_phy_probe(struct platform_device *pdev) +-{ +- struct dm816x_usb_phy *phy; +- struct resource *res; +- struct phy *generic_phy; +- struct phy_provider *phy_provider; +- struct usb_otg *otg; +- const struct of_device_id *of_id; +- const struct usb_phy_data *phy_data; +- int error; +- +- of_id = of_match_device(of_match_ptr(dm816x_usb_phy_id_table), +- &pdev->dev); +- if (!of_id) +- return -EINVAL; +- +- phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) +- return -ENOENT; +- +- phy->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, +- "syscon"); +- if (IS_ERR(phy->syscon)) +- return PTR_ERR(phy->syscon); +- +- /* +- * According to sprs614e.pdf, the first usb_ctrl is shared and +- * the second instance for usb_ctrl is reserved.. Also the +- * register bits are different from earlier TRMs. +- */ +- phy->usb_ctrl = 0x20; +- phy->usbphy_ctrl = (res->start & 0xff) + 4; +- if (phy->usbphy_ctrl == 0x2c) +- phy->instance = 1; +- +- phy_data = of_id->data; +- +- otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); +- if (!otg) +- return -ENOMEM; +- +- phy->dev = &pdev->dev; +- phy->phy.dev = phy->dev; +- phy->phy.label = "dm8168_usb_phy"; +- phy->phy.otg = otg; +- phy->phy.type = USB_PHY_TYPE_USB2; +- otg->set_host = dm816x_usb_phy_set_host; +- otg->set_peripheral = dm816x_usb_phy_set_peripheral; +- otg->usb_phy = &phy->phy; +- +- platform_set_drvdata(pdev, phy); +- +- phy->refclk = devm_clk_get(phy->dev, "refclk"); +- if (IS_ERR(phy->refclk)) +- return PTR_ERR(phy->refclk); +- error = clk_prepare(phy->refclk); +- if (error) +- return error; +- +- pm_runtime_enable(phy->dev); +- generic_phy = devm_phy_create(phy->dev, NULL, &ops); +- if (IS_ERR(generic_phy)) +- return PTR_ERR(generic_phy); +- +- phy_set_drvdata(generic_phy, phy); +- +- phy_provider = devm_of_phy_provider_register(phy->dev, +- of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) +- return PTR_ERR(phy_provider); +- +- usb_add_phy_dev(&phy->phy); +- +- return 0; +-} +- +-static int dm816x_usb_phy_remove(struct platform_device *pdev) +-{ +- struct dm816x_usb_phy *phy = platform_get_drvdata(pdev); +- +- usb_remove_phy(&phy->phy); +- pm_runtime_disable(phy->dev); +- clk_unprepare(phy->refclk); +- +- return 0; +-} +- +-static struct platform_driver dm816x_usb_phy_driver = { +- .probe = dm816x_usb_phy_probe, +- .remove = dm816x_usb_phy_remove, +- .driver = { +- .name = "dm816x-usb-phy", +- .pm = &dm816x_usb_phy_pm_ops, +- .of_match_table = of_match_ptr(dm816x_usb_phy_id_table), +- }, +-}; +- +-module_platform_driver(dm816x_usb_phy_driver); +- +-MODULE_ALIAS("platform:dm816x_usb"); +-MODULE_AUTHOR("Tony Lindgren "); +-MODULE_DESCRIPTION("dm816x usb phy driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c +deleted file mode 100644 +index bb3279dbf88c..000000000000 +--- a/drivers/phy/phy-exynos-dp-video.c ++++ /dev/null +@@ -1,122 +0,0 @@ +-/* +- * Samsung EXYNOS SoC series Display Port PHY driver +- * +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Author: Jingoo Han +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-struct exynos_dp_video_phy_drvdata { +- u32 phy_ctrl_offset; +-}; +- +-struct exynos_dp_video_phy { +- struct regmap *regs; +- const struct exynos_dp_video_phy_drvdata *drvdata; +-}; +- +-static int exynos_dp_video_phy_power_on(struct phy *phy) +-{ +- struct exynos_dp_video_phy *state = phy_get_drvdata(phy); +- +- /* Disable power isolation on DP-PHY */ +- return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, +- EXYNOS4_PHY_ENABLE, EXYNOS4_PHY_ENABLE); +-} +- +-static int exynos_dp_video_phy_power_off(struct phy *phy) +-{ +- struct exynos_dp_video_phy *state = phy_get_drvdata(phy); +- +- /* Enable power isolation on DP-PHY */ +- return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, +- EXYNOS4_PHY_ENABLE, 0); +-} +- +-static const struct phy_ops exynos_dp_video_phy_ops = { +- .power_on = exynos_dp_video_phy_power_on, +- .power_off = exynos_dp_video_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static const struct exynos_dp_video_phy_drvdata exynos5250_dp_video_phy = { +- .phy_ctrl_offset = EXYNOS5_DPTX_PHY_CONTROL, +-}; +- +-static const struct exynos_dp_video_phy_drvdata exynos5420_dp_video_phy = { +- .phy_ctrl_offset = EXYNOS5420_DPTX_PHY_CONTROL, +-}; +- +-static const struct of_device_id exynos_dp_video_phy_of_match[] = { +- { +- .compatible = "samsung,exynos5250-dp-video-phy", +- .data = &exynos5250_dp_video_phy, +- }, { +- .compatible = "samsung,exynos5420-dp-video-phy", +- .data = &exynos5420_dp_video_phy, +- }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match); +- +-static int exynos_dp_video_phy_probe(struct platform_device *pdev) +-{ +- struct exynos_dp_video_phy *state; +- struct device *dev = &pdev->dev; +- const struct of_device_id *match; +- struct phy_provider *phy_provider; +- struct phy *phy; +- +- state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); +- if (!state) +- return -ENOMEM; +- +- state->regs = syscon_regmap_lookup_by_phandle(dev->of_node, +- "samsung,pmu-syscon"); +- if (IS_ERR(state->regs)) { +- dev_err(dev, "Failed to lookup PMU regmap\n"); +- return PTR_ERR(state->regs); +- } +- +- match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node); +- state->drvdata = match->data; +- +- phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create Display Port PHY\n"); +- return PTR_ERR(phy); +- } +- phy_set_drvdata(phy, state); +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static struct platform_driver exynos_dp_video_phy_driver = { +- .probe = exynos_dp_video_phy_probe, +- .driver = { +- .name = "exynos-dp-video-phy", +- .of_match_table = exynos_dp_video_phy_of_match, +- } +-}; +-module_platform_driver(exynos_dp_video_phy_driver); +- +-MODULE_AUTHOR("Jingoo Han "); +-MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c +deleted file mode 100644 +index c198886f80a3..000000000000 +--- a/drivers/phy/phy-exynos-mipi-video.c ++++ /dev/null +@@ -1,377 +0,0 @@ +-/* +- * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver +- * +- * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. +- * Author: Sylwester Nawrocki +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-enum exynos_mipi_phy_id { +- EXYNOS_MIPI_PHY_ID_NONE = -1, +- EXYNOS_MIPI_PHY_ID_CSIS0, +- EXYNOS_MIPI_PHY_ID_DSIM0, +- EXYNOS_MIPI_PHY_ID_CSIS1, +- EXYNOS_MIPI_PHY_ID_DSIM1, +- EXYNOS_MIPI_PHY_ID_CSIS2, +- EXYNOS_MIPI_PHYS_NUM +-}; +- +-enum exynos_mipi_phy_regmap_id { +- EXYNOS_MIPI_REGMAP_PMU, +- EXYNOS_MIPI_REGMAP_DISP, +- EXYNOS_MIPI_REGMAP_CAM0, +- EXYNOS_MIPI_REGMAP_CAM1, +- EXYNOS_MIPI_REGMAPS_NUM +-}; +- +-struct mipi_phy_device_desc { +- int num_phys; +- int num_regmaps; +- const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM]; +- struct exynos_mipi_phy_desc { +- enum exynos_mipi_phy_id coupled_phy_id; +- u32 enable_val; +- unsigned int enable_reg; +- enum exynos_mipi_phy_regmap_id enable_map; +- u32 resetn_val; +- unsigned int resetn_reg; +- enum exynos_mipi_phy_regmap_id resetn_map; +- } phys[EXYNOS_MIPI_PHYS_NUM]; +-}; +- +-static const struct mipi_phy_device_desc s5pv210_mipi_phy = { +- .num_regmaps = 1, +- .regmap_names = {"syscon"}, +- .num_phys = 4, +- .phys = { +- { +- /* EXYNOS_MIPI_PHY_ID_CSIS0 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, +- .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, { +- /* EXYNOS_MIPI_PHY_ID_DSIM0 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, +- .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, { +- /* EXYNOS_MIPI_PHY_ID_CSIS1 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, +- .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, { +- /* EXYNOS_MIPI_PHY_ID_DSIM1 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, +- .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, +- }, +-}; +- +-static const struct mipi_phy_device_desc exynos5420_mipi_phy = { +- .num_regmaps = 1, +- .regmap_names = {"syscon"}, +- .num_phys = 5, +- .phys = { +- { +- /* EXYNOS_MIPI_PHY_ID_CSIS0 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, +- .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, { +- /* EXYNOS_MIPI_PHY_ID_DSIM0 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, +- .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, { +- /* EXYNOS_MIPI_PHY_ID_CSIS1 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, +- .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, { +- /* EXYNOS_MIPI_PHY_ID_DSIM1 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, +- .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, { +- /* EXYNOS_MIPI_PHY_ID_CSIS2 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, +- .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), +- .resetn_map = EXYNOS_MIPI_REGMAP_PMU, +- }, +- }, +-}; +- +-#define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C +-#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014 +-#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020 +- +-static const struct mipi_phy_device_desc exynos5433_mipi_phy = { +- .num_regmaps = 4, +- .regmap_names = { +- "samsung,pmu-syscon", +- "samsung,disp-sysreg", +- "samsung,cam0-sysreg", +- "samsung,cam1-sysreg" +- }, +- .num_phys = 5, +- .phys = { +- { +- /* EXYNOS_MIPI_PHY_ID_CSIS0 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = BIT(0), +- .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, +- .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, +- }, { +- /* EXYNOS_MIPI_PHY_ID_DSIM0 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = BIT(0), +- .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, +- .resetn_map = EXYNOS_MIPI_REGMAP_DISP, +- }, { +- /* EXYNOS_MIPI_PHY_ID_CSIS1 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = BIT(1), +- .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, +- .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, +- }, { +- /* EXYNOS_MIPI_PHY_ID_DSIM1 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = BIT(1), +- .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, +- .resetn_map = EXYNOS_MIPI_REGMAP_DISP, +- }, { +- /* EXYNOS_MIPI_PHY_ID_CSIS2 */ +- .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, +- .enable_val = EXYNOS4_PHY_ENABLE, +- .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(2), +- .enable_map = EXYNOS_MIPI_REGMAP_PMU, +- .resetn_val = BIT(0), +- .resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON, +- .resetn_map = EXYNOS_MIPI_REGMAP_CAM1, +- }, +- }, +-}; +- +-struct exynos_mipi_video_phy { +- struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM]; +- int num_phys; +- struct video_phy_desc { +- struct phy *phy; +- unsigned int index; +- const struct exynos_mipi_phy_desc *data; +- } phys[EXYNOS_MIPI_PHYS_NUM]; +- spinlock_t slock; +-}; +- +-static int __set_phy_state(const struct exynos_mipi_phy_desc *data, +- struct exynos_mipi_video_phy *state, unsigned int on) +-{ +- u32 val; +- +- spin_lock(&state->slock); +- +- /* disable in PMU sysreg */ +- if (!on && data->coupled_phy_id >= 0 && +- state->phys[data->coupled_phy_id].phy->power_count == 0) { +- regmap_read(state->regmaps[data->enable_map], data->enable_reg, +- &val); +- val &= ~data->enable_val; +- regmap_write(state->regmaps[data->enable_map], data->enable_reg, +- val); +- } +- +- /* PHY reset */ +- regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val); +- val = on ? (val | data->resetn_val) : (val & ~data->resetn_val); +- regmap_write(state->regmaps[data->resetn_map], data->resetn_reg, val); +- +- /* enable in PMU sysreg */ +- if (on) { +- regmap_read(state->regmaps[data->enable_map], data->enable_reg, +- &val); +- val |= data->enable_val; +- regmap_write(state->regmaps[data->enable_map], data->enable_reg, +- val); +- } +- +- spin_unlock(&state->slock); +- +- return 0; +-} +- +-#define to_mipi_video_phy(desc) \ +- container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]) +- +-static int exynos_mipi_video_phy_power_on(struct phy *phy) +-{ +- struct video_phy_desc *phy_desc = phy_get_drvdata(phy); +- struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); +- +- return __set_phy_state(phy_desc->data, state, 1); +-} +- +-static int exynos_mipi_video_phy_power_off(struct phy *phy) +-{ +- struct video_phy_desc *phy_desc = phy_get_drvdata(phy); +- struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); +- +- return __set_phy_state(phy_desc->data, state, 0); +-} +- +-static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct exynos_mipi_video_phy *state = dev_get_drvdata(dev); +- +- if (WARN_ON(args->args[0] >= state->num_phys)) +- return ERR_PTR(-ENODEV); +- +- return state->phys[args->args[0]].phy; +-} +- +-static const struct phy_ops exynos_mipi_video_phy_ops = { +- .power_on = exynos_mipi_video_phy_power_on, +- .power_off = exynos_mipi_video_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int exynos_mipi_video_phy_probe(struct platform_device *pdev) +-{ +- const struct mipi_phy_device_desc *phy_dev; +- struct exynos_mipi_video_phy *state; +- struct device *dev = &pdev->dev; +- struct device_node *np = dev->of_node; +- struct phy_provider *phy_provider; +- unsigned int i; +- +- phy_dev = of_device_get_match_data(dev); +- if (!phy_dev) +- return -ENODEV; +- +- state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); +- if (!state) +- return -ENOMEM; +- +- for (i = 0; i < phy_dev->num_regmaps; i++) { +- state->regmaps[i] = syscon_regmap_lookup_by_phandle(np, +- phy_dev->regmap_names[i]); +- if (IS_ERR(state->regmaps[i])) +- return PTR_ERR(state->regmaps[i]); +- } +- state->num_phys = phy_dev->num_phys; +- spin_lock_init(&state->slock); +- +- dev_set_drvdata(dev, state); +- +- for (i = 0; i < state->num_phys; i++) { +- struct phy *phy = devm_phy_create(dev, NULL, +- &exynos_mipi_video_phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create PHY %d\n", i); +- return PTR_ERR(phy); +- } +- +- state->phys[i].phy = phy; +- state->phys[i].index = i; +- state->phys[i].data = &phy_dev->phys[i]; +- phy_set_drvdata(phy, &state->phys[i]); +- } +- +- phy_provider = devm_of_phy_provider_register(dev, +- exynos_mipi_video_phy_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id exynos_mipi_video_phy_of_match[] = { +- { +- .compatible = "samsung,s5pv210-mipi-video-phy", +- .data = &s5pv210_mipi_phy, +- }, { +- .compatible = "samsung,exynos5420-mipi-video-phy", +- .data = &exynos5420_mipi_phy, +- }, { +- .compatible = "samsung,exynos5433-mipi-video-phy", +- .data = &exynos5433_mipi_phy, +- }, +- { /* sentinel */ }, +-}; +-MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match); +- +-static struct platform_driver exynos_mipi_video_phy_driver = { +- .probe = exynos_mipi_video_phy_probe, +- .driver = { +- .of_match_table = exynos_mipi_video_phy_of_match, +- .name = "exynos-mipi-video-phy", +- } +-}; +-module_platform_driver(exynos_mipi_video_phy_driver); +- +-MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver"); +-MODULE_AUTHOR("Sylwester Nawrocki "); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-exynos-pcie.c b/drivers/phy/phy-exynos-pcie.c +deleted file mode 100644 +index a89c12faff39..000000000000 +--- a/drivers/phy/phy-exynos-pcie.c ++++ /dev/null +@@ -1,281 +0,0 @@ +-/* +- * Samsung EXYNOS SoC series PCIe PHY driver +- * +- * Phy provider for PCIe controller on Exynos SoC series +- * +- * Copyright (C) 2017 Samsung Electronics Co., Ltd. +- * Jaehoon Chung +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* PCIe Purple registers */ +-#define PCIE_PHY_GLOBAL_RESET 0x000 +-#define PCIE_PHY_COMMON_RESET 0x004 +-#define PCIE_PHY_CMN_REG 0x008 +-#define PCIE_PHY_MAC_RESET 0x00c +-#define PCIE_PHY_PLL_LOCKED 0x010 +-#define PCIE_PHY_TRSVREG_RESET 0x020 +-#define PCIE_PHY_TRSV_RESET 0x024 +- +-/* PCIe PHY registers */ +-#define PCIE_PHY_IMPEDANCE 0x004 +-#define PCIE_PHY_PLL_DIV_0 0x008 +-#define PCIE_PHY_PLL_BIAS 0x00c +-#define PCIE_PHY_DCC_FEEDBACK 0x014 +-#define PCIE_PHY_PLL_DIV_1 0x05c +-#define PCIE_PHY_COMMON_POWER 0x064 +-#define PCIE_PHY_COMMON_PD_CMN BIT(3) +-#define PCIE_PHY_TRSV0_EMP_LVL 0x084 +-#define PCIE_PHY_TRSV0_DRV_LVL 0x088 +-#define PCIE_PHY_TRSV0_RXCDR 0x0ac +-#define PCIE_PHY_TRSV0_POWER 0x0c4 +-#define PCIE_PHY_TRSV0_PD_TSV BIT(7) +-#define PCIE_PHY_TRSV0_LVCC 0x0dc +-#define PCIE_PHY_TRSV1_EMP_LVL 0x144 +-#define PCIE_PHY_TRSV1_RXCDR 0x16c +-#define PCIE_PHY_TRSV1_POWER 0x184 +-#define PCIE_PHY_TRSV1_PD_TSV BIT(7) +-#define PCIE_PHY_TRSV1_LVCC 0x19c +-#define PCIE_PHY_TRSV2_EMP_LVL 0x204 +-#define PCIE_PHY_TRSV2_RXCDR 0x22c +-#define PCIE_PHY_TRSV2_POWER 0x244 +-#define PCIE_PHY_TRSV2_PD_TSV BIT(7) +-#define PCIE_PHY_TRSV2_LVCC 0x25c +-#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 +-#define PCIE_PHY_TRSV3_RXCDR 0x2ec +-#define PCIE_PHY_TRSV3_POWER 0x304 +-#define PCIE_PHY_TRSV3_PD_TSV BIT(7) +-#define PCIE_PHY_TRSV3_LVCC 0x31c +- +-struct exynos_pcie_phy_data { +- const struct phy_ops *ops; +-}; +- +-/* For Exynos pcie phy */ +-struct exynos_pcie_phy { +- const struct exynos_pcie_phy_data *drv_data; +- void __iomem *phy_base; +- void __iomem *blk_base; /* For exynos5440 */ +-}; +- +-static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset) +-{ +- writel(val, base + offset); +-} +- +-static u32 exynos_pcie_phy_readl(void __iomem *base, u32 offset) +-{ +- return readl(base + offset); +-} +- +-/* For Exynos5440 specific functions */ +-static int exynos5440_pcie_phy_init(struct phy *phy) +-{ +- struct exynos_pcie_phy *ep = phy_get_drvdata(phy); +- +- /* DCC feedback control off */ +- exynos_pcie_phy_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK); +- +- /* set TX/RX impedance */ +- exynos_pcie_phy_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE); +- +- /* set 50Mhz PHY clock */ +- exynos_pcie_phy_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0); +- exynos_pcie_phy_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1); +- +- /* set TX Differential output for lane 0 */ +- exynos_pcie_phy_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL); +- +- /* set TX Pre-emphasis Level Control for lane 0 to minimum */ +- exynos_pcie_phy_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL); +- +- /* set RX clock and data recovery bandwidth */ +- exynos_pcie_phy_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS); +- exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR); +- exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR); +- exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR); +- exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR); +- +- /* change TX Pre-emphasis Level Control for lanes */ +- exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL); +- exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL); +- exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL); +- exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL); +- +- /* set LVCC */ +- exynos_pcie_phy_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC); +- exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC); +- exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC); +- exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC); +- +- /* pulse for common reset */ +- exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_COMMON_RESET); +- udelay(500); +- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); +- +- return 0; +-} +- +-static int exynos5440_pcie_phy_power_on(struct phy *phy) +-{ +- struct exynos_pcie_phy *ep = phy_get_drvdata(phy); +- u32 val; +- +- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); +- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_CMN_REG); +- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSVREG_RESET); +- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSV_RESET); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); +- val &= ~PCIE_PHY_COMMON_PD_CMN; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); +- val &= ~PCIE_PHY_TRSV0_PD_TSV; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); +- val &= ~PCIE_PHY_TRSV1_PD_TSV; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); +- val &= ~PCIE_PHY_TRSV2_PD_TSV; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); +- val &= ~PCIE_PHY_TRSV3_PD_TSV; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); +- +- return 0; +-} +- +-static int exynos5440_pcie_phy_power_off(struct phy *phy) +-{ +- struct exynos_pcie_phy *ep = phy_get_drvdata(phy); +- u32 val; +- +- if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val, +- (val != 0), 1, 500)) { +- dev_err(&phy->dev, "PLL Locked: 0x%x\n", val); +- return -ETIMEDOUT; +- } +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); +- val |= PCIE_PHY_COMMON_PD_CMN; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); +- val |= PCIE_PHY_TRSV0_PD_TSV; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); +- val |= PCIE_PHY_TRSV1_PD_TSV; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); +- val |= PCIE_PHY_TRSV2_PD_TSV; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); +- +- val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); +- val |= PCIE_PHY_TRSV3_PD_TSV; +- exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); +- +- return 0; +-} +- +-static int exynos5440_pcie_phy_reset(struct phy *phy) +-{ +- struct exynos_pcie_phy *ep = phy_get_drvdata(phy); +- +- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_MAC_RESET); +- exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_GLOBAL_RESET); +- exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_GLOBAL_RESET); +- +- return 0; +-} +- +-static const struct phy_ops exynos5440_phy_ops = { +- .init = exynos5440_pcie_phy_init, +- .power_on = exynos5440_pcie_phy_power_on, +- .power_off = exynos5440_pcie_phy_power_off, +- .reset = exynos5440_pcie_phy_reset, +- .owner = THIS_MODULE, +-}; +- +-static const struct exynos_pcie_phy_data exynos5440_pcie_phy_data = { +- .ops = &exynos5440_phy_ops, +-}; +- +-static const struct of_device_id exynos_pcie_phy_match[] = { +- { +- .compatible = "samsung,exynos5440-pcie-phy", +- .data = &exynos5440_pcie_phy_data, +- }, +- {}, +-}; +- +-static int exynos_pcie_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct exynos_pcie_phy *exynos_phy; +- struct phy *generic_phy; +- struct phy_provider *phy_provider; +- struct resource *res; +- const struct exynos_pcie_phy_data *drv_data; +- +- drv_data = of_device_get_match_data(dev); +- if (!drv_data) +- return -ENODEV; +- +- exynos_phy = devm_kzalloc(dev, sizeof(*exynos_phy), GFP_KERNEL); +- if (!exynos_phy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- exynos_phy->phy_base = devm_ioremap_resource(dev, res); +- if (IS_ERR(exynos_phy->phy_base)) +- return PTR_ERR(exynos_phy->phy_base); +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- exynos_phy->blk_base = devm_ioremap_resource(dev, res); +- if (IS_ERR(exynos_phy->blk_base)) +- return PTR_ERR(exynos_phy->blk_base); +- +- exynos_phy->drv_data = drv_data; +- +- generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops); +- if (IS_ERR(generic_phy)) { +- dev_err(dev, "failed to create PHY\n"); +- return PTR_ERR(generic_phy); +- } +- +- phy_set_drvdata(generic_phy, exynos_phy); +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static struct platform_driver exynos_pcie_phy_driver = { +- .probe = exynos_pcie_phy_probe, +- .driver = { +- .of_match_table = exynos_pcie_phy_match, +- .name = "exynos_pcie_phy", +- } +-}; +- +-builtin_platform_driver(exynos_pcie_phy_driver); +diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c +deleted file mode 100644 +index 1f50e1004828..000000000000 +--- a/drivers/phy/phy-exynos4210-usb2.c ++++ /dev/null +@@ -1,260 +0,0 @@ +-/* +- * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support +- * +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Author: Kamil Debski +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include "phy-samsung-usb2.h" +- +-/* Exynos USB PHY registers */ +- +-/* PHY power control */ +-#define EXYNOS_4210_UPHYPWR 0x0 +- +-#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0) +-#define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3) +-#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4) +-#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5) +-#define EXYNOS_4210_UPHYPWR_PHY0 ( \ +- EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \ +- EXYNOS_4210_UPHYPWR_PHY0_PWR | \ +- EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \ +- EXYNOS_4210_UPHYPWR_PHY0_SLEEP) +- +-#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6) +-#define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7) +-#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8) +-#define EXYNOS_4210_UPHYPWR_PHY1 ( \ +- EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \ +- EXYNOS_4210_UPHYPWR_PHY1_PWR | \ +- EXYNOS_4210_UPHYPWR_PHY1_SLEEP) +- +-#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9) +-#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10) +-#define EXYNOS_4210_UPHYPWR_HSIC0 ( \ +- EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \ +- EXYNOS_4210_UPHYPWR_HSIC0_SLEEP) +- +-#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11) +-#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12) +-#define EXYNOS_4210_UPHYPWR_HSIC1 ( \ +- EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \ +- EXYNOS_4210_UPHYPWR_HSIC1_SLEEP) +- +-/* PHY clock control */ +-#define EXYNOS_4210_UPHYCLK 0x4 +- +-#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) +-#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0 +-#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) +-#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) +-#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) +- +-#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2) +-#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4) +-#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7) +- +-/* PHY reset control */ +-#define EXYNOS_4210_UPHYRST 0x8 +- +-#define EXYNOS_4210_URSTCON_PHY0 BIT(0) +-#define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1) +-#define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2) +-#define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3) +-#define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4) +-#define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5) +-#define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6) +-#define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7) +-#define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8) +-#define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9) +- +-/* Isolation, configured in the power management unit */ +-#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704 +-#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0) +-#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708 +-#define EXYNOS_4210_USB_ISOL_HOST BIT(0) +- +-/* USBYPHY1 Floating prevention */ +-#define EXYNOS_4210_UPHY1CON 0x34 +-#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1 +- +-/* Mode switching SUB Device <-> Host */ +-#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c +-#define EXYNOS_4210_MODE_SWITCH_MASK 1 +-#define EXYNOS_4210_MODE_SWITCH_DEVICE 0 +-#define EXYNOS_4210_MODE_SWITCH_HOST 1 +- +-enum exynos4210_phy_id { +- EXYNOS4210_DEVICE, +- EXYNOS4210_HOST, +- EXYNOS4210_HSIC0, +- EXYNOS4210_HSIC1, +- EXYNOS4210_NUM_PHYS, +-}; +- +-/* +- * exynos4210_rate_to_clk() converts the supplied clock rate to the value that +- * can be written to the phy register. +- */ +-static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) +-{ +- switch (rate) { +- case 12 * MHZ: +- *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ; +- break; +- case 24 * MHZ: +- *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ; +- break; +- case 48 * MHZ: +- *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 offset; +- u32 mask; +- +- switch (inst->cfg->id) { +- case EXYNOS4210_DEVICE: +- offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET; +- mask = EXYNOS_4210_USB_ISOL_DEVICE; +- break; +- case EXYNOS4210_HOST: +- offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET; +- mask = EXYNOS_4210_USB_ISOL_HOST; +- break; +- default: +- return; +- } +- +- regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); +-} +- +-static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 rstbits = 0; +- u32 phypwr = 0; +- u32 rst; +- u32 pwr; +- u32 clk; +- +- switch (inst->cfg->id) { +- case EXYNOS4210_DEVICE: +- phypwr = EXYNOS_4210_UPHYPWR_PHY0; +- rstbits = EXYNOS_4210_URSTCON_PHY0; +- break; +- case EXYNOS4210_HOST: +- phypwr = EXYNOS_4210_UPHYPWR_PHY1; +- rstbits = EXYNOS_4210_URSTCON_PHY1_ALL | +- EXYNOS_4210_URSTCON_PHY1_P0 | +- EXYNOS_4210_URSTCON_PHY1_P1P2 | +- EXYNOS_4210_URSTCON_HOST_LINK_ALL | +- EXYNOS_4210_URSTCON_HOST_LINK_P0; +- writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON); +- break; +- case EXYNOS4210_HSIC0: +- phypwr = EXYNOS_4210_UPHYPWR_HSIC0; +- rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | +- EXYNOS_4210_URSTCON_HOST_LINK_P1; +- break; +- case EXYNOS4210_HSIC1: +- phypwr = EXYNOS_4210_UPHYPWR_HSIC1; +- rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | +- EXYNOS_4210_URSTCON_HOST_LINK_P2; +- break; +- } +- +- if (on) { +- clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK); +- clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK; +- clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET; +- writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK); +- +- pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); +- pwr &= ~phypwr; +- writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); +- +- rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST); +- rst |= rstbits; +- writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); +- udelay(10); +- rst &= ~rstbits; +- writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); +- /* The following delay is necessary for the reset sequence to be +- * completed */ +- udelay(80); +- } else { +- pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); +- pwr |= phypwr; +- writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); +- } +-} +- +-static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst) +-{ +- /* Order of initialisation is important - first power then isolation */ +- exynos4210_phy_pwr(inst, 1); +- exynos4210_isol(inst, 0); +- +- return 0; +-} +- +-static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst) +-{ +- exynos4210_isol(inst, 1); +- exynos4210_phy_pwr(inst, 0); +- +- return 0; +-} +- +- +-static const struct samsung_usb2_common_phy exynos4210_phys[] = { +- { +- .label = "device", +- .id = EXYNOS4210_DEVICE, +- .power_on = exynos4210_power_on, +- .power_off = exynos4210_power_off, +- }, +- { +- .label = "host", +- .id = EXYNOS4210_HOST, +- .power_on = exynos4210_power_on, +- .power_off = exynos4210_power_off, +- }, +- { +- .label = "hsic0", +- .id = EXYNOS4210_HSIC0, +- .power_on = exynos4210_power_on, +- .power_off = exynos4210_power_off, +- }, +- { +- .label = "hsic1", +- .id = EXYNOS4210_HSIC1, +- .power_on = exynos4210_power_on, +- .power_off = exynos4210_power_off, +- }, +-}; +- +-const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { +- .has_mode_switch = 0, +- .num_phys = EXYNOS4210_NUM_PHYS, +- .phys = exynos4210_phys, +- .rate_to_clk = exynos4210_rate_to_clk, +-}; +diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c +deleted file mode 100644 +index 7f27a91acf87..000000000000 +--- a/drivers/phy/phy-exynos4x12-usb2.c ++++ /dev/null +@@ -1,378 +0,0 @@ +-/* +- * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support +- * +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Author: Kamil Debski +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include "phy-samsung-usb2.h" +- +-/* Exynos USB PHY registers */ +- +-/* PHY power control */ +-#define EXYNOS_4x12_UPHYPWR 0x0 +- +-#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0) +-#define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3) +-#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4) +-#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5) +-#define EXYNOS_4x12_UPHYPWR_PHY0 ( \ +- EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \ +- EXYNOS_4x12_UPHYPWR_PHY0_PWR | \ +- EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \ +- EXYNOS_4x12_UPHYPWR_PHY0_SLEEP) +- +-#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6) +-#define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7) +-#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8) +-#define EXYNOS_4x12_UPHYPWR_PHY1 ( \ +- EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \ +- EXYNOS_4x12_UPHYPWR_PHY1_PWR | \ +- EXYNOS_4x12_UPHYPWR_PHY1_SLEEP) +- +-#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9) +-#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10) +-#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11) +-#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \ +- EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \ +- EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \ +- EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP) +- +-#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12) +-#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13) +-#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14) +-#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \ +- EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \ +- EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \ +- EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP) +- +-/* PHY clock control */ +-#define EXYNOS_4x12_UPHYCLK 0x4 +- +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0) +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0 +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0) +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0) +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0) +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0) +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) +-#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0) +- +-#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8) +- +-#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) +-#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) +-#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) +- +-#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10) +-#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10 +-#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10) +-#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10) +-#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10) +-#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10) +-#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10) +- +-/* PHY reset control */ +-#define EXYNOS_4x12_UPHYRST 0x8 +- +-#define EXYNOS_4x12_URSTCON_PHY0 BIT(0) +-#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) +-#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) +-#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) +-/* The following bit defines are presented in the +- * order taken from the Exynos4412 reference manual. +- * +- * During experiments with the hardware and debugging +- * it was determined that the hardware behaves contrary +- * to the manual. +- * +- * The following bit values were chaned accordingly to the +- * results of real hardware experiments. +- */ +-#define EXYNOS_4x12_URSTCON_PHY1 BIT(4) +-#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) +-#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) +-#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) +-#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) +-#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) +-#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) +- +-/* Isolation, configured in the power management unit */ +-#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 +-#define EXYNOS_4x12_USB_ISOL_OTG BIT(0) +-#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708 +-#define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0) +-#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c +-#define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0) +- +-/* Mode switching SUB Device <-> Host */ +-#define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c +-#define EXYNOS_4x12_MODE_SWITCH_MASK 1 +-#define EXYNOS_4x12_MODE_SWITCH_DEVICE 0 +-#define EXYNOS_4x12_MODE_SWITCH_HOST 1 +- +-enum exynos4x12_phy_id { +- EXYNOS4x12_DEVICE, +- EXYNOS4x12_HOST, +- EXYNOS4x12_HSIC0, +- EXYNOS4x12_HSIC1, +- EXYNOS4x12_NUM_PHYS, +-}; +- +-/* +- * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that +- * can be written to the phy register. +- */ +-static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) +-{ +- /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */ +- +- switch (rate) { +- case 9600 * KHZ: +- *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6; +- break; +- case 10 * MHZ: +- *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ; +- break; +- case 12 * MHZ: +- *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ; +- break; +- case 19200 * KHZ: +- *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2; +- break; +- case 20 * MHZ: +- *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ; +- break; +- case 24 * MHZ: +- *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ; +- break; +- case 50 * MHZ: +- *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 offset; +- u32 mask; +- +- switch (inst->cfg->id) { +- case EXYNOS4x12_DEVICE: +- case EXYNOS4x12_HOST: +- offset = EXYNOS_4x12_USB_ISOL_OFFSET; +- mask = EXYNOS_4x12_USB_ISOL_OTG; +- break; +- case EXYNOS4x12_HSIC0: +- offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET; +- mask = EXYNOS_4x12_USB_ISOL_HSIC0; +- break; +- case EXYNOS4x12_HSIC1: +- offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET; +- mask = EXYNOS_4x12_USB_ISOL_HSIC1; +- break; +- default: +- return; +- } +- +- regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); +-} +- +-static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 clk; +- +- clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); +- clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; +- +- if (drv->cfg->has_refclk_sel) +- clk = EXYNOS_3250_UPHYCLK_REFCLKSEL; +- +- clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; +- clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; +- writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); +-} +- +-static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 rstbits = 0; +- u32 phypwr = 0; +- u32 rst; +- u32 pwr; +- +- switch (inst->cfg->id) { +- case EXYNOS4x12_DEVICE: +- phypwr = EXYNOS_4x12_UPHYPWR_PHY0; +- rstbits = EXYNOS_4x12_URSTCON_PHY0; +- break; +- case EXYNOS4x12_HOST: +- phypwr = EXYNOS_4x12_UPHYPWR_PHY1; +- rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | +- EXYNOS_4x12_URSTCON_PHY1 | +- EXYNOS_4x12_URSTCON_HOST_LINK_P0; +- break; +- case EXYNOS4x12_HSIC0: +- phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; +- rstbits = EXYNOS_4x12_URSTCON_HSIC0 | +- EXYNOS_4x12_URSTCON_HOST_LINK_P1; +- break; +- case EXYNOS4x12_HSIC1: +- phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; +- rstbits = EXYNOS_4x12_URSTCON_HSIC1 | +- EXYNOS_4x12_URSTCON_HOST_LINK_P1; +- break; +- } +- +- if (on) { +- pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); +- pwr &= ~phypwr; +- writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); +- +- rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST); +- rst |= rstbits; +- writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); +- udelay(10); +- rst &= ~rstbits; +- writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); +- /* The following delay is necessary for the reset sequence to be +- * completed */ +- udelay(80); +- } else { +- pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); +- pwr |= phypwr; +- writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); +- } +-} +- +-static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) +-{ +- if (inst->int_cnt++ > 0) +- return; +- +- exynos4x12_setup_clk(inst); +- exynos4x12_isol(inst, 0); +- exynos4x12_phy_pwr(inst, 1); +-} +- +-static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- +- if (inst->ext_cnt++ > 0) +- return 0; +- +- if (inst->cfg->id == EXYNOS4x12_HOST) { +- regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, +- EXYNOS_4x12_MODE_SWITCH_MASK, +- EXYNOS_4x12_MODE_SWITCH_HOST); +- exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); +- } +- +- if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) +- regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, +- EXYNOS_4x12_MODE_SWITCH_MASK, +- EXYNOS_4x12_MODE_SWITCH_DEVICE); +- +- if (inst->cfg->id == EXYNOS4x12_HSIC0 || +- inst->cfg->id == EXYNOS4x12_HSIC1) { +- exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); +- exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); +- } +- +- exynos4x12_power_on_int(inst); +- +- return 0; +-} +- +-static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) +-{ +- if (inst->int_cnt-- > 1) +- return; +- +- exynos4x12_isol(inst, 1); +- exynos4x12_phy_pwr(inst, 0); +-} +- +-static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- +- if (inst->ext_cnt-- > 1) +- return 0; +- +- if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) +- regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, +- EXYNOS_4x12_MODE_SWITCH_MASK, +- EXYNOS_4x12_MODE_SWITCH_HOST); +- +- if (inst->cfg->id == EXYNOS4x12_HOST) +- exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); +- +- if (inst->cfg->id == EXYNOS4x12_HSIC0 || +- inst->cfg->id == EXYNOS4x12_HSIC1) { +- exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); +- exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); +- } +- +- exynos4x12_power_off_int(inst); +- +- return 0; +-} +- +- +-static const struct samsung_usb2_common_phy exynos4x12_phys[] = { +- { +- .label = "device", +- .id = EXYNOS4x12_DEVICE, +- .power_on = exynos4x12_power_on, +- .power_off = exynos4x12_power_off, +- }, +- { +- .label = "host", +- .id = EXYNOS4x12_HOST, +- .power_on = exynos4x12_power_on, +- .power_off = exynos4x12_power_off, +- }, +- { +- .label = "hsic0", +- .id = EXYNOS4x12_HSIC0, +- .power_on = exynos4x12_power_on, +- .power_off = exynos4x12_power_off, +- }, +- { +- .label = "hsic1", +- .id = EXYNOS4x12_HSIC1, +- .power_on = exynos4x12_power_on, +- .power_off = exynos4x12_power_off, +- }, +-}; +- +-const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { +- .has_refclk_sel = 1, +- .num_phys = 1, +- .phys = exynos4x12_phys, +- .rate_to_clk = exynos4x12_rate_to_clk, +-}; +- +-const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { +- .has_mode_switch = 1, +- .num_phys = EXYNOS4x12_NUM_PHYS, +- .phys = exynos4x12_phys, +- .rate_to_clk = exynos4x12_rate_to_clk, +-}; +diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c +deleted file mode 100644 +index 7c41daa2c625..000000000000 +--- a/drivers/phy/phy-exynos5-usbdrd.c ++++ /dev/null +@@ -1,782 +0,0 @@ +-/* +- * Samsung EXYNOS5 SoC series USB DRD PHY driver +- * +- * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series +- * +- * Copyright (C) 2014 Samsung Electronics Co., Ltd. +- * Author: Vivek Gautam +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* Exynos USB PHY registers */ +-#define EXYNOS5_FSEL_9MHZ6 0x0 +-#define EXYNOS5_FSEL_10MHZ 0x1 +-#define EXYNOS5_FSEL_12MHZ 0x2 +-#define EXYNOS5_FSEL_19MHZ2 0x3 +-#define EXYNOS5_FSEL_20MHZ 0x4 +-#define EXYNOS5_FSEL_24MHZ 0x5 +-#define EXYNOS5_FSEL_50MHZ 0x7 +- +-/* EXYNOS5: USB 3.0 DRD PHY registers */ +-#define EXYNOS5_DRD_LINKSYSTEM 0x04 +- +-#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) +-#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) +-#define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) +- +-#define EXYNOS5_DRD_PHYUTMI 0x08 +- +-#define PHYUTMI_OTGDISABLE BIT(6) +-#define PHYUTMI_FORCESUSPEND BIT(1) +-#define PHYUTMI_FORCESLEEP BIT(0) +- +-#define EXYNOS5_DRD_PHYPIPE 0x0c +- +-#define EXYNOS5_DRD_PHYCLKRST 0x10 +- +-#define PHYCLKRST_EN_UTMISUSPEND BIT(31) +- +-#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) +-#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) +- +-#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) +-#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) +- +-#define PHYCLKRST_SSC_EN BIT(20) +-#define PHYCLKRST_REF_SSP_EN BIT(19) +-#define PHYCLKRST_REF_CLKDIV2 BIT(18) +- +-#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) +-#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) +-#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x32 << 11) +-#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) +-#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) +-#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) +- +-#define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) +-#define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) +-#define PHYCLKRST_FSEL(_x) ((_x) << 5) +-#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) +-#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) +-#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) +-#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) +- +-#define PHYCLKRST_RETENABLEN BIT(4) +- +-#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) +-#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) +-#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) +- +-#define PHYCLKRST_PORTRESET BIT(1) +-#define PHYCLKRST_COMMONONN BIT(0) +- +-#define EXYNOS5_DRD_PHYREG0 0x14 +-#define EXYNOS5_DRD_PHYREG1 0x18 +- +-#define EXYNOS5_DRD_PHYPARAM0 0x1c +- +-#define PHYPARAM0_REF_USE_PAD BIT(31) +-#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) +-#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) +- +-#define EXYNOS5_DRD_PHYPARAM1 0x20 +- +-#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +-#define PHYPARAM1_PCS_TXDEEMPH (0x1c) +- +-#define EXYNOS5_DRD_PHYTERM 0x24 +- +-#define EXYNOS5_DRD_PHYTEST 0x28 +- +-#define PHYTEST_POWERDOWN_SSP BIT(3) +-#define PHYTEST_POWERDOWN_HSP BIT(2) +- +-#define EXYNOS5_DRD_PHYADP 0x2c +- +-#define EXYNOS5_DRD_PHYUTMICLKSEL 0x30 +- +-#define PHYUTMICLKSEL_UTMI_CLKSEL BIT(2) +- +-#define EXYNOS5_DRD_PHYRESUME 0x34 +-#define EXYNOS5_DRD_LINKPORT 0x44 +- +-#define KHZ 1000 +-#define MHZ (KHZ * KHZ) +- +-enum exynos5_usbdrd_phy_id { +- EXYNOS5_DRDPHY_UTMI, +- EXYNOS5_DRDPHY_PIPE3, +- EXYNOS5_DRDPHYS_NUM, +-}; +- +-struct phy_usb_instance; +-struct exynos5_usbdrd_phy; +- +-struct exynos5_usbdrd_phy_config { +- u32 id; +- void (*phy_isol)(struct phy_usb_instance *inst, u32 on); +- void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd); +- unsigned int (*set_refclk)(struct phy_usb_instance *inst); +-}; +- +-struct exynos5_usbdrd_phy_drvdata { +- const struct exynos5_usbdrd_phy_config *phy_cfg; +- u32 pmu_offset_usbdrd0_phy; +- u32 pmu_offset_usbdrd1_phy; +- bool has_common_clk_gate; +-}; +- +-/** +- * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY +- * @dev: pointer to device instance of this platform device +- * @reg_phy: usb phy controller register memory base +- * @clk: phy clock for register access +- * @pipeclk: clock for pipe3 phy +- * @utmiclk: clock for utmi+ phy +- * @itpclk: clock for ITP generation +- * @drv_data: pointer to SoC level driver data structure +- * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY +- * instances each with its 'phy' and 'phy_cfg'. +- * @extrefclk: frequency select settings when using 'separate +- * reference clocks' for SS and HS operations +- * @ref_clk: reference clock to PHY block from which PHY's +- * operational clocks are derived +- * vbus: VBUS regulator for phy +- * vbus_boost: Boost regulator for VBUS present on few Exynos boards +- */ +-struct exynos5_usbdrd_phy { +- struct device *dev; +- void __iomem *reg_phy; +- struct clk *clk; +- struct clk *pipeclk; +- struct clk *utmiclk; +- struct clk *itpclk; +- const struct exynos5_usbdrd_phy_drvdata *drv_data; +- struct phy_usb_instance { +- struct phy *phy; +- u32 index; +- struct regmap *reg_pmu; +- u32 pmu_offset; +- const struct exynos5_usbdrd_phy_config *phy_cfg; +- } phys[EXYNOS5_DRDPHYS_NUM]; +- u32 extrefclk; +- struct clk *ref_clk; +- struct regulator *vbus; +- struct regulator *vbus_boost; +-}; +- +-static inline +-struct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst) +-{ +- return container_of((inst), struct exynos5_usbdrd_phy, +- phys[(inst)->index]); +-} +- +-/* +- * exynos5_rate_to_clk() converts the supplied clock rate to the value that +- * can be written to the phy register. +- */ +-static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg) +-{ +- /* EXYNOS5_FSEL_MASK */ +- +- switch (rate) { +- case 9600 * KHZ: +- *reg = EXYNOS5_FSEL_9MHZ6; +- break; +- case 10 * MHZ: +- *reg = EXYNOS5_FSEL_10MHZ; +- break; +- case 12 * MHZ: +- *reg = EXYNOS5_FSEL_12MHZ; +- break; +- case 19200 * KHZ: +- *reg = EXYNOS5_FSEL_19MHZ2; +- break; +- case 20 * MHZ: +- *reg = EXYNOS5_FSEL_20MHZ; +- break; +- case 24 * MHZ: +- *reg = EXYNOS5_FSEL_24MHZ; +- break; +- case 50 * MHZ: +- *reg = EXYNOS5_FSEL_50MHZ; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst, +- unsigned int on) +-{ +- unsigned int val; +- +- if (!inst->reg_pmu) +- return; +- +- val = on ? 0 : EXYNOS4_PHY_ENABLE; +- +- regmap_update_bits(inst->reg_pmu, inst->pmu_offset, +- EXYNOS4_PHY_ENABLE, val); +-} +- +-/* +- * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock +- * from clock core. Further sets multiplier values and spread spectrum +- * clock settings for SuperSpeed operations. +- */ +-static unsigned int +-exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst) +-{ +- u32 reg; +- struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); +- +- /* restore any previous reference clock settings */ +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); +- +- /* Use EXTREFCLK as ref clock */ +- reg &= ~PHYCLKRST_REFCLKSEL_MASK; +- reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; +- +- /* FSEL settings corresponding to reference clock */ +- reg &= ~PHYCLKRST_FSEL_PIPE_MASK | +- PHYCLKRST_MPLL_MULTIPLIER_MASK | +- PHYCLKRST_SSC_REFCLKSEL_MASK; +- switch (phy_drd->extrefclk) { +- case EXYNOS5_FSEL_50MHZ: +- reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | +- PHYCLKRST_SSC_REFCLKSEL(0x00)); +- break; +- case EXYNOS5_FSEL_24MHZ: +- reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | +- PHYCLKRST_SSC_REFCLKSEL(0x88)); +- break; +- case EXYNOS5_FSEL_20MHZ: +- reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | +- PHYCLKRST_SSC_REFCLKSEL(0x00)); +- break; +- case EXYNOS5_FSEL_19MHZ2: +- reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | +- PHYCLKRST_SSC_REFCLKSEL(0x88)); +- break; +- default: +- dev_dbg(phy_drd->dev, "unsupported ref clk\n"); +- break; +- } +- +- return reg; +-} +- +-/* +- * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock +- * from clock core. Further sets the FSEL values for HighSpeed operations. +- */ +-static unsigned int +-exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst) +-{ +- u32 reg; +- struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); +- +- /* restore any previous reference clock settings */ +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); +- +- reg &= ~PHYCLKRST_REFCLKSEL_MASK; +- reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; +- +- reg &= ~PHYCLKRST_FSEL_UTMI_MASK | +- PHYCLKRST_MPLL_MULTIPLIER_MASK | +- PHYCLKRST_SSC_REFCLKSEL_MASK; +- reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); +- +- return reg; +-} +- +-static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd) +-{ +- u32 reg; +- +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); +- /* Set Tx De-Emphasis level */ +- reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; +- reg |= PHYPARAM1_PCS_TXDEEMPH; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); +- +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +- reg &= ~PHYTEST_POWERDOWN_SSP; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +-} +- +-static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) +-{ +- u32 reg; +- +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); +- /* Set Loss-of-Signal Detector sensitivity */ +- reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK; +- reg |= PHYPARAM0_REF_LOSLEVEL; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); +- +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); +- /* Set Tx De-Emphasis level */ +- reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; +- reg |= PHYPARAM1_PCS_TXDEEMPH; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); +- +- /* UTMI Power Control */ +- writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); +- +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +- reg &= ~PHYTEST_POWERDOWN_HSP; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +-} +- +-static int exynos5_usbdrd_phy_init(struct phy *phy) +-{ +- int ret; +- u32 reg; +- struct phy_usb_instance *inst = phy_get_drvdata(phy); +- struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); +- +- ret = clk_prepare_enable(phy_drd->clk); +- if (ret) +- return ret; +- +- /* Reset USB 3.0 PHY */ +- writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); +- writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME); +- +- /* +- * Setting the Frame length Adj value[6:1] to default 0x20 +- * See xHCI 1.0 spec, 5.2.4 +- */ +- reg = LINKSYSTEM_XHCI_VERSION_CONTROL | +- LINKSYSTEM_FLADJ(0x20); +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); +- +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); +- /* Select PHY CLK source */ +- reg &= ~PHYPARAM0_REF_USE_PAD; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); +- +- /* This bit must be set for both HS and SS operations */ +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); +- reg |= PHYUTMICLKSEL_UTMI_CLKSEL; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); +- +- /* UTMI or PIPE3 specific init */ +- inst->phy_cfg->phy_init(phy_drd); +- +- /* reference clock settings */ +- reg = inst->phy_cfg->set_refclk(inst); +- +- /* Digital power supply in normal operating mode */ +- reg |= PHYCLKRST_RETENABLEN | +- /* Enable ref clock for SS function */ +- PHYCLKRST_REF_SSP_EN | +- /* Enable spread spectrum */ +- PHYCLKRST_SSC_EN | +- /* Power down HS Bias and PLL blocks in suspend mode */ +- PHYCLKRST_COMMONONN | +- /* Reset the port */ +- PHYCLKRST_PORTRESET; +- +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); +- +- udelay(10); +- +- reg &= ~PHYCLKRST_PORTRESET; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); +- +- clk_disable_unprepare(phy_drd->clk); +- +- return 0; +-} +- +-static int exynos5_usbdrd_phy_exit(struct phy *phy) +-{ +- int ret; +- u32 reg; +- struct phy_usb_instance *inst = phy_get_drvdata(phy); +- struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); +- +- ret = clk_prepare_enable(phy_drd->clk); +- if (ret) +- return ret; +- +- reg = PHYUTMI_OTGDISABLE | +- PHYUTMI_FORCESUSPEND | +- PHYUTMI_FORCESLEEP; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); +- +- /* Resetting the PHYCLKRST enable bits to reduce leakage current */ +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); +- reg &= ~(PHYCLKRST_REF_SSP_EN | +- PHYCLKRST_SSC_EN | +- PHYCLKRST_COMMONONN); +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); +- +- /* Control PHYTEST to remove leakage current */ +- reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +- reg |= PHYTEST_POWERDOWN_SSP | +- PHYTEST_POWERDOWN_HSP; +- writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +- +- clk_disable_unprepare(phy_drd->clk); +- +- return 0; +-} +- +-static int exynos5_usbdrd_phy_power_on(struct phy *phy) +-{ +- int ret; +- struct phy_usb_instance *inst = phy_get_drvdata(phy); +- struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); +- +- dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); +- +- clk_prepare_enable(phy_drd->ref_clk); +- if (!phy_drd->drv_data->has_common_clk_gate) { +- clk_prepare_enable(phy_drd->pipeclk); +- clk_prepare_enable(phy_drd->utmiclk); +- clk_prepare_enable(phy_drd->itpclk); +- } +- +- /* Enable VBUS supply */ +- if (phy_drd->vbus_boost) { +- ret = regulator_enable(phy_drd->vbus_boost); +- if (ret) { +- dev_err(phy_drd->dev, +- "Failed to enable VBUS boost supply\n"); +- goto fail_vbus; +- } +- } +- +- if (phy_drd->vbus) { +- ret = regulator_enable(phy_drd->vbus); +- if (ret) { +- dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); +- goto fail_vbus_boost; +- } +- } +- +- /* Power-on PHY*/ +- inst->phy_cfg->phy_isol(inst, 0); +- +- return 0; +- +-fail_vbus_boost: +- if (phy_drd->vbus_boost) +- regulator_disable(phy_drd->vbus_boost); +- +-fail_vbus: +- clk_disable_unprepare(phy_drd->ref_clk); +- if (!phy_drd->drv_data->has_common_clk_gate) { +- clk_disable_unprepare(phy_drd->itpclk); +- clk_disable_unprepare(phy_drd->utmiclk); +- clk_disable_unprepare(phy_drd->pipeclk); +- } +- +- return ret; +-} +- +-static int exynos5_usbdrd_phy_power_off(struct phy *phy) +-{ +- struct phy_usb_instance *inst = phy_get_drvdata(phy); +- struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); +- +- dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); +- +- /* Power-off the PHY */ +- inst->phy_cfg->phy_isol(inst, 1); +- +- /* Disable VBUS supply */ +- if (phy_drd->vbus) +- regulator_disable(phy_drd->vbus); +- if (phy_drd->vbus_boost) +- regulator_disable(phy_drd->vbus_boost); +- +- clk_disable_unprepare(phy_drd->ref_clk); +- if (!phy_drd->drv_data->has_common_clk_gate) { +- clk_disable_unprepare(phy_drd->itpclk); +- clk_disable_unprepare(phy_drd->pipeclk); +- clk_disable_unprepare(phy_drd->utmiclk); +- } +- +- return 0; +-} +- +-static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev); +- +- if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM)) +- return ERR_PTR(-ENODEV); +- +- return phy_drd->phys[args->args[0]].phy; +-} +- +-static const struct phy_ops exynos5_usbdrd_phy_ops = { +- .init = exynos5_usbdrd_phy_init, +- .exit = exynos5_usbdrd_phy_exit, +- .power_on = exynos5_usbdrd_phy_power_on, +- .power_off = exynos5_usbdrd_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) +-{ +- unsigned long ref_rate; +- int ret; +- +- phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); +- if (IS_ERR(phy_drd->clk)) { +- dev_err(phy_drd->dev, "Failed to get phy clock\n"); +- return PTR_ERR(phy_drd->clk); +- } +- +- phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); +- if (IS_ERR(phy_drd->ref_clk)) { +- dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); +- return PTR_ERR(phy_drd->ref_clk); +- } +- ref_rate = clk_get_rate(phy_drd->ref_clk); +- +- ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); +- if (ret) { +- dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", +- ref_rate); +- return ret; +- } +- +- if (!phy_drd->drv_data->has_common_clk_gate) { +- phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); +- if (IS_ERR(phy_drd->pipeclk)) { +- dev_info(phy_drd->dev, +- "PIPE3 phy operational clock not specified\n"); +- phy_drd->pipeclk = NULL; +- } +- +- phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); +- if (IS_ERR(phy_drd->utmiclk)) { +- dev_info(phy_drd->dev, +- "UTMI phy operational clock not specified\n"); +- phy_drd->utmiclk = NULL; +- } +- +- phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); +- if (IS_ERR(phy_drd->itpclk)) { +- dev_info(phy_drd->dev, +- "ITP clock from main OSC not specified\n"); +- phy_drd->itpclk = NULL; +- } +- } +- +- return 0; +-} +- +-static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { +- { +- .id = EXYNOS5_DRDPHY_UTMI, +- .phy_isol = exynos5_usbdrd_phy_isol, +- .phy_init = exynos5_usbdrd_utmi_init, +- .set_refclk = exynos5_usbdrd_utmi_set_refclk, +- }, +- { +- .id = EXYNOS5_DRDPHY_PIPE3, +- .phy_isol = exynos5_usbdrd_phy_isol, +- .phy_init = exynos5_usbdrd_pipe3_init, +- .set_refclk = exynos5_usbdrd_pipe3_set_refclk, +- }, +-}; +- +-static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { +- .phy_cfg = phy_cfg_exynos5, +- .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, +- .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, +- .has_common_clk_gate = true, +-}; +- +-static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { +- .phy_cfg = phy_cfg_exynos5, +- .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, +- .has_common_clk_gate = true, +-}; +- +-static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = { +- .phy_cfg = phy_cfg_exynos5, +- .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, +- .pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL, +- .has_common_clk_gate = false, +-}; +- +-static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { +- .phy_cfg = phy_cfg_exynos5, +- .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, +- .has_common_clk_gate = false, +-}; +- +-static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { +- { +- .compatible = "samsung,exynos5250-usbdrd-phy", +- .data = &exynos5250_usbdrd_phy +- }, { +- .compatible = "samsung,exynos5420-usbdrd-phy", +- .data = &exynos5420_usbdrd_phy +- }, { +- .compatible = "samsung,exynos5433-usbdrd-phy", +- .data = &exynos5433_usbdrd_phy +- }, { +- .compatible = "samsung,exynos7-usbdrd-phy", +- .data = &exynos7_usbdrd_phy +- }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match); +- +-static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct device_node *node = dev->of_node; +- struct exynos5_usbdrd_phy *phy_drd; +- struct phy_provider *phy_provider; +- struct resource *res; +- const struct of_device_id *match; +- const struct exynos5_usbdrd_phy_drvdata *drv_data; +- struct regmap *reg_pmu; +- u32 pmu_offset; +- int i, ret; +- int channel; +- +- phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL); +- if (!phy_drd) +- return -ENOMEM; +- +- dev_set_drvdata(dev, phy_drd); +- phy_drd->dev = dev; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- phy_drd->reg_phy = devm_ioremap_resource(dev, res); +- if (IS_ERR(phy_drd->reg_phy)) +- return PTR_ERR(phy_drd->reg_phy); +- +- match = of_match_node(exynos5_usbdrd_phy_of_match, pdev->dev.of_node); +- +- drv_data = match->data; +- phy_drd->drv_data = drv_data; +- +- ret = exynos5_usbdrd_phy_clk_handle(phy_drd); +- if (ret) { +- dev_err(dev, "Failed to initialize clocks\n"); +- return ret; +- } +- +- 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); +- } +- +- /* +- * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with +- * each having separate power control registers. +- * 'channel' facilitates to set such registers. +- */ +- channel = of_alias_get_id(node, "usbdrdphy"); +- if (channel < 0) +- dev_dbg(dev, "Not a multi-controller usbdrd phy\n"); +- +- switch (channel) { +- case 1: +- pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy; +- break; +- case 0: +- default: +- pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy; +- break; +- } +- +- /* Get Vbus regulators */ +- phy_drd->vbus = devm_regulator_get(dev, "vbus"); +- if (IS_ERR(phy_drd->vbus)) { +- ret = PTR_ERR(phy_drd->vbus); +- if (ret == -EPROBE_DEFER) +- return ret; +- +- dev_warn(dev, "Failed to get VBUS supply regulator\n"); +- phy_drd->vbus = NULL; +- } +- +- phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); +- if (IS_ERR(phy_drd->vbus_boost)) { +- ret = PTR_ERR(phy_drd->vbus_boost); +- if (ret == -EPROBE_DEFER) +- return ret; +- +- dev_warn(dev, "Failed to get VBUS boost supply regulator\n"); +- phy_drd->vbus_boost = NULL; +- } +- +- dev_vdbg(dev, "Creating usbdrd_phy phy\n"); +- +- for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { +- struct phy *phy = devm_phy_create(dev, NULL, +- &exynos5_usbdrd_phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "Failed to create usbdrd_phy phy\n"); +- return PTR_ERR(phy); +- } +- +- phy_drd->phys[i].phy = phy; +- phy_drd->phys[i].index = i; +- phy_drd->phys[i].reg_pmu = reg_pmu; +- phy_drd->phys[i].pmu_offset = pmu_offset; +- phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; +- phy_set_drvdata(phy, &phy_drd->phys[i]); +- } +- +- 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); +- } +- +- return 0; +-} +- +-static struct platform_driver exynos5_usb3drd_phy = { +- .probe = exynos5_usbdrd_phy_probe, +- .driver = { +- .of_match_table = exynos5_usbdrd_phy_of_match, +- .name = "exynos5_usb3drd_phy", +- } +-}; +- +-module_platform_driver(exynos5_usb3drd_phy); +-MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver"); +-MODULE_AUTHOR("Vivek Gautam "); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:exynos5_usb3drd_phy"); +diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c +deleted file mode 100644 +index 60e13afcd9b8..000000000000 +--- a/drivers/phy/phy-exynos5250-sata.c ++++ /dev/null +@@ -1,250 +0,0 @@ +-/* +- * Samsung SATA SerDes(PHY) driver +- * +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Authors: Girish K S +- * Yuvaraj Kumar C D +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define SATAPHY_CONTROL_OFFSET 0x0724 +-#define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0) +-#define EXYNOS5_SATA_RESET 0x4 +-#define RESET_GLOBAL_RST_N BIT(0) +-#define RESET_CMN_RST_N BIT(1) +-#define RESET_CMN_BLOCK_RST_N BIT(2) +-#define RESET_CMN_I2C_RST_N BIT(3) +-#define RESET_TX_RX_PIPE_RST_N BIT(4) +-#define RESET_TX_RX_BLOCK_RST_N BIT(5) +-#define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7)) +-#define LINK_RESET 0xf0000 +-#define EXYNOS5_SATA_MODE0 0x10 +-#define SATA_SPD_GEN3 BIT(1) +-#define EXYNOS5_SATA_CTRL0 0x14 +-#define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9) +-#define CTRL0_P0_PHY_CALIBRATED BIT(8) +-#define EXYNOS5_SATA_PHSATA_CTRLM 0xe0 +-#define PHCTRLM_REF_RATE BIT(1) +-#define PHCTRLM_HIGH_SPEED BIT(0) +-#define EXYNOS5_SATA_PHSATA_STATM 0xf0 +-#define PHSTATM_PLL_LOCKED BIT(0) +- +-#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000)) +- +-struct exynos_sata_phy { +- struct phy *phy; +- struct clk *phyclk; +- void __iomem *regs; +- struct regmap *pmureg; +- struct i2c_client *client; +-}; +- +-static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit, +- u32 status) +-{ +- unsigned long timeout = jiffies + PHY_PLL_TIMEOUT; +- +- while (time_before(jiffies, timeout)) { +- if ((readl(base + reg) & checkbit) == status) +- return 0; +- } +- +- return -EFAULT; +-} +- +-static int exynos_sata_phy_power_on(struct phy *phy) +-{ +- struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); +- +- return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, +- EXYNOS5_SATAPHY_PMU_ENABLE, true); +- +-} +- +-static int exynos_sata_phy_power_off(struct phy *phy) +-{ +- struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); +- +- return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, +- EXYNOS5_SATAPHY_PMU_ENABLE, false); +- +-} +- +-static int exynos_sata_phy_init(struct phy *phy) +-{ +- u32 val = 0; +- int ret = 0; +- u8 buf[] = { 0x3a, 0x0b }; +- struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); +- +- ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, +- EXYNOS5_SATAPHY_PMU_ENABLE, true); +- if (ret != 0) +- dev_err(&sata_phy->phy->dev, "phy init failed\n"); +- +- writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); +- +- val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); +- val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N +- | RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N +- | RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N; +- writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); +- +- val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); +- val |= LINK_RESET; +- writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); +- +- val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); +- val |= RESET_CMN_RST_N; +- writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); +- +- val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); +- val &= ~PHCTRLM_REF_RATE; +- writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); +- +- /* High speed enable for Gen3 */ +- val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); +- val |= PHCTRLM_HIGH_SPEED; +- writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); +- +- val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0); +- val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED; +- writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0); +- +- val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0); +- val |= SATA_SPD_GEN3; +- writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0); +- +- ret = i2c_master_send(sata_phy->client, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- /* release cmu reset */ +- val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); +- val &= ~RESET_CMN_RST_N; +- writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); +- +- val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); +- val |= RESET_CMN_RST_N; +- writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); +- +- ret = wait_for_reg_status(sata_phy->regs, +- EXYNOS5_SATA_PHSATA_STATM, +- PHSTATM_PLL_LOCKED, 1); +- if (ret < 0) +- dev_err(&sata_phy->phy->dev, +- "PHY PLL locking failed\n"); +- return ret; +-} +- +-static const struct phy_ops exynos_sata_phy_ops = { +- .init = exynos_sata_phy_init, +- .power_on = exynos_sata_phy_power_on, +- .power_off = exynos_sata_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int exynos_sata_phy_probe(struct platform_device *pdev) +-{ +- struct exynos_sata_phy *sata_phy; +- struct device *dev = &pdev->dev; +- struct resource *res; +- struct phy_provider *phy_provider; +- struct device_node *node; +- int ret = 0; +- +- sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL); +- if (!sata_phy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- +- sata_phy->regs = devm_ioremap_resource(dev, res); +- if (IS_ERR(sata_phy->regs)) +- return PTR_ERR(sata_phy->regs); +- +- sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, +- "samsung,syscon-phandle"); +- if (IS_ERR(sata_phy->pmureg)) { +- dev_err(dev, "syscon regmap lookup failed.\n"); +- return PTR_ERR(sata_phy->pmureg); +- } +- +- node = of_parse_phandle(dev->of_node, +- "samsung,exynos-sataphy-i2c-phandle", 0); +- if (!node) +- return -EINVAL; +- +- sata_phy->client = of_find_i2c_device_by_node(node); +- if (!sata_phy->client) +- return -EPROBE_DEFER; +- +- dev_set_drvdata(dev, sata_phy); +- +- sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl"); +- if (IS_ERR(sata_phy->phyclk)) { +- dev_err(dev, "failed to get clk for PHY\n"); +- return PTR_ERR(sata_phy->phyclk); +- } +- +- ret = clk_prepare_enable(sata_phy->phyclk); +- if (ret < 0) { +- dev_err(dev, "failed to enable source clk\n"); +- return ret; +- } +- +- sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops); +- if (IS_ERR(sata_phy->phy)) { +- clk_disable_unprepare(sata_phy->phyclk); +- dev_err(dev, "failed to create PHY\n"); +- return PTR_ERR(sata_phy->phy); +- } +- +- phy_set_drvdata(sata_phy->phy, sata_phy); +- +- phy_provider = devm_of_phy_provider_register(dev, +- of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) { +- clk_disable_unprepare(sata_phy->phyclk); +- return PTR_ERR(phy_provider); +- } +- +- return 0; +-} +- +-static const struct of_device_id exynos_sata_phy_of_match[] = { +- { .compatible = "samsung,exynos5250-sata-phy" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match); +- +-static struct platform_driver exynos_sata_phy_driver = { +- .probe = exynos_sata_phy_probe, +- .driver = { +- .of_match_table = exynos_sata_phy_of_match, +- .name = "samsung,sata-phy", +- } +-}; +-module_platform_driver(exynos_sata_phy_driver); +- +-MODULE_DESCRIPTION("Samsung SerDes PHY driver"); +-MODULE_LICENSE("GPL v2"); +-MODULE_AUTHOR("Girish K S "); +-MODULE_AUTHOR("Yuvaraj C D "); +diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c +deleted file mode 100644 +index aad806272305..000000000000 +--- a/drivers/phy/phy-exynos5250-usb2.c ++++ /dev/null +@@ -1,401 +0,0 @@ +-/* +- * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support +- * +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Author: Kamil Debski +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include "phy-samsung-usb2.h" +- +-/* Exynos USB PHY registers */ +-#define EXYNOS_5250_REFCLKSEL_CRYSTAL 0x0 +-#define EXYNOS_5250_REFCLKSEL_XO 0x1 +-#define EXYNOS_5250_REFCLKSEL_CLKCORE 0x2 +- +-#define EXYNOS_5250_FSEL_9MHZ6 0x0 +-#define EXYNOS_5250_FSEL_10MHZ 0x1 +-#define EXYNOS_5250_FSEL_12MHZ 0x2 +-#define EXYNOS_5250_FSEL_19MHZ2 0x3 +-#define EXYNOS_5250_FSEL_20MHZ 0x4 +-#define EXYNOS_5250_FSEL_24MHZ 0x5 +-#define EXYNOS_5250_FSEL_50MHZ 0x7 +- +-/* Normal host */ +-#define EXYNOS_5250_HOSTPHYCTRL0 0x0 +- +-#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL BIT(31) +-#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT 19 +-#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK \ +- (0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT) +-#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT 16 +-#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \ +- (0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT) +-#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN BIT(11) +-#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE BIT(10) +-#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N BIT(9) +-#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK (0x3 << 7) +-#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL (0x0 << 7) +-#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0 (0x1 << 7) +-#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST (0x2 << 7) +-#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ BIT(6) +-#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP BIT(5) +-#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND BIT(4) +-#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE BIT(3) +-#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST BIT(2) +-#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST BIT(1) +-#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST BIT(0) +- +-/* HSIC0 & HSIC1 */ +-#define EXYNOS_5250_HSICPHYCTRL1 0x10 +-#define EXYNOS_5250_HSICPHYCTRL2 0x20 +- +-#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK (0x3 << 23) +-#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT (0x2 << 23) +-#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK (0x7f << 16) +-#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 (0x24 << 16) +-#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15 (0x1c << 16) +-#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16 (0x1a << 16) +-#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2 (0x15 << 16) +-#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20 (0x14 << 16) +-#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ BIT(6) +-#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP BIT(5) +-#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND BIT(4) +-#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE BIT(3) +-#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST BIT(2) +-#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST BIT(0) +- +-/* EHCI control */ +-#define EXYNOS_5250_HOSTEHCICTRL 0x30 +-#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN BIT(29) +-#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 BIT(28) +-#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 BIT(27) +-#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16 BIT(26) +-#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN BIT(25) +-#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT 19 +-#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \ +- (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT) +-#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT 13 +-#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK \ +- (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT) +-#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT 7 +-#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \ +- (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT) +-#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT 1 +-#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \ +- (0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT) +-#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE BIT(0) +- +-/* OHCI control */ +-#define EXYNOS_5250_HOSTOHCICTRL 0x34 +-#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT 1 +-#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \ +- (0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT) +-#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN BIT(0) +- +-/* USBOTG */ +-#define EXYNOS_5250_USBOTGSYS 0x38 +-#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET BIT(14) +-#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG BIT(13) +-#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST BIT(12) +-#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT 9 +-#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \ +- (0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT) +-#define EXYNOS_5250_USBOTGSYS_ID_PULLUP BIT(8) +-#define EXYNOS_5250_USBOTGSYS_COMMON_ON BIT(7) +-#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT 4 +-#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \ +- (0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT) +-#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP BIT(3) +-#define EXYNOS_5250_USBOTGSYS_OTGDISABLE BIT(2) +-#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG BIT(1) +-#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND BIT(0) +- +-/* Isolation, configured in the power management unit */ +-#define EXYNOS_5250_USB_ISOL_OTG_OFFSET 0x704 +-#define EXYNOS_5250_USB_ISOL_OTG BIT(0) +-#define EXYNOS_5250_USB_ISOL_HOST_OFFSET 0x708 +-#define EXYNOS_5250_USB_ISOL_HOST BIT(0) +- +-/* Mode swtich register */ +-#define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230 +-#define EXYNOS_5250_MODE_SWITCH_MASK 1 +-#define EXYNOS_5250_MODE_SWITCH_DEVICE 0 +-#define EXYNOS_5250_MODE_SWITCH_HOST 1 +- +-enum exynos4x12_phy_id { +- EXYNOS5250_DEVICE, +- EXYNOS5250_HOST, +- EXYNOS5250_HSIC0, +- EXYNOS5250_HSIC1, +- EXYNOS5250_NUM_PHYS, +-}; +- +-/* +- * exynos5250_rate_to_clk() converts the supplied clock rate to the value that +- * can be written to the phy register. +- */ +-static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg) +-{ +- /* EXYNOS_5250_FSEL_MASK */ +- +- switch (rate) { +- case 9600 * KHZ: +- *reg = EXYNOS_5250_FSEL_9MHZ6; +- break; +- case 10 * MHZ: +- *reg = EXYNOS_5250_FSEL_10MHZ; +- break; +- case 12 * MHZ: +- *reg = EXYNOS_5250_FSEL_12MHZ; +- break; +- case 19200 * KHZ: +- *reg = EXYNOS_5250_FSEL_19MHZ2; +- break; +- case 20 * MHZ: +- *reg = EXYNOS_5250_FSEL_20MHZ; +- break; +- case 24 * MHZ: +- *reg = EXYNOS_5250_FSEL_24MHZ; +- break; +- case 50 * MHZ: +- *reg = EXYNOS_5250_FSEL_50MHZ; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 offset; +- u32 mask; +- +- switch (inst->cfg->id) { +- case EXYNOS5250_DEVICE: +- offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET; +- mask = EXYNOS_5250_USB_ISOL_OTG; +- break; +- case EXYNOS5250_HOST: +- offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET; +- mask = EXYNOS_5250_USB_ISOL_HOST; +- break; +- default: +- return; +- } +- +- regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); +-} +- +-static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 ctrl0; +- u32 otg; +- u32 ehci; +- u32 ohci; +- u32 hsic; +- +- switch (inst->cfg->id) { +- case EXYNOS5250_DEVICE: +- regmap_update_bits(drv->reg_sys, +- EXYNOS_5250_MODE_SWITCH_OFFSET, +- EXYNOS_5250_MODE_SWITCH_MASK, +- EXYNOS_5250_MODE_SWITCH_DEVICE); +- +- /* OTG configuration */ +- otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); +- /* The clock */ +- otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK; +- otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT; +- /* Reset */ +- otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | +- EXYNOS_5250_USBOTGSYS_FORCE_SLEEP | +- EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG); +- otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST | +- EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | +- EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | +- EXYNOS_5250_USBOTGSYS_OTGDISABLE; +- /* Ref clock */ +- otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK; +- otg |= EXYNOS_5250_REFCLKSEL_CLKCORE << +- EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT; +- writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); +- udelay(100); +- otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST | +- EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | +- EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | +- EXYNOS_5250_USBOTGSYS_OTGDISABLE); +- writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); +- +- +- break; +- case EXYNOS5250_HOST: +- case EXYNOS5250_HSIC0: +- case EXYNOS5250_HSIC1: +- /* Host registers configuration */ +- ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); +- /* The clock */ +- ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK; +- ctrl0 |= drv->ref_reg_val << +- EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT; +- +- /* Reset */ +- ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST | +- EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL | +- EXYNOS_5250_HOSTPHYCTRL0_SIDDQ | +- EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND | +- EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP); +- ctrl0 |= EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST | +- EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST | +- EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N; +- writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); +- udelay(10); +- ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST | +- EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST); +- writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); +- +- /* OTG configuration */ +- otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); +- /* The clock */ +- otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK; +- otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT; +- /* Reset */ +- otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | +- EXYNOS_5250_USBOTGSYS_FORCE_SLEEP | +- EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG); +- otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST | +- EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | +- EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | +- EXYNOS_5250_USBOTGSYS_OTGDISABLE; +- /* Ref clock */ +- otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK; +- otg |= EXYNOS_5250_REFCLKSEL_CLKCORE << +- EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT; +- writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); +- udelay(10); +- otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST | +- EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | +- EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET); +- +- /* HSIC phy configuration */ +- hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 | +- EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT | +- EXYNOS_5250_HSICPHYCTRLX_PHYSWRST); +- writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); +- writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); +- udelay(10); +- hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST; +- writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); +- writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); +- /* The following delay is necessary for the reset sequence to be +- * completed */ +- udelay(80); +- +- /* Enable EHCI DMA burst */ +- ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL); +- ehci |= EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN | +- EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 | +- EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 | +- EXYNOS_5250_HOSTEHCICTRL_ENAINCR16; +- writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL); +- +- /* OHCI settings */ +- ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL); +- /* Following code is based on the old driver */ +- ohci |= 0x1 << 3; +- writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL); +- +- break; +- } +- exynos5250_isol(inst, 0); +- +- return 0; +-} +- +-static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 ctrl0; +- u32 otg; +- u32 hsic; +- +- exynos5250_isol(inst, 1); +- +- switch (inst->cfg->id) { +- case EXYNOS5250_DEVICE: +- otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); +- otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | +- EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG | +- EXYNOS_5250_USBOTGSYS_FORCE_SLEEP); +- writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); +- break; +- case EXYNOS5250_HOST: +- ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); +- ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ | +- EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND | +- EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP | +- EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST | +- EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL); +- writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); +- break; +- case EXYNOS5250_HSIC0: +- case EXYNOS5250_HSIC1: +- hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 | +- EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT | +- EXYNOS_5250_HSICPHYCTRLX_SIDDQ | +- EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP | +- EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND +- ); +- writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); +- writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); +- break; +- } +- +- return 0; +-} +- +- +-static const struct samsung_usb2_common_phy exynos5250_phys[] = { +- { +- .label = "device", +- .id = EXYNOS5250_DEVICE, +- .power_on = exynos5250_power_on, +- .power_off = exynos5250_power_off, +- }, +- { +- .label = "host", +- .id = EXYNOS5250_HOST, +- .power_on = exynos5250_power_on, +- .power_off = exynos5250_power_off, +- }, +- { +- .label = "hsic0", +- .id = EXYNOS5250_HSIC0, +- .power_on = exynos5250_power_on, +- .power_off = exynos5250_power_off, +- }, +- { +- .label = "hsic1", +- .id = EXYNOS5250_HSIC1, +- .power_on = exynos5250_power_on, +- .power_off = exynos5250_power_off, +- }, +-}; +- +-const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = { +- .has_mode_switch = 1, +- .num_phys = EXYNOS5250_NUM_PHYS, +- .phys = exynos5250_phys, +- .rate_to_clk = exynos5250_rate_to_clk, +-}; +diff --git a/drivers/phy/phy-hi6220-usb.c b/drivers/phy/phy-hi6220-usb.c +deleted file mode 100644 +index 398c1021deec..000000000000 +--- a/drivers/phy/phy-hi6220-usb.c ++++ /dev/null +@@ -1,168 +0,0 @@ +-/* +- * Copyright (c) 2015 Linaro Ltd. +- * Copyright (c) 2015 Hisilicon Limited. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#define SC_PERIPH_CTRL4 0x00c +- +-#define CTRL4_PICO_SIDDQ BIT(6) +-#define CTRL4_PICO_OGDISABLE BIT(8) +-#define CTRL4_PICO_VBUSVLDEXT BIT(10) +-#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11) +-#define CTRL4_OTG_PHY_SEL BIT(21) +- +-#define SC_PERIPH_CTRL5 0x010 +- +-#define CTRL5_USBOTG_RES_SEL BIT(3) +-#define CTRL5_PICOPHY_ACAENB BIT(4) +-#define CTRL5_PICOPHY_BC_MODE BIT(5) +-#define CTRL5_PICOPHY_CHRGSEL BIT(6) +-#define CTRL5_PICOPHY_VDATSRCEND BIT(7) +-#define CTRL5_PICOPHY_VDATDETENB BIT(8) +-#define CTRL5_PICOPHY_DCDENB BIT(9) +-#define CTRL5_PICOPHY_IDDIG BIT(10) +- +-#define SC_PERIPH_CTRL8 0x018 +-#define SC_PERIPH_RSTEN0 0x300 +-#define SC_PERIPH_RSTDIS0 0x304 +- +-#define RST0_USBOTG_BUS BIT(4) +-#define RST0_POR_PICOPHY BIT(5) +-#define RST0_USBOTG BIT(6) +-#define RST0_USBOTG_32K BIT(7) +- +-#define EYE_PATTERN_PARA 0x7053348c +- +-struct hi6220_priv { +- struct regmap *reg; +- struct device *dev; +-}; +- +-static void hi6220_phy_init(struct hi6220_priv *priv) +-{ +- struct regmap *reg = priv->reg; +- u32 val, mask; +- +- val = RST0_USBOTG_BUS | RST0_POR_PICOPHY | +- RST0_USBOTG | RST0_USBOTG_32K; +- mask = val; +- regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val); +- regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val); +-} +- +-static int hi6220_phy_setup(struct hi6220_priv *priv, bool on) +-{ +- struct regmap *reg = priv->reg; +- u32 val, mask; +- int ret; +- +- if (on) { +- val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB; +- mask = val | CTRL5_PICOPHY_BC_MODE; +- ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val); +- if (ret) +- goto out; +- +- val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL | +- CTRL4_OTG_PHY_SEL; +- mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE; +- ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); +- if (ret) +- goto out; +- +- ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA); +- if (ret) +- goto out; +- } else { +- val = CTRL4_PICO_SIDDQ; +- mask = val; +- ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); +- if (ret) +- goto out; +- } +- +- return 0; +-out: +- dev_err(priv->dev, "failed to setup phy ret: %d\n", ret); +- return ret; +-} +- +-static int hi6220_phy_start(struct phy *phy) +-{ +- struct hi6220_priv *priv = phy_get_drvdata(phy); +- +- return hi6220_phy_setup(priv, true); +-} +- +-static int hi6220_phy_exit(struct phy *phy) +-{ +- struct hi6220_priv *priv = phy_get_drvdata(phy); +- +- return hi6220_phy_setup(priv, false); +-} +- +-static const struct phy_ops hi6220_phy_ops = { +- .init = hi6220_phy_start, +- .exit = hi6220_phy_exit, +- .owner = THIS_MODULE, +-}; +- +-static int hi6220_phy_probe(struct platform_device *pdev) +-{ +- struct phy_provider *phy_provider; +- struct device *dev = &pdev->dev; +- struct phy *phy; +- struct hi6220_priv *priv; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->dev = dev; +- priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node, +- "hisilicon,peripheral-syscon"); +- if (IS_ERR(priv->reg)) { +- dev_err(dev, "no hisilicon,peripheral-syscon\n"); +- return PTR_ERR(priv->reg); +- } +- +- hi6220_phy_init(priv); +- +- phy = devm_phy_create(dev, NULL, &hi6220_phy_ops); +- if (IS_ERR(phy)) +- return PTR_ERR(phy); +- +- phy_set_drvdata(phy, priv); +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id hi6220_phy_of_match[] = { +- {.compatible = "hisilicon,hi6220-usb-phy",}, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, hi6220_phy_of_match); +- +-static struct platform_driver hi6220_phy_driver = { +- .probe = hi6220_phy_probe, +- .driver = { +- .name = "hi6220-usb-phy", +- .of_match_table = hi6220_phy_of_match, +- } +-}; +-module_platform_driver(hi6220_phy_driver); +- +-MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver"); +-MODULE_ALIAS("platform:hi6220-usb-phy"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c +deleted file mode 100644 +index e5ab3aa78b9d..000000000000 +--- a/drivers/phy/phy-hix5hd2-sata.c ++++ /dev/null +@@ -1,191 +0,0 @@ +-/* +- * Copyright (c) 2014 Linaro Ltd. +- * Copyright (c) 2014 Hisilicon Limited. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define SATA_PHY0_CTLL 0xa0 +-#define MPLL_MULTIPLIER_SHIFT 1 +-#define MPLL_MULTIPLIER_MASK 0xfe +-#define MPLL_MULTIPLIER_50M 0x3c +-#define MPLL_MULTIPLIER_100M 0x1e +-#define PHY_RESET BIT(0) +-#define REF_SSP_EN BIT(9) +-#define SSC_EN BIT(10) +-#define REF_USE_PAD BIT(23) +- +-#define SATA_PORT_PHYCTL 0x174 +-#define SPEED_MODE_MASK 0x6f0000 +-#define HALF_RATE_SHIFT 16 +-#define PHY_CONFIG_SHIFT 18 +-#define GEN2_EN_SHIFT 21 +-#define SPEED_CTRL BIT(20) +- +-#define SATA_PORT_PHYCTL1 0x148 +-#define AMPLITUDE_MASK 0x3ffffe +-#define AMPLITUDE_GEN3 0x68 +-#define AMPLITUDE_GEN3_SHIFT 15 +-#define AMPLITUDE_GEN2 0x56 +-#define AMPLITUDE_GEN2_SHIFT 8 +-#define AMPLITUDE_GEN1 0x56 +-#define AMPLITUDE_GEN1_SHIFT 1 +- +-#define SATA_PORT_PHYCTL2 0x14c +-#define PREEMPH_MASK 0x3ffff +-#define PREEMPH_GEN3 0x20 +-#define PREEMPH_GEN3_SHIFT 12 +-#define PREEMPH_GEN2 0x15 +-#define PREEMPH_GEN2_SHIFT 6 +-#define PREEMPH_GEN1 0x5 +-#define PREEMPH_GEN1_SHIFT 0 +- +-struct hix5hd2_priv { +- void __iomem *base; +- struct regmap *peri_ctrl; +-}; +- +-enum phy_speed_mode { +- SPEED_MODE_GEN1 = 0, +- SPEED_MODE_GEN2 = 1, +- SPEED_MODE_GEN3 = 2, +-}; +- +-static int hix5hd2_sata_phy_init(struct phy *phy) +-{ +- struct hix5hd2_priv *priv = phy_get_drvdata(phy); +- u32 val, data[2]; +- int ret; +- +- if (priv->peri_ctrl) { +- ret = of_property_read_u32_array(phy->dev.of_node, +- "hisilicon,power-reg", +- &data[0], 2); +- if (ret) { +- dev_err(&phy->dev, "Fail read hisilicon,power-reg\n"); +- return ret; +- } +- +- regmap_update_bits(priv->peri_ctrl, data[0], +- BIT(data[1]), BIT(data[1])); +- } +- +- /* reset phy */ +- val = readl_relaxed(priv->base + SATA_PHY0_CTLL); +- val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD); +- val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT | +- REF_SSP_EN | PHY_RESET; +- writel_relaxed(val, priv->base + SATA_PHY0_CTLL); +- msleep(20); +- val &= ~PHY_RESET; +- writel_relaxed(val, priv->base + SATA_PHY0_CTLL); +- +- val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1); +- val &= ~AMPLITUDE_MASK; +- val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT | +- AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT | +- AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT; +- writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1); +- +- val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2); +- val &= ~PREEMPH_MASK; +- val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT | +- PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT | +- PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT; +- writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2); +- +- /* ensure PHYCTRL setting takes effect */ +- val = readl_relaxed(priv->base + SATA_PORT_PHYCTL); +- val &= ~SPEED_MODE_MASK; +- val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT | +- SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT | +- SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL; +- writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); +- +- msleep(20); +- val &= ~SPEED_MODE_MASK; +- val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT | +- SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT | +- SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL; +- writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); +- +- val &= ~(SPEED_MODE_MASK | SPEED_CTRL); +- val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT | +- SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT | +- SPEED_MODE_GEN2 << GEN2_EN_SHIFT; +- writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); +- +- return 0; +-} +- +-static const struct phy_ops hix5hd2_sata_phy_ops = { +- .init = hix5hd2_sata_phy_init, +- .owner = THIS_MODULE, +-}; +- +-static int hix5hd2_sata_phy_probe(struct platform_device *pdev) +-{ +- struct phy_provider *phy_provider; +- struct device *dev = &pdev->dev; +- struct resource *res; +- struct phy *phy; +- struct hix5hd2_priv *priv; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) +- return -EINVAL; +- +- priv->base = devm_ioremap(dev, res->start, resource_size(res)); +- if (!priv->base) +- return -ENOMEM; +- +- priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node, +- "hisilicon,peripheral-syscon"); +- if (IS_ERR(priv->peri_ctrl)) +- priv->peri_ctrl = NULL; +- +- phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create PHY\n"); +- return PTR_ERR(phy); +- } +- +- phy_set_drvdata(phy, priv); +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id hix5hd2_sata_phy_of_match[] = { +- {.compatible = "hisilicon,hix5hd2-sata-phy",}, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match); +- +-static struct platform_driver hix5hd2_sata_phy_driver = { +- .probe = hix5hd2_sata_phy_probe, +- .driver = { +- .name = "hix5hd2-sata-phy", +- .of_match_table = hix5hd2_sata_phy_of_match, +- } +-}; +-module_platform_driver(hix5hd2_sata_phy_driver); +- +-MODULE_AUTHOR("Jiancheng Xue "); +-MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver"); +-MODULE_ALIAS("platform:hix5hd2-sata-phy"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-meson8b-usb2.c b/drivers/phy/phy-meson8b-usb2.c +deleted file mode 100644 +index 30f56a6a411f..000000000000 +--- a/drivers/phy/phy-meson8b-usb2.c ++++ /dev/null +@@ -1,286 +0,0 @@ +-/* +- * Meson8b and GXBB USB2 PHY driver +- * +- * Copyright (C) 2016 Martin Blumenstingl +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program. If not, see . +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define REG_CONFIG 0x00 +- #define REG_CONFIG_CLK_EN BIT(0) +- #define REG_CONFIG_CLK_SEL_MASK GENMASK(3, 1) +- #define REG_CONFIG_CLK_DIV_MASK GENMASK(10, 4) +- #define REG_CONFIG_CLK_32k_ALTSEL BIT(15) +- #define REG_CONFIG_TEST_TRIG BIT(31) +- +-#define REG_CTRL 0x04 +- #define REG_CTRL_SOFT_PRST BIT(0) +- #define REG_CTRL_SOFT_HRESET BIT(1) +- #define REG_CTRL_SS_SCALEDOWN_MODE_MASK GENMASK(3, 2) +- #define REG_CTRL_CLK_DET_RST BIT(4) +- #define REG_CTRL_INTR_SEL BIT(5) +- #define REG_CTRL_CLK_DETECTED BIT(8) +- #define REG_CTRL_SOF_SENT_RCVD_TGL BIT(9) +- #define REG_CTRL_SOF_TOGGLE_OUT BIT(10) +- #define REG_CTRL_POWER_ON_RESET BIT(15) +- #define REG_CTRL_SLEEPM BIT(16) +- #define REG_CTRL_TX_BITSTUFF_ENN_H BIT(17) +- #define REG_CTRL_TX_BITSTUFF_ENN BIT(18) +- #define REG_CTRL_COMMON_ON BIT(19) +- #define REG_CTRL_REF_CLK_SEL_MASK GENMASK(21, 20) +- #define REG_CTRL_REF_CLK_SEL_SHIFT 20 +- #define REG_CTRL_FSEL_MASK GENMASK(24, 22) +- #define REG_CTRL_FSEL_SHIFT 22 +- #define REG_CTRL_PORT_RESET BIT(25) +- #define REG_CTRL_THREAD_ID_MASK GENMASK(31, 26) +- +-#define REG_ENDP_INTR 0x08 +- +-/* bits [31:26], [24:21] and [15:3] seem to be read-only */ +-#define REG_ADP_BC 0x0c +- #define REG_ADP_BC_VBUS_VLD_EXT_SEL BIT(0) +- #define REG_ADP_BC_VBUS_VLD_EXT BIT(1) +- #define REG_ADP_BC_OTG_DISABLE BIT(2) +- #define REG_ADP_BC_ID_PULLUP BIT(3) +- #define REG_ADP_BC_DRV_VBUS BIT(4) +- #define REG_ADP_BC_ADP_PRB_EN BIT(5) +- #define REG_ADP_BC_ADP_DISCHARGE BIT(6) +- #define REG_ADP_BC_ADP_CHARGE BIT(7) +- #define REG_ADP_BC_SESS_END BIT(8) +- #define REG_ADP_BC_DEVICE_SESS_VLD BIT(9) +- #define REG_ADP_BC_B_VALID BIT(10) +- #define REG_ADP_BC_A_VALID BIT(11) +- #define REG_ADP_BC_ID_DIG BIT(12) +- #define REG_ADP_BC_VBUS_VALID BIT(13) +- #define REG_ADP_BC_ADP_PROBE BIT(14) +- #define REG_ADP_BC_ADP_SENSE BIT(15) +- #define REG_ADP_BC_ACA_ENABLE BIT(16) +- #define REG_ADP_BC_DCD_ENABLE BIT(17) +- #define REG_ADP_BC_VDAT_DET_EN_B BIT(18) +- #define REG_ADP_BC_VDAT_SRC_EN_B BIT(19) +- #define REG_ADP_BC_CHARGE_SEL BIT(20) +- #define REG_ADP_BC_CHARGE_DETECT BIT(21) +- #define REG_ADP_BC_ACA_PIN_RANGE_C BIT(22) +- #define REG_ADP_BC_ACA_PIN_RANGE_B BIT(23) +- #define REG_ADP_BC_ACA_PIN_RANGE_A BIT(24) +- #define REG_ADP_BC_ACA_PIN_GND BIT(25) +- #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) +- +-#define REG_DBG_UART 0x10 +- +-#define REG_TEST 0x14 +- #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) +- #define REG_TEST_EN_MASK GENMASK(7, 4) +- #define REG_TEST_ADDR_MASK GENMASK(11, 8) +- #define REG_TEST_DATA_OUT_SEL BIT(12) +- #define REG_TEST_CLK BIT(13) +- #define REG_TEST_VA_TEST_EN_B_MASK GENMASK(15, 14) +- #define REG_TEST_DATA_OUT_MASK GENMASK(19, 16) +- #define REG_TEST_DISABLE_ID_PULLUP BIT(20) +- +-#define REG_TUNE 0x18 +- #define REG_TUNE_TX_RES_TUNE_MASK GENMASK(1, 0) +- #define REG_TUNE_TX_HSXV_TUNE_MASK GENMASK(3, 2) +- #define REG_TUNE_TX_VREF_TUNE_MASK GENMASK(7, 4) +- #define REG_TUNE_TX_RISE_TUNE_MASK GENMASK(9, 8) +- #define REG_TUNE_TX_PREEMP_PULSE_TUNE BIT(10) +- #define REG_TUNE_TX_PREEMP_AMP_TUNE_MASK GENMASK(12, 11) +- #define REG_TUNE_TX_FSLS_TUNE_MASK GENMASK(16, 13) +- #define REG_TUNE_SQRX_TUNE_MASK GENMASK(19, 17) +- #define REG_TUNE_OTG_TUNE GENMASK(22, 20) +- #define REG_TUNE_COMP_DIS_TUNE GENMASK(25, 23) +- #define REG_TUNE_HOST_DM_PULLDOWN BIT(26) +- #define REG_TUNE_HOST_DP_PULLDOWN BIT(27) +- +-#define RESET_COMPLETE_TIME 500 +-#define ACA_ENABLE_COMPLETE_TIME 50 +- +-struct phy_meson8b_usb2_priv { +- void __iomem *regs; +- enum usb_dr_mode dr_mode; +- struct clk *clk_usb_general; +- struct clk *clk_usb; +- struct reset_control *reset; +-}; +- +-static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, +- u32 reg) +-{ +- return readl(phy_priv->regs + reg); +-} +- +-static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, +- u32 reg, u32 mask, u32 value) +-{ +- u32 data; +- +- data = phy_meson8b_usb2_read(phy_priv, reg); +- data &= ~mask; +- data |= (value & mask); +- +- writel(data, phy_priv->regs + reg); +-} +- +-static int phy_meson8b_usb2_power_on(struct phy *phy) +-{ +- struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); +- int ret; +- +- if (!IS_ERR_OR_NULL(priv->reset)) { +- ret = reset_control_reset(priv->reset); +- if (ret) { +- dev_err(&phy->dev, "Failed to trigger USB reset\n"); +- return ret; +- } +- } +- +- ret = clk_prepare_enable(priv->clk_usb_general); +- if (ret) { +- dev_err(&phy->dev, "Failed to enable USB general clock\n"); +- return ret; +- } +- +- ret = clk_prepare_enable(priv->clk_usb); +- if (ret) { +- dev_err(&phy->dev, "Failed to enable USB DDR clock\n"); +- clk_disable_unprepare(priv->clk_usb_general); +- return ret; +- } +- +- phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, +- REG_CONFIG_CLK_32k_ALTSEL); +- +- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, +- 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); +- +- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, +- 0x5 << REG_CTRL_FSEL_SHIFT); +- +- /* reset the PHY */ +- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, +- REG_CTRL_POWER_ON_RESET); +- udelay(RESET_COMPLETE_TIME); +- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); +- udelay(RESET_COMPLETE_TIME); +- +- phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, +- REG_CTRL_SOF_TOGGLE_OUT); +- +- if (priv->dr_mode == USB_DR_MODE_HOST) { +- phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, +- REG_ADP_BC_ACA_ENABLE, +- REG_ADP_BC_ACA_ENABLE); +- +- udelay(ACA_ENABLE_COMPLETE_TIME); +- +- if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & +- REG_ADP_BC_ACA_PIN_FLOAT) { +- dev_warn(&phy->dev, "USB ID detect failed!\n"); +- clk_disable_unprepare(priv->clk_usb); +- clk_disable_unprepare(priv->clk_usb_general); +- return -EINVAL; +- } +- } +- +- return 0; +-} +- +-static int phy_meson8b_usb2_power_off(struct phy *phy) +-{ +- struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); +- +- clk_disable_unprepare(priv->clk_usb); +- clk_disable_unprepare(priv->clk_usb_general); +- +- return 0; +-} +- +-static const struct phy_ops phy_meson8b_usb2_ops = { +- .power_on = phy_meson8b_usb2_power_on, +- .power_off = phy_meson8b_usb2_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int phy_meson8b_usb2_probe(struct platform_device *pdev) +-{ +- struct phy_meson8b_usb2_priv *priv; +- struct resource *res; +- struct phy *phy; +- struct phy_provider *phy_provider; +- +- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->regs = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(priv->regs)) +- return PTR_ERR(priv->regs); +- +- priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); +- if (IS_ERR(priv->clk_usb_general)) +- return PTR_ERR(priv->clk_usb_general); +- +- priv->clk_usb = devm_clk_get(&pdev->dev, "usb"); +- if (IS_ERR(priv->clk_usb)) +- return PTR_ERR(priv->clk_usb); +- +- priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL); +- if (PTR_ERR(priv->reset) == -EPROBE_DEFER) +- return PTR_ERR(priv->reset); +- +- priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); +- if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { +- dev_err(&pdev->dev, +- "missing dual role configuration of the controller\n"); +- return -EINVAL; +- } +- +- phy = devm_phy_create(&pdev->dev, NULL, &phy_meson8b_usb2_ops); +- if (IS_ERR(phy)) { +- dev_err(&pdev->dev, "failed to create PHY\n"); +- return PTR_ERR(phy); +- } +- +- phy_set_drvdata(phy, priv); +- +- phy_provider = +- devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id phy_meson8b_usb2_of_match[] = { +- { .compatible = "amlogic,meson8b-usb2-phy", }, +- { .compatible = "amlogic,meson-gxbb-usb2-phy", }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); +- +-static struct platform_driver phy_meson8b_usb2_driver = { +- .probe = phy_meson8b_usb2_probe, +- .driver = { +- .name = "phy-meson-usb2", +- .of_match_table = phy_meson8b_usb2_of_match, +- }, +-}; +-module_platform_driver(phy_meson8b_usb2_driver); +- +-MODULE_AUTHOR("Martin Blumenstingl "); +-MODULE_DESCRIPTION("Meson8b and GXBB USB2 PHY driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c +deleted file mode 100644 +index 213e2e15339c..000000000000 +--- a/drivers/phy/phy-miphy28lp.c ++++ /dev/null +@@ -1,1286 +0,0 @@ +-/* +- * Copyright (C) 2014 STMicroelectronics +- * +- * STMicroelectronics PHY driver MiPHY28lp (for SoC STiH407). +- * +- * Author: Alexandre Torgue +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2, as +- * published by the Free Software Foundation. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-/* MiPHY registers */ +-#define MIPHY_CONF_RESET 0x00 +-#define RST_APPLI_SW BIT(0) +-#define RST_CONF_SW BIT(1) +-#define RST_MACRO_SW BIT(2) +- +-#define MIPHY_RESET 0x01 +-#define RST_PLL_SW BIT(0) +-#define RST_COMP_SW BIT(2) +- +-#define MIPHY_STATUS_1 0x02 +-#define PHY_RDY BIT(0) +-#define HFC_RDY BIT(1) +-#define HFC_PLL BIT(2) +- +-#define MIPHY_CONTROL 0x04 +-#define TERM_EN_SW BIT(2) +-#define DIS_LINK_RST BIT(3) +-#define AUTO_RST_RX BIT(4) +-#define PX_RX_POL BIT(5) +- +-#define MIPHY_BOUNDARY_SEL 0x0a +-#define TX_SEL BIT(6) +-#define SSC_SEL BIT(4) +-#define GENSEL_SEL BIT(0) +- +-#define MIPHY_BOUNDARY_1 0x0b +-#define MIPHY_BOUNDARY_2 0x0c +-#define SSC_EN_SW BIT(2) +- +-#define MIPHY_PLL_CLKREF_FREQ 0x0d +-#define MIPHY_SPEED 0x0e +-#define TX_SPDSEL_80DEC 0 +-#define TX_SPDSEL_40DEC 1 +-#define TX_SPDSEL_20DEC 2 +-#define RX_SPDSEL_80DEC 0 +-#define RX_SPDSEL_40DEC (1 << 2) +-#define RX_SPDSEL_20DEC (2 << 2) +- +-#define MIPHY_CONF 0x0f +-#define MIPHY_CTRL_TEST_SEL 0x20 +-#define MIPHY_CTRL_TEST_1 0x21 +-#define MIPHY_CTRL_TEST_2 0x22 +-#define MIPHY_CTRL_TEST_3 0x23 +-#define MIPHY_CTRL_TEST_4 0x24 +-#define MIPHY_FEEDBACK_TEST 0x25 +-#define MIPHY_DEBUG_BUS 0x26 +-#define MIPHY_DEBUG_STATUS_MSB 0x27 +-#define MIPHY_DEBUG_STATUS_LSB 0x28 +-#define MIPHY_PWR_RAIL_1 0x29 +-#define MIPHY_PWR_RAIL_2 0x2a +-#define MIPHY_SYNCHAR_CONTROL 0x30 +- +-#define MIPHY_COMP_FSM_1 0x3a +-#define COMP_START BIT(6) +- +-#define MIPHY_COMP_FSM_6 0x3f +-#define COMP_DONE BIT(7) +- +-#define MIPHY_COMP_POSTP 0x42 +-#define MIPHY_TX_CTRL_1 0x49 +-#define TX_REG_STEP_0V 0 +-#define TX_REG_STEP_P_25MV 1 +-#define TX_REG_STEP_P_50MV 2 +-#define TX_REG_STEP_N_25MV 7 +-#define TX_REG_STEP_N_50MV 6 +-#define TX_REG_STEP_N_75MV 5 +- +-#define MIPHY_TX_CTRL_2 0x4a +-#define TX_SLEW_SW_40_PS 0 +-#define TX_SLEW_SW_80_PS 1 +-#define TX_SLEW_SW_120_PS 2 +- +-#define MIPHY_TX_CTRL_3 0x4b +-#define MIPHY_TX_CAL_MAN 0x4e +-#define TX_SLEW_CAL_MAN_EN BIT(0) +- +-#define MIPHY_TST_BIAS_BOOST_2 0x62 +-#define MIPHY_BIAS_BOOST_1 0x63 +-#define MIPHY_BIAS_BOOST_2 0x64 +-#define MIPHY_RX_DESBUFF_FDB_2 0x67 +-#define MIPHY_RX_DESBUFF_FDB_3 0x68 +-#define MIPHY_SIGDET_COMPENS1 0x69 +-#define MIPHY_SIGDET_COMPENS2 0x6a +-#define MIPHY_JITTER_PERIOD 0x6b +-#define MIPHY_JITTER_AMPLITUDE_1 0x6c +-#define MIPHY_JITTER_AMPLITUDE_2 0x6d +-#define MIPHY_JITTER_AMPLITUDE_3 0x6e +-#define MIPHY_RX_K_GAIN 0x78 +-#define MIPHY_RX_BUFFER_CTRL 0x7a +-#define VGA_GAIN BIT(0) +-#define EQ_DC_GAIN BIT(2) +-#define EQ_BOOST_GAIN BIT(3) +- +-#define MIPHY_RX_VGA_GAIN 0x7b +-#define MIPHY_RX_EQU_GAIN_1 0x7f +-#define MIPHY_RX_EQU_GAIN_2 0x80 +-#define MIPHY_RX_EQU_GAIN_3 0x81 +-#define MIPHY_RX_CAL_CTRL_1 0x97 +-#define MIPHY_RX_CAL_CTRL_2 0x98 +- +-#define MIPHY_RX_CAL_OFFSET_CTRL 0x99 +-#define CAL_OFFSET_VGA_64 (0x03 << 0) +-#define CAL_OFFSET_THRESHOLD_64 (0x03 << 2) +-#define VGA_OFFSET_POLARITY BIT(4) +-#define OFFSET_COMPENSATION_EN BIT(6) +- +-#define MIPHY_RX_CAL_VGA_STEP 0x9a +-#define MIPHY_RX_CAL_EYE_MIN 0x9d +-#define MIPHY_RX_CAL_OPT_LENGTH 0x9f +-#define MIPHY_RX_LOCK_CTRL_1 0xc1 +-#define MIPHY_RX_LOCK_SETTINGS_OPT 0xc2 +-#define MIPHY_RX_LOCK_STEP 0xc4 +- +-#define MIPHY_RX_SIGDET_SLEEP_OA 0xc9 +-#define MIPHY_RX_SIGDET_SLEEP_SEL 0xca +-#define MIPHY_RX_SIGDET_WAIT_SEL 0xcb +-#define MIPHY_RX_SIGDET_DATA_SEL 0xcc +-#define EN_ULTRA_LOW_POWER BIT(0) +-#define EN_FIRST_HALF BIT(1) +-#define EN_SECOND_HALF BIT(2) +-#define EN_DIGIT_SIGNAL_CHECK BIT(3) +- +-#define MIPHY_RX_POWER_CTRL_1 0xcd +-#define MIPHY_RX_POWER_CTRL_2 0xce +-#define MIPHY_PLL_CALSET_CTRL 0xd3 +-#define MIPHY_PLL_CALSET_1 0xd4 +-#define MIPHY_PLL_CALSET_2 0xd5 +-#define MIPHY_PLL_CALSET_3 0xd6 +-#define MIPHY_PLL_CALSET_4 0xd7 +-#define MIPHY_PLL_SBR_1 0xe3 +-#define SET_NEW_CHANGE BIT(1) +- +-#define MIPHY_PLL_SBR_2 0xe4 +-#define MIPHY_PLL_SBR_3 0xe5 +-#define MIPHY_PLL_SBR_4 0xe6 +-#define MIPHY_PLL_COMMON_MISC_2 0xe9 +-#define START_ACT_FILT BIT(6) +- +-#define MIPHY_PLL_SPAREIN 0xeb +- +-/* +- * On STiH407 the glue logic can be different among MiPHY devices; for example: +- * MiPHY0: OSC_FORCE_EXT means: +- * 0: 30MHz crystal clk - 1: 100MHz ext clk routed through MiPHY1 +- * MiPHY1: OSC_FORCE_EXT means: +- * 1: 30MHz crystal clk - 0: 100MHz ext clk routed through MiPHY1 +- * Some devices have not the possibility to check if the osc is ready. +- */ +-#define MIPHY_OSC_FORCE_EXT BIT(3) +-#define MIPHY_OSC_RDY BIT(5) +- +-#define MIPHY_CTRL_MASK 0x0f +-#define MIPHY_CTRL_DEFAULT 0 +-#define MIPHY_CTRL_SYNC_D_EN BIT(2) +- +-/* SATA / PCIe defines */ +-#define SATA_CTRL_MASK 0x07 +-#define PCIE_CTRL_MASK 0xff +-#define SATA_CTRL_SELECT_SATA 1 +-#define SATA_CTRL_SELECT_PCIE 0 +-#define SYSCFG_PCIE_PCIE_VAL 0x80 +-#define SATA_SPDMODE 1 +- +-#define MIPHY_SATA_BANK_NB 3 +-#define MIPHY_PCIE_BANK_NB 2 +- +-enum { +- SYSCFG_CTRL, +- SYSCFG_STATUS, +- SYSCFG_PCI, +- SYSCFG_SATA, +- SYSCFG_REG_MAX, +-}; +- +-struct miphy28lp_phy { +- struct phy *phy; +- struct miphy28lp_dev *phydev; +- void __iomem *base; +- void __iomem *pipebase; +- +- bool osc_force_ext; +- bool osc_rdy; +- bool px_rx_pol_inv; +- bool ssc; +- bool tx_impedance; +- +- struct reset_control *miphy_rst; +- +- u32 sata_gen; +- +- /* Sysconfig registers offsets needed to configure the device */ +- u32 syscfg_reg[SYSCFG_REG_MAX]; +- u8 type; +-}; +- +-struct miphy28lp_dev { +- struct device *dev; +- struct regmap *regmap; +- struct mutex miphy_mutex; +- struct miphy28lp_phy **phys; +- int nphys; +-}; +- +-struct miphy_initval { +- u16 reg; +- u16 val; +-}; +- +-enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 }; +- +-static char *PHY_TYPE_name[] = { "sata-up", "pcie-up", "", "usb3-up" }; +- +-struct pll_ratio { +- int clk_ref; +- int calset_1; +- int calset_2; +- int calset_3; +- int calset_4; +- int cal_ctrl; +-}; +- +-static struct pll_ratio sata_pll_ratio = { +- .clk_ref = 0x1e, +- .calset_1 = 0xc8, +- .calset_2 = 0x00, +- .calset_3 = 0x00, +- .calset_4 = 0x00, +- .cal_ctrl = 0x00, +-}; +- +-static struct pll_ratio pcie_pll_ratio = { +- .clk_ref = 0x1e, +- .calset_1 = 0xa6, +- .calset_2 = 0xaa, +- .calset_3 = 0xaa, +- .calset_4 = 0x00, +- .cal_ctrl = 0x00, +-}; +- +-static struct pll_ratio usb3_pll_ratio = { +- .clk_ref = 0x1e, +- .calset_1 = 0xa6, +- .calset_2 = 0xaa, +- .calset_3 = 0xaa, +- .calset_4 = 0x04, +- .cal_ctrl = 0x00, +-}; +- +-struct miphy28lp_pll_gen { +- int bank; +- int speed; +- int bias_boost_1; +- int bias_boost_2; +- int tx_ctrl_1; +- int tx_ctrl_2; +- int tx_ctrl_3; +- int rx_k_gain; +- int rx_vga_gain; +- int rx_equ_gain_1; +- int rx_equ_gain_2; +- int rx_equ_gain_3; +- int rx_buff_ctrl; +-}; +- +-static struct miphy28lp_pll_gen sata_pll_gen[] = { +- { +- .bank = 0x00, +- .speed = TX_SPDSEL_80DEC | RX_SPDSEL_80DEC, +- .bias_boost_1 = 0x00, +- .bias_boost_2 = 0xae, +- .tx_ctrl_2 = 0x53, +- .tx_ctrl_3 = 0x00, +- .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, +- .rx_vga_gain = 0x00, +- .rx_equ_gain_1 = 0x7d, +- .rx_equ_gain_2 = 0x56, +- .rx_equ_gain_3 = 0x00, +- }, +- { +- .bank = 0x01, +- .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, +- .bias_boost_1 = 0x00, +- .bias_boost_2 = 0xae, +- .tx_ctrl_2 = 0x72, +- .tx_ctrl_3 = 0x20, +- .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, +- .rx_vga_gain = 0x00, +- .rx_equ_gain_1 = 0x7d, +- .rx_equ_gain_2 = 0x56, +- .rx_equ_gain_3 = 0x00, +- }, +- { +- .bank = 0x02, +- .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, +- .bias_boost_1 = 0x00, +- .bias_boost_2 = 0xae, +- .tx_ctrl_2 = 0xc0, +- .tx_ctrl_3 = 0x20, +- .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, +- .rx_vga_gain = 0x00, +- .rx_equ_gain_1 = 0x7d, +- .rx_equ_gain_2 = 0x56, +- .rx_equ_gain_3 = 0x00, +- }, +-}; +- +-static struct miphy28lp_pll_gen pcie_pll_gen[] = { +- { +- .bank = 0x00, +- .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, +- .bias_boost_1 = 0x00, +- .bias_boost_2 = 0xa5, +- .tx_ctrl_1 = TX_REG_STEP_N_25MV, +- .tx_ctrl_2 = 0x71, +- .tx_ctrl_3 = 0x60, +- .rx_k_gain = 0x98, +- .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, +- .rx_vga_gain = 0x00, +- .rx_equ_gain_1 = 0x79, +- .rx_equ_gain_2 = 0x56, +- }, +- { +- .bank = 0x01, +- .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, +- .bias_boost_1 = 0x00, +- .bias_boost_2 = 0xa5, +- .tx_ctrl_1 = TX_REG_STEP_N_25MV, +- .tx_ctrl_2 = 0x70, +- .tx_ctrl_3 = 0x60, +- .rx_k_gain = 0xcc, +- .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, +- .rx_vga_gain = 0x00, +- .rx_equ_gain_1 = 0x78, +- .rx_equ_gain_2 = 0x07, +- }, +-}; +- +-static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- u8 val; +- +- /* Putting Macro in reset */ +- writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); +- +- val = RST_APPLI_SW | RST_CONF_SW; +- writeb_relaxed(val, base + MIPHY_CONF_RESET); +- +- writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); +- +- /* Bringing the MIPHY-CPU registers out of reset */ +- if (miphy_phy->type == PHY_TYPE_PCIE) { +- val = AUTO_RST_RX | TERM_EN_SW; +- writeb_relaxed(val, base + MIPHY_CONTROL); +- } else { +- val = AUTO_RST_RX | TERM_EN_SW | DIS_LINK_RST; +- writeb_relaxed(val, base + MIPHY_CONTROL); +- } +-} +- +-static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy, +- struct pll_ratio *pll_ratio) +-{ +- void __iomem *base = miphy_phy->base; +- u8 val; +- +- /* Applying PLL Settings */ +- writeb_relaxed(0x1d, base + MIPHY_PLL_SPAREIN); +- writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); +- +- /* PLL Ratio */ +- writeb_relaxed(pll_ratio->calset_1, base + MIPHY_PLL_CALSET_1); +- writeb_relaxed(pll_ratio->calset_2, base + MIPHY_PLL_CALSET_2); +- writeb_relaxed(pll_ratio->calset_3, base + MIPHY_PLL_CALSET_3); +- writeb_relaxed(pll_ratio->calset_4, base + MIPHY_PLL_CALSET_4); +- writeb_relaxed(pll_ratio->cal_ctrl, base + MIPHY_PLL_CALSET_CTRL); +- +- writeb_relaxed(TX_SEL, base + MIPHY_BOUNDARY_SEL); +- +- val = (0x68 << 1) | TX_SLEW_CAL_MAN_EN; +- writeb_relaxed(val, base + MIPHY_TX_CAL_MAN); +- +- val = VGA_OFFSET_POLARITY | CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; +- +- if (miphy_phy->type != PHY_TYPE_SATA) +- val |= OFFSET_COMPENSATION_EN; +- +- writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); +- +- if (miphy_phy->type == PHY_TYPE_USB3) { +- writeb_relaxed(0x00, base + MIPHY_CONF); +- writeb_relaxed(0x70, base + MIPHY_RX_LOCK_STEP); +- writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_OA); +- writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_SEL); +- writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_WAIT_SEL); +- +- val = EN_DIGIT_SIGNAL_CHECK | EN_FIRST_HALF; +- writeb_relaxed(val, base + MIPHY_RX_SIGDET_DATA_SEL); +- } +- +-} +- +-static inline void miphy28lp_sata_config_gen(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(sata_pll_gen); i++) { +- struct miphy28lp_pll_gen *gen = &sata_pll_gen[i]; +- +- /* Banked settings */ +- writeb_relaxed(gen->bank, base + MIPHY_CONF); +- writeb_relaxed(gen->speed, base + MIPHY_SPEED); +- writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); +- writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); +- +- /* TX buffer Settings */ +- writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); +- writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); +- +- /* RX Buffer Settings */ +- writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); +- writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); +- writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); +- writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); +- writeb_relaxed(gen->rx_equ_gain_3, base + MIPHY_RX_EQU_GAIN_3); +- } +-} +- +-static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(pcie_pll_gen); i++) { +- struct miphy28lp_pll_gen *gen = &pcie_pll_gen[i]; +- +- /* Banked settings */ +- writeb_relaxed(gen->bank, base + MIPHY_CONF); +- writeb_relaxed(gen->speed, base + MIPHY_SPEED); +- writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); +- writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); +- +- /* TX buffer Settings */ +- writeb_relaxed(gen->tx_ctrl_1, base + MIPHY_TX_CTRL_1); +- writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); +- writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); +- +- writeb_relaxed(gen->rx_k_gain, base + MIPHY_RX_K_GAIN); +- +- /* RX Buffer Settings */ +- writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); +- writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); +- writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); +- writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); +- } +-} +- +-static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy) +-{ +- unsigned long finish = jiffies + 5 * HZ; +- u8 val; +- +- /* Waiting for Compensation to complete */ +- do { +- val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6); +- +- if (time_after_eq(jiffies, finish)) +- return -EBUSY; +- cpu_relax(); +- } while (!(val & COMP_DONE)); +- +- return 0; +-} +- +- +-static inline int miphy28lp_compensation(struct miphy28lp_phy *miphy_phy, +- struct pll_ratio *pll_ratio) +-{ +- void __iomem *base = miphy_phy->base; +- +- /* Poll for HFC ready after reset release */ +- /* Compensation measurement */ +- writeb_relaxed(RST_PLL_SW | RST_COMP_SW, base + MIPHY_RESET); +- +- writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); +- writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); +- writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); +- +- if (miphy_phy->type == PHY_TYPE_PCIE) +- writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); +- +- writeb_relaxed(0x00, base + MIPHY_RESET); +- writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); +- writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); +- +- /* TX compensation offset to re-center TX impedance */ +- writeb_relaxed(0x00, base + MIPHY_COMP_POSTP); +- +- if (miphy_phy->type == PHY_TYPE_PCIE) +- return miphy28lp_wait_compensation(miphy_phy); +- +- return 0; +-} +- +-static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- u8 val; +- +- /* MIPHY Reset */ +- writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); +- writeb_relaxed(0x00, base + MIPHY_CONF_RESET); +- writeb_relaxed(RST_COMP_SW, base + MIPHY_RESET); +- +- val = RST_COMP_SW | RST_PLL_SW; +- writeb_relaxed(val, base + MIPHY_RESET); +- +- writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); +- writeb_relaxed(0x1e, base + MIPHY_PLL_CLKREF_FREQ); +- writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); +- writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); +- writeb_relaxed(0x00, base + MIPHY_RESET); +- writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); +- writeb_relaxed(0x00, base + MIPHY_CONF); +- writeb_relaxed(0x00, base + MIPHY_BOUNDARY_1); +- writeb_relaxed(0x00, base + MIPHY_TST_BIAS_BOOST_2); +- writeb_relaxed(0x00, base + MIPHY_CONF); +- writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); +- writeb_relaxed(0xa5, base + MIPHY_DEBUG_BUS); +- writeb_relaxed(0x00, base + MIPHY_CONF); +-} +- +-static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- u8 val; +- +- /* Compensate Tx impedance to avoid out of range values */ +- /* +- * Enable the SSC on PLL for all banks +- * SSC Modulation @ 31 KHz and 4000 ppm modulation amp +- */ +- val = readb_relaxed(base + MIPHY_BOUNDARY_2); +- val |= SSC_EN_SW; +- writeb_relaxed(val, base + MIPHY_BOUNDARY_2); +- +- val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); +- val |= SSC_SEL; +- writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); +- +- for (val = 0; val < MIPHY_SATA_BANK_NB; val++) { +- writeb_relaxed(val, base + MIPHY_CONF); +- +- /* Add value to each reference clock cycle */ +- /* and define the period length of the SSC */ +- writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); +- writeb_relaxed(0x6c, base + MIPHY_PLL_SBR_3); +- writeb_relaxed(0x81, base + MIPHY_PLL_SBR_4); +- +- /* Clear any previous request */ +- writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); +- +- /* requests the PLL to take in account new parameters */ +- writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); +- +- /* To be sure there is no other pending requests */ +- writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); +- } +-} +- +-static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- u8 val; +- +- /* Compensate Tx impedance to avoid out of range values */ +- /* +- * Enable the SSC on PLL for all banks +- * SSC Modulation @ 31 KHz and 4000 ppm modulation amp +- */ +- val = readb_relaxed(base + MIPHY_BOUNDARY_2); +- val |= SSC_EN_SW; +- writeb_relaxed(val, base + MIPHY_BOUNDARY_2); +- +- val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); +- val |= SSC_SEL; +- writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); +- +- for (val = 0; val < MIPHY_PCIE_BANK_NB; val++) { +- writeb_relaxed(val, base + MIPHY_CONF); +- +- /* Validate Step component */ +- writeb_relaxed(0x69, base + MIPHY_PLL_SBR_3); +- writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); +- +- /* Validate Period component */ +- writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); +- writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); +- +- /* Clear any previous request */ +- writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); +- +- /* requests the PLL to take in account new parameters */ +- writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); +- +- /* To be sure there is no other pending requests */ +- writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); +- } +-} +- +-static inline void miphy_tune_tx_impedance(struct miphy28lp_phy *miphy_phy) +-{ +- /* Compensate Tx impedance to avoid out of range values */ +- writeb_relaxed(0x02, miphy_phy->base + MIPHY_COMP_POSTP); +-} +- +-static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- int err; +- u8 val; +- +- /* Putting Macro in reset */ +- miphy28lp_set_reset(miphy_phy); +- +- /* PLL calibration */ +- miphy28lp_pll_calibration(miphy_phy, &sata_pll_ratio); +- +- /* Banked settings Gen1/Gen2/Gen3 */ +- miphy28lp_sata_config_gen(miphy_phy); +- +- /* Power control */ +- /* Input bridge enable, manual input bridge control */ +- writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); +- +- /* Macro out of reset */ +- writeb_relaxed(0x00, base + MIPHY_CONF_RESET); +- +- /* Poll for HFC ready after reset release */ +- /* Compensation measurement */ +- err = miphy28lp_compensation(miphy_phy, &sata_pll_ratio); +- if (err) +- return err; +- +- if (miphy_phy->px_rx_pol_inv) { +- /* Invert Rx polarity */ +- val = readb_relaxed(miphy_phy->base + MIPHY_CONTROL); +- val |= PX_RX_POL; +- writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL); +- } +- +- if (miphy_phy->ssc) +- miphy_sata_tune_ssc(miphy_phy); +- +- if (miphy_phy->tx_impedance) +- miphy_tune_tx_impedance(miphy_phy); +- +- return 0; +-} +- +-static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- int err; +- +- /* Putting Macro in reset */ +- miphy28lp_set_reset(miphy_phy); +- +- /* PLL calibration */ +- miphy28lp_pll_calibration(miphy_phy, &pcie_pll_ratio); +- +- /* Banked settings Gen1/Gen2 */ +- miphy28lp_pcie_config_gen(miphy_phy); +- +- /* Power control */ +- /* Input bridge enable, manual input bridge control */ +- writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); +- +- /* Macro out of reset */ +- writeb_relaxed(0x00, base + MIPHY_CONF_RESET); +- +- /* Poll for HFC ready after reset release */ +- /* Compensation measurement */ +- err = miphy28lp_compensation(miphy_phy, &pcie_pll_ratio); +- if (err) +- return err; +- +- if (miphy_phy->ssc) +- miphy_pcie_tune_ssc(miphy_phy); +- +- if (miphy_phy->tx_impedance) +- miphy_tune_tx_impedance(miphy_phy); +- +- return 0; +-} +- +- +-static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy) +-{ +- void __iomem *base = miphy_phy->base; +- u8 val; +- +- /* Putting Macro in reset */ +- miphy28lp_set_reset(miphy_phy); +- +- /* PLL calibration */ +- miphy28lp_pll_calibration(miphy_phy, &usb3_pll_ratio); +- +- /* Writing The Speed Rate */ +- writeb_relaxed(0x00, base + MIPHY_CONF); +- +- val = RX_SPDSEL_20DEC | TX_SPDSEL_20DEC; +- writeb_relaxed(val, base + MIPHY_SPEED); +- +- /* RX Channel compensation and calibration */ +- writeb_relaxed(0x1c, base + MIPHY_RX_LOCK_SETTINGS_OPT); +- writeb_relaxed(0x51, base + MIPHY_RX_CAL_CTRL_1); +- writeb_relaxed(0x70, base + MIPHY_RX_CAL_CTRL_2); +- +- val = OFFSET_COMPENSATION_EN | VGA_OFFSET_POLARITY | +- CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; +- writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); +- writeb_relaxed(0x22, base + MIPHY_RX_CAL_VGA_STEP); +- writeb_relaxed(0x0e, base + MIPHY_RX_CAL_OPT_LENGTH); +- +- val = EQ_DC_GAIN | VGA_GAIN; +- writeb_relaxed(val, base + MIPHY_RX_BUFFER_CTRL); +- writeb_relaxed(0x78, base + MIPHY_RX_EQU_GAIN_1); +- writeb_relaxed(0x1b, base + MIPHY_SYNCHAR_CONTROL); +- +- /* TX compensation offset to re-center TX impedance */ +- writeb_relaxed(0x02, base + MIPHY_COMP_POSTP); +- +- /* Enable GENSEL_SEL and SSC */ +- /* TX_SEL=0 swing preemp forced by pipe registres */ +- val = SSC_SEL | GENSEL_SEL; +- writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); +- +- /* MIPHY Bias boost */ +- writeb_relaxed(0x00, base + MIPHY_BIAS_BOOST_1); +- writeb_relaxed(0xa7, base + MIPHY_BIAS_BOOST_2); +- +- /* SSC modulation */ +- writeb_relaxed(SSC_EN_SW, base + MIPHY_BOUNDARY_2); +- +- /* MIPHY TX control */ +- writeb_relaxed(0x00, base + MIPHY_CONF); +- +- /* Validate Step component */ +- writeb_relaxed(0x5a, base + MIPHY_PLL_SBR_3); +- writeb_relaxed(0xa0, base + MIPHY_PLL_SBR_4); +- +- /* Validate Period component */ +- writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); +- writeb_relaxed(0xa1, base + MIPHY_PLL_SBR_4); +- +- /* Clear any previous request */ +- writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); +- +- /* requests the PLL to take in account new parameters */ +- writeb_relaxed(0x02, base + MIPHY_PLL_SBR_1); +- +- /* To be sure there is no other pending requests */ +- writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); +- +- /* Rx PI controller settings */ +- writeb_relaxed(0xca, base + MIPHY_RX_K_GAIN); +- +- /* MIPHY RX input bridge control */ +- /* INPUT_BRIDGE_EN_SW=1, manual input bridge control[0]=1 */ +- writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); +- writeb_relaxed(0x29, base + MIPHY_RX_POWER_CTRL_1); +- writeb_relaxed(0x1a, base + MIPHY_RX_POWER_CTRL_2); +- +- /* MIPHY Reset for usb3 */ +- miphy28_usb3_miphy_reset(miphy_phy); +-} +- +-static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy) +-{ +- unsigned long finish = jiffies + 5 * HZ; +- u8 mask = HFC_PLL | HFC_RDY; +- u8 val; +- +- /* +- * For PCIe and USB3 check only that PLL and HFC are ready +- * For SATA check also that phy is ready! +- */ +- if (miphy_phy->type == PHY_TYPE_SATA) +- mask |= PHY_RDY; +- +- do { +- val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1); +- if ((val & mask) != mask) +- cpu_relax(); +- else +- return 0; +- } while (!time_after_eq(jiffies, finish)); +- +- return -EBUSY; +-} +- +-static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) +-{ +- struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; +- unsigned long finish = jiffies + 5 * HZ; +- u32 val; +- +- if (!miphy_phy->osc_rdy) +- return 0; +- +- if (!miphy_phy->syscfg_reg[SYSCFG_STATUS]) +- return -EINVAL; +- +- do { +- regmap_read(miphy_dev->regmap, +- miphy_phy->syscfg_reg[SYSCFG_STATUS], &val); +- +- if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) +- cpu_relax(); +- else +- return 0; +- } while (!time_after_eq(jiffies, finish)); +- +- return -EBUSY; +-} +- +-static int miphy28lp_get_resource_byname(struct device_node *child, +- char *rname, struct resource *res) +-{ +- int index; +- +- index = of_property_match_string(child, "reg-names", rname); +- if (index < 0) +- return -ENODEV; +- +- return of_address_to_resource(child, index, res); +-} +- +-static int miphy28lp_get_one_addr(struct device *dev, +- struct device_node *child, char *rname, +- void __iomem **base) +-{ +- struct resource res; +- int ret; +- +- ret = miphy28lp_get_resource_byname(child, rname, &res); +- if (!ret) { +- *base = devm_ioremap(dev, res.start, resource_size(&res)); +- if (!*base) { +- dev_err(dev, "failed to ioremap %s address region\n" +- , rname); +- return -ENOENT; +- } +- } +- +- return 0; +-} +- +-/* MiPHY reset and sysconf setup */ +-static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) +-{ +- int err; +- struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; +- +- if (!miphy_phy->syscfg_reg[SYSCFG_CTRL]) +- return -EINVAL; +- +- err = reset_control_assert(miphy_phy->miphy_rst); +- if (err) { +- dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); +- return err; +- } +- +- if (miphy_phy->osc_force_ext) +- miphy_val |= MIPHY_OSC_FORCE_EXT; +- +- regmap_update_bits(miphy_dev->regmap, +- miphy_phy->syscfg_reg[SYSCFG_CTRL], +- MIPHY_CTRL_MASK, miphy_val); +- +- err = reset_control_deassert(miphy_phy->miphy_rst); +- if (err) { +- dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); +- return err; +- } +- +- return miphy_osc_is_ready(miphy_phy); +-} +- +-static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) +-{ +- struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; +- int err, sata_conf = SATA_CTRL_SELECT_SATA; +- +- if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || +- (!miphy_phy->syscfg_reg[SYSCFG_PCI]) || +- (!miphy_phy->base)) +- return -EINVAL; +- +- dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base); +- +- /* Configure the glue-logic */ +- sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE); +- +- regmap_update_bits(miphy_dev->regmap, +- miphy_phy->syscfg_reg[SYSCFG_SATA], +- SATA_CTRL_MASK, sata_conf); +- +- regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], +- PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE); +- +- /* MiPHY path and clocking init */ +- err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); +- +- if (err) { +- dev_err(miphy_dev->dev, "SATA phy setup failed\n"); +- return err; +- } +- +- /* initialize miphy */ +- miphy28lp_configure_sata(miphy_phy); +- +- return miphy_is_ready(miphy_phy); +-} +- +-static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy) +-{ +- struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; +- int err; +- +- if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || +- (!miphy_phy->syscfg_reg[SYSCFG_PCI]) +- || (!miphy_phy->base) || (!miphy_phy->pipebase)) +- return -EINVAL; +- +- dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base); +- +- /* Configure the glue-logic */ +- regmap_update_bits(miphy_dev->regmap, +- miphy_phy->syscfg_reg[SYSCFG_SATA], +- SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE); +- +- regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], +- PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL); +- +- /* MiPHY path and clocking init */ +- err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); +- +- if (err) { +- dev_err(miphy_dev->dev, "PCIe phy setup failed\n"); +- return err; +- } +- +- /* initialize miphy */ +- err = miphy28lp_configure_pcie(miphy_phy); +- if (err) +- return err; +- +- /* PIPE Wrapper Configuration */ +- writeb_relaxed(0x68, miphy_phy->pipebase + 0x104); /* Rise_0 */ +- writeb_relaxed(0x61, miphy_phy->pipebase + 0x105); /* Rise_1 */ +- writeb_relaxed(0x68, miphy_phy->pipebase + 0x108); /* Fall_0 */ +- writeb_relaxed(0x61, miphy_phy->pipebase + 0x109); /* Fall-1 */ +- writeb_relaxed(0x68, miphy_phy->pipebase + 0x10c); /* Threshold_0 */ +- writeb_relaxed(0x60, miphy_phy->pipebase + 0x10d); /* Threshold_1 */ +- +- /* Wait for phy_ready */ +- return miphy_is_ready(miphy_phy); +-} +- +-static int miphy28lp_init_usb3(struct miphy28lp_phy *miphy_phy) +-{ +- struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; +- int err; +- +- if ((!miphy_phy->base) || (!miphy_phy->pipebase)) +- return -EINVAL; +- +- dev_info(miphy_dev->dev, "usb3-up mode, addr 0x%p\n", miphy_phy->base); +- +- /* MiPHY path and clocking init */ +- err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_SYNC_D_EN); +- if (err) { +- dev_err(miphy_dev->dev, "USB3 phy setup failed\n"); +- return err; +- } +- +- /* initialize miphy */ +- miphy28lp_configure_usb3(miphy_phy); +- +- /* PIPE Wrapper Configuration */ +- writeb_relaxed(0x68, miphy_phy->pipebase + 0x23); +- writeb_relaxed(0x61, miphy_phy->pipebase + 0x24); +- writeb_relaxed(0x68, miphy_phy->pipebase + 0x26); +- writeb_relaxed(0x61, miphy_phy->pipebase + 0x27); +- writeb_relaxed(0x18, miphy_phy->pipebase + 0x29); +- writeb_relaxed(0x61, miphy_phy->pipebase + 0x2a); +- +- /* pipe Wrapper usb3 TX swing de-emph margin PREEMPH[7:4], SWING[3:0] */ +- writeb_relaxed(0X67, miphy_phy->pipebase + 0x68); +- writeb_relaxed(0x0d, miphy_phy->pipebase + 0x69); +- writeb_relaxed(0X67, miphy_phy->pipebase + 0x6a); +- writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6b); +- writeb_relaxed(0X67, miphy_phy->pipebase + 0x6c); +- writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6d); +- writeb_relaxed(0X67, miphy_phy->pipebase + 0x6e); +- writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6f); +- +- return miphy_is_ready(miphy_phy); +-} +- +-static int miphy28lp_init(struct phy *phy) +-{ +- struct miphy28lp_phy *miphy_phy = phy_get_drvdata(phy); +- struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; +- int ret; +- +- mutex_lock(&miphy_dev->miphy_mutex); +- +- switch (miphy_phy->type) { +- +- case PHY_TYPE_SATA: +- ret = miphy28lp_init_sata(miphy_phy); +- break; +- case PHY_TYPE_PCIE: +- ret = miphy28lp_init_pcie(miphy_phy); +- break; +- case PHY_TYPE_USB3: +- ret = miphy28lp_init_usb3(miphy_phy); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- +- mutex_unlock(&miphy_dev->miphy_mutex); +- +- return ret; +-} +- +-static int miphy28lp_get_addr(struct miphy28lp_phy *miphy_phy) +-{ +- struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; +- struct device_node *phynode = miphy_phy->phy->dev.of_node; +- int err; +- +- if ((miphy_phy->type != PHY_TYPE_SATA) && +- (miphy_phy->type != PHY_TYPE_PCIE) && +- (miphy_phy->type != PHY_TYPE_USB3)) { +- return -EINVAL; +- } +- +- err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, +- PHY_TYPE_name[miphy_phy->type - PHY_TYPE_SATA], +- &miphy_phy->base); +- if (err) +- return err; +- +- if ((miphy_phy->type == PHY_TYPE_PCIE) || +- (miphy_phy->type == PHY_TYPE_USB3)) { +- err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, "pipew", +- &miphy_phy->pipebase); +- if (err) +- return err; +- } +- +- return 0; +-} +- +-static struct phy *miphy28lp_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct miphy28lp_dev *miphy_dev = dev_get_drvdata(dev); +- struct miphy28lp_phy *miphy_phy = NULL; +- struct device_node *phynode = args->np; +- int ret, index = 0; +- +- if (args->args_count != 1) { +- dev_err(dev, "Invalid number of cells in 'phy' property\n"); +- return ERR_PTR(-EINVAL); +- } +- +- for (index = 0; index < miphy_dev->nphys; index++) +- if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { +- miphy_phy = miphy_dev->phys[index]; +- break; +- } +- +- if (!miphy_phy) { +- dev_err(dev, "Failed to find appropriate phy\n"); +- return ERR_PTR(-EINVAL); +- } +- +- miphy_phy->type = args->args[0]; +- +- ret = miphy28lp_get_addr(miphy_phy); +- if (ret < 0) +- return ERR_PTR(ret); +- +- return miphy_phy->phy; +-} +- +-static const struct phy_ops miphy28lp_ops = { +- .init = miphy28lp_init, +- .owner = THIS_MODULE, +-}; +- +-static int miphy28lp_probe_resets(struct device_node *node, +- struct miphy28lp_phy *miphy_phy) +-{ +- struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; +- int err; +- +- miphy_phy->miphy_rst = +- of_reset_control_get_shared(node, "miphy-sw-rst"); +- +- if (IS_ERR(miphy_phy->miphy_rst)) { +- dev_err(miphy_dev->dev, +- "miphy soft reset control not defined\n"); +- return PTR_ERR(miphy_phy->miphy_rst); +- } +- +- err = reset_control_deassert(miphy_phy->miphy_rst); +- if (err) { +- dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); +- return err; +- } +- +- return 0; +-} +- +-static int miphy28lp_of_probe(struct device_node *np, +- struct miphy28lp_phy *miphy_phy) +-{ +- int i; +- u32 ctrlreg; +- +- miphy_phy->osc_force_ext = +- of_property_read_bool(np, "st,osc-force-ext"); +- +- miphy_phy->osc_rdy = of_property_read_bool(np, "st,osc-rdy"); +- +- miphy_phy->px_rx_pol_inv = +- of_property_read_bool(np, "st,px_rx_pol_inv"); +- +- miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on"); +- +- miphy_phy->tx_impedance = +- of_property_read_bool(np, "st,tx-impedance-comp"); +- +- of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen); +- if (!miphy_phy->sata_gen) +- miphy_phy->sata_gen = SATA_GEN1; +- +- for (i = 0; i < SYSCFG_REG_MAX; i++) { +- if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg)) +- miphy_phy->syscfg_reg[i] = ctrlreg; +- } +- +- return 0; +-} +- +-static int miphy28lp_probe(struct platform_device *pdev) +-{ +- struct device_node *child, *np = pdev->dev.of_node; +- struct miphy28lp_dev *miphy_dev; +- struct phy_provider *provider; +- struct phy *phy; +- int ret, port = 0; +- +- miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); +- if (!miphy_dev) +- return -ENOMEM; +- +- miphy_dev->nphys = of_get_child_count(np); +- miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys, +- sizeof(*miphy_dev->phys), GFP_KERNEL); +- if (!miphy_dev->phys) +- return -ENOMEM; +- +- miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); +- if (IS_ERR(miphy_dev->regmap)) { +- dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); +- return PTR_ERR(miphy_dev->regmap); +- } +- +- miphy_dev->dev = &pdev->dev; +- +- dev_set_drvdata(&pdev->dev, miphy_dev); +- +- mutex_init(&miphy_dev->miphy_mutex); +- +- for_each_child_of_node(np, child) { +- struct miphy28lp_phy *miphy_phy; +- +- miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), +- GFP_KERNEL); +- if (!miphy_phy) { +- ret = -ENOMEM; +- goto put_child; +- } +- +- miphy_dev->phys[port] = miphy_phy; +- +- phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops); +- if (IS_ERR(phy)) { +- dev_err(&pdev->dev, "failed to create PHY\n"); +- ret = PTR_ERR(phy); +- goto put_child; +- } +- +- miphy_dev->phys[port]->phy = phy; +- miphy_dev->phys[port]->phydev = miphy_dev; +- +- ret = miphy28lp_of_probe(child, miphy_phy); +- if (ret) +- goto put_child; +- +- ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]); +- if (ret) +- goto put_child; +- +- phy_set_drvdata(phy, miphy_dev->phys[port]); +- port++; +- +- } +- +- provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate); +- return PTR_ERR_OR_ZERO(provider); +-put_child: +- of_node_put(child); +- return ret; +-} +- +-static const struct of_device_id miphy28lp_of_match[] = { +- {.compatible = "st,miphy28lp-phy", }, +- {}, +-}; +- +-MODULE_DEVICE_TABLE(of, miphy28lp_of_match); +- +-static struct platform_driver miphy28lp_driver = { +- .probe = miphy28lp_probe, +- .driver = { +- .name = "miphy28lp-phy", +- .of_match_table = miphy28lp_of_match, +- } +-}; +- +-module_platform_driver(miphy28lp_driver); +- +-MODULE_AUTHOR("Alexandre Torgue "); +-MODULE_DESCRIPTION("STMicroelectronics miphy28lp driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c +deleted file mode 100644 +index 768ce92e81ce..000000000000 +--- a/drivers/phy/phy-mvebu-sata.c ++++ /dev/null +@@ -1,138 +0,0 @@ +-/* +- * phy-mvebu-sata.c: SATA Phy driver for the Marvell mvebu SoCs. +- * +- * Copyright (C) 2013 Andrew Lunn +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version +- * 2 of the License, or (at your option) any later version. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-struct priv { +- struct clk *clk; +- void __iomem *base; +-}; +- +-#define SATA_PHY_MODE_2 0x0330 +-#define MODE_2_FORCE_PU_TX BIT(0) +-#define MODE_2_FORCE_PU_RX BIT(1) +-#define MODE_2_PU_PLL BIT(2) +-#define MODE_2_PU_IVREF BIT(3) +-#define SATA_IF_CTRL 0x0050 +-#define CTRL_PHY_SHUTDOWN BIT(9) +- +-static int phy_mvebu_sata_power_on(struct phy *phy) +-{ +- struct priv *priv = phy_get_drvdata(phy); +- u32 reg; +- +- clk_prepare_enable(priv->clk); +- +- /* Enable PLL and IVREF */ +- reg = readl(priv->base + SATA_PHY_MODE_2); +- reg |= (MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | +- MODE_2_PU_PLL | MODE_2_PU_IVREF); +- writel(reg , priv->base + SATA_PHY_MODE_2); +- +- /* Enable PHY */ +- reg = readl(priv->base + SATA_IF_CTRL); +- reg &= ~CTRL_PHY_SHUTDOWN; +- writel(reg, priv->base + SATA_IF_CTRL); +- +- clk_disable_unprepare(priv->clk); +- +- return 0; +-} +- +-static int phy_mvebu_sata_power_off(struct phy *phy) +-{ +- struct priv *priv = phy_get_drvdata(phy); +- u32 reg; +- +- clk_prepare_enable(priv->clk); +- +- /* Disable PLL and IVREF */ +- reg = readl(priv->base + SATA_PHY_MODE_2); +- reg &= ~(MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | +- MODE_2_PU_PLL | MODE_2_PU_IVREF); +- writel(reg, priv->base + SATA_PHY_MODE_2); +- +- /* Disable PHY */ +- reg = readl(priv->base + SATA_IF_CTRL); +- reg |= CTRL_PHY_SHUTDOWN; +- writel(reg, priv->base + SATA_IF_CTRL); +- +- clk_disable_unprepare(priv->clk); +- +- return 0; +-} +- +-static const struct phy_ops phy_mvebu_sata_ops = { +- .power_on = phy_mvebu_sata_power_on, +- .power_off = phy_mvebu_sata_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int phy_mvebu_sata_probe(struct platform_device *pdev) +-{ +- struct phy_provider *phy_provider; +- struct resource *res; +- struct priv *priv; +- struct phy *phy; +- +- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(priv->base)) +- return PTR_ERR(priv->base); +- +- priv->clk = devm_clk_get(&pdev->dev, "sata"); +- if (IS_ERR(priv->clk)) +- return PTR_ERR(priv->clk); +- +- phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops); +- if (IS_ERR(phy)) +- return PTR_ERR(phy); +- +- phy_set_drvdata(phy, priv); +- +- phy_provider = devm_of_phy_provider_register(&pdev->dev, +- of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) +- return PTR_ERR(phy_provider); +- +- /* The boot loader may of left it on. Turn it off. */ +- phy_mvebu_sata_power_off(phy); +- +- return 0; +-} +- +-static const struct of_device_id phy_mvebu_sata_of_match[] = { +- { .compatible = "marvell,mvebu-sata-phy" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match); +- +-static struct platform_driver phy_mvebu_sata_driver = { +- .probe = phy_mvebu_sata_probe, +- .driver = { +- .name = "phy-mvebu-sata", +- .of_match_table = phy_mvebu_sata_of_match, +- } +-}; +-module_platform_driver(phy_mvebu_sata_driver); +- +-MODULE_AUTHOR("Andrew Lunn "); +-MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c +deleted file mode 100644 +index e9c41b3fa0ee..000000000000 +--- a/drivers/phy/phy-omap-control.c ++++ /dev/null +@@ -1,360 +0,0 @@ +-/* +- * omap-control-phy.c - The PHY part of control module. +- * +- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * Author: Kishon Vijay Abraham I +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/** +- * omap_control_pcie_pcs - set the PCS delay count +- * @dev: the control module device +- * @delay: 8 bit delay value +- */ +-void omap_control_pcie_pcs(struct device *dev, u8 delay) +-{ +- u32 val; +- struct omap_control_phy *control_phy; +- +- if (IS_ERR(dev) || !dev) { +- pr_err("%s: invalid device\n", __func__); +- return; +- } +- +- control_phy = dev_get_drvdata(dev); +- if (!control_phy) { +- dev_err(dev, "%s: invalid control phy device\n", __func__); +- return; +- } +- +- if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { +- dev_err(dev, "%s: unsupported operation\n", __func__); +- return; +- } +- +- val = readl(control_phy->pcie_pcs); +- val &= ~(OMAP_CTRL_PCIE_PCS_MASK << +- OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); +- val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); +- writel(val, control_phy->pcie_pcs); +-} +-EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); +- +-/** +- * omap_control_phy_power - power on/off the phy using control module reg +- * @dev: the control module device +- * @on: 0 or 1, based on powering on or off the PHY +- */ +-void omap_control_phy_power(struct device *dev, int on) +-{ +- u32 val; +- unsigned long rate; +- struct omap_control_phy *control_phy; +- +- if (IS_ERR(dev) || !dev) { +- pr_err("%s: invalid device\n", __func__); +- return; +- } +- +- control_phy = dev_get_drvdata(dev); +- if (!control_phy) { +- dev_err(dev, "%s: invalid control phy device\n", __func__); +- return; +- } +- +- if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) +- return; +- +- val = readl(control_phy->power); +- +- switch (control_phy->type) { +- case OMAP_CTRL_TYPE_USB2: +- if (on) +- val &= ~OMAP_CTRL_DEV_PHY_PD; +- else +- val |= OMAP_CTRL_DEV_PHY_PD; +- break; +- +- case OMAP_CTRL_TYPE_PCIE: +- case OMAP_CTRL_TYPE_PIPE3: +- rate = clk_get_rate(control_phy->sys_clk); +- rate = rate/1000000; +- +- if (on) { +- val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | +- OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); +- val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << +- OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; +- val |= rate << +- OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; +- } else { +- val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; +- val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << +- OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; +- } +- break; +- +- case OMAP_CTRL_TYPE_DRA7USB2: +- if (on) +- val &= ~OMAP_CTRL_USB2_PHY_PD; +- else +- val |= OMAP_CTRL_USB2_PHY_PD; +- break; +- +- case OMAP_CTRL_TYPE_AM437USB2: +- if (on) { +- val &= ~(AM437X_CTRL_USB2_PHY_PD | +- AM437X_CTRL_USB2_OTG_PD); +- val |= (AM437X_CTRL_USB2_OTGVDET_EN | +- AM437X_CTRL_USB2_OTGSESSEND_EN); +- } else { +- val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | +- AM437X_CTRL_USB2_OTGSESSEND_EN); +- val |= (AM437X_CTRL_USB2_PHY_PD | +- AM437X_CTRL_USB2_OTG_PD); +- } +- break; +- default: +- dev_err(dev, "%s: type %d not recognized\n", +- __func__, control_phy->type); +- break; +- } +- +- writel(val, control_phy->power); +-} +-EXPORT_SYMBOL_GPL(omap_control_phy_power); +- +-/** +- * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded +- * @ctrl_phy: struct omap_control_phy * +- * +- * Writes to the mailbox register to notify the usb core that a usb +- * device has been connected. +- */ +-static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy) +-{ +- u32 val; +- +- val = readl(ctrl_phy->otghs_control); +- val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); +- val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; +- writel(val, ctrl_phy->otghs_control); +-} +- +-/** +- * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high +- * impedance +- * @ctrl_phy: struct omap_control_phy * +- * +- * Writes to the mailbox register to notify the usb core that it has been +- * connected to a usb host. +- */ +-static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy) +-{ +- u32 val; +- +- val = readl(ctrl_phy->otghs_control); +- val &= ~OMAP_CTRL_DEV_SESSEND; +- val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | +- OMAP_CTRL_DEV_VBUSVALID; +- writel(val, ctrl_phy->otghs_control); +-} +- +-/** +- * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high +- * impedance +- * @ctrl_phy: struct omap_control_phy * +- * +- * Writes to the mailbox register to notify the usb core it's now in +- * disconnected state. +- */ +-static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy) +-{ +- u32 val; +- +- val = readl(ctrl_phy->otghs_control); +- val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); +- val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; +- writel(val, ctrl_phy->otghs_control); +-} +- +-/** +- * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode +- * or device mode or to denote disconnected state +- * @dev: the control module device +- * @mode: The mode to which usb should be configured +- * +- * This is an API to write to the mailbox register to notify the usb core that +- * a usb device has been connected. +- */ +-void omap_control_usb_set_mode(struct device *dev, +- enum omap_control_usb_mode mode) +-{ +- struct omap_control_phy *ctrl_phy; +- +- if (IS_ERR(dev) || !dev) +- return; +- +- ctrl_phy = dev_get_drvdata(dev); +- if (!ctrl_phy) { +- dev_err(dev, "Invalid control phy device\n"); +- return; +- } +- +- if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS) +- return; +- +- switch (mode) { +- case USB_MODE_HOST: +- omap_control_usb_host_mode(ctrl_phy); +- break; +- case USB_MODE_DEVICE: +- omap_control_usb_device_mode(ctrl_phy); +- break; +- case USB_MODE_DISCONNECT: +- omap_control_usb_set_sessionend(ctrl_phy); +- break; +- default: +- dev_vdbg(dev, "invalid omap control usb mode\n"); +- } +-} +-EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); +- +-static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; +-static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; +-static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; +-static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; +-static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; +-static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; +- +-static const struct of_device_id omap_control_phy_id_table[] = { +- { +- .compatible = "ti,control-phy-otghs", +- .data = &otghs_data, +- }, +- { +- .compatible = "ti,control-phy-usb2", +- .data = &usb2_data, +- }, +- { +- .compatible = "ti,control-phy-pipe3", +- .data = &pipe3_data, +- }, +- { +- .compatible = "ti,control-phy-pcie", +- .data = &pcie_data, +- }, +- { +- .compatible = "ti,control-phy-usb2-dra7", +- .data = &dra7usb2_data, +- }, +- { +- .compatible = "ti,control-phy-usb2-am437", +- .data = &am437usb2_data, +- }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, omap_control_phy_id_table); +- +-static int omap_control_phy_probe(struct platform_device *pdev) +-{ +- struct resource *res; +- const struct of_device_id *of_id; +- struct omap_control_phy *control_phy; +- +- of_id = of_match_device(omap_control_phy_id_table, &pdev->dev); +- if (!of_id) +- return -EINVAL; +- +- control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy), +- GFP_KERNEL); +- if (!control_phy) +- return -ENOMEM; +- +- control_phy->dev = &pdev->dev; +- control_phy->type = *(enum omap_control_phy_type *)of_id->data; +- +- if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) { +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +- "otghs_control"); +- control_phy->otghs_control = devm_ioremap_resource( +- &pdev->dev, res); +- if (IS_ERR(control_phy->otghs_control)) +- return PTR_ERR(control_phy->otghs_control); +- } else { +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +- "power"); +- control_phy->power = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(control_phy->power)) { +- dev_err(&pdev->dev, "Couldn't get power register\n"); +- return PTR_ERR(control_phy->power); +- } +- } +- +- if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || +- control_phy->type == OMAP_CTRL_TYPE_PCIE) { +- control_phy->sys_clk = devm_clk_get(control_phy->dev, +- "sys_clkin"); +- if (IS_ERR(control_phy->sys_clk)) { +- pr_err("%s: unable to get sys_clkin\n", __func__); +- return -EINVAL; +- } +- } +- +- if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +- "pcie_pcs"); +- control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(control_phy->pcie_pcs)) +- return PTR_ERR(control_phy->pcie_pcs); +- } +- +- dev_set_drvdata(control_phy->dev, control_phy); +- +- return 0; +-} +- +-static struct platform_driver omap_control_phy_driver = { +- .probe = omap_control_phy_probe, +- .driver = { +- .name = "omap-control-phy", +- .of_match_table = omap_control_phy_id_table, +- }, +-}; +- +-static int __init omap_control_phy_init(void) +-{ +- return platform_driver_register(&omap_control_phy_driver); +-} +-subsys_initcall(omap_control_phy_init); +- +-static void __exit omap_control_phy_exit(void) +-{ +- platform_driver_unregister(&omap_control_phy_driver); +-} +-module_exit(omap_control_phy_exit); +- +-MODULE_ALIAS("platform:omap_control_phy"); +-MODULE_AUTHOR("Texas Instruments Inc."); +-MODULE_DESCRIPTION("OMAP Control Module PHY Driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c +deleted file mode 100644 +index fe909fd8144f..000000000000 +--- a/drivers/phy/phy-omap-usb2.c ++++ /dev/null +@@ -1,439 +0,0 @@ +-/* +- * omap-usb2.c - USB PHY, talking to musb controller in OMAP. +- * +- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * Author: Kishon Vijay Abraham I +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define USB2PHY_DISCON_BYP_LATCH (1 << 31) +-#define USB2PHY_ANA_CONFIG1 0x4c +- +-/** +- * omap_usb2_set_comparator - links the comparator present in the sytem with +- * this phy +- * @comparator - the companion phy(comparator) for this phy +- * +- * The phy companion driver should call this API passing the phy_companion +- * filled with set_vbus and start_srp to be used by usb phy. +- * +- * For use by phy companion driver +- */ +-int omap_usb2_set_comparator(struct phy_companion *comparator) +-{ +- struct omap_usb *phy; +- struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2); +- +- if (IS_ERR(x)) +- return -ENODEV; +- +- phy = phy_to_omapusb(x); +- phy->comparator = comparator; +- return 0; +-} +-EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); +- +-static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) +-{ +- struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); +- +- if (!phy->comparator) +- return -ENODEV; +- +- return phy->comparator->set_vbus(phy->comparator, enabled); +-} +- +-static int omap_usb_start_srp(struct usb_otg *otg) +-{ +- struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); +- +- if (!phy->comparator) +- return -ENODEV; +- +- return phy->comparator->start_srp(phy->comparator); +-} +- +-static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) +-{ +- otg->host = host; +- if (!host) +- otg->state = OTG_STATE_UNDEFINED; +- +- return 0; +-} +- +-static int omap_usb_set_peripheral(struct usb_otg *otg, +- struct usb_gadget *gadget) +-{ +- otg->gadget = gadget; +- if (!gadget) +- otg->state = OTG_STATE_UNDEFINED; +- +- return 0; +-} +- +-static int omap_usb_phy_power(struct omap_usb *phy, int on) +-{ +- u32 val; +- int ret; +- +- if (!phy->syscon_phy_power) { +- omap_control_phy_power(phy->control_dev, on); +- return 0; +- } +- +- if (on) +- val = phy->power_on; +- else +- val = phy->power_off; +- +- ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg, +- phy->mask, val); +- return ret; +-} +- +-static int omap_usb_power_off(struct phy *x) +-{ +- struct omap_usb *phy = phy_get_drvdata(x); +- +- return omap_usb_phy_power(phy, false); +-} +- +-static int omap_usb_power_on(struct phy *x) +-{ +- struct omap_usb *phy = phy_get_drvdata(x); +- +- return omap_usb_phy_power(phy, true); +-} +- +-static int omap_usb2_disable_clocks(struct omap_usb *phy) +-{ +- clk_disable(phy->wkupclk); +- if (!IS_ERR(phy->optclk)) +- clk_disable(phy->optclk); +- +- return 0; +-} +- +-static int omap_usb2_enable_clocks(struct omap_usb *phy) +-{ +- int ret; +- +- ret = clk_enable(phy->wkupclk); +- if (ret < 0) { +- dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); +- goto err0; +- } +- +- if (!IS_ERR(phy->optclk)) { +- ret = clk_enable(phy->optclk); +- if (ret < 0) { +- dev_err(phy->dev, "Failed to enable optclk %d\n", ret); +- goto err1; +- } +- } +- +- return 0; +- +-err1: +- clk_disable(phy->wkupclk); +- +-err0: +- return ret; +-} +- +-static int omap_usb_init(struct phy *x) +-{ +- struct omap_usb *phy = phy_get_drvdata(x); +- u32 val; +- +- omap_usb2_enable_clocks(phy); +- +- if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { +- /* +- * +- * Reduce the sensitivity of internal PHY by enabling the +- * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This +- * resolves issues with certain devices which can otherwise +- * be prone to false disconnects. +- * +- */ +- val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1); +- val |= USB2PHY_DISCON_BYP_LATCH; +- omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val); +- } +- +- return 0; +-} +- +-static int omap_usb_exit(struct phy *x) +-{ +- struct omap_usb *phy = phy_get_drvdata(x); +- +- return omap_usb2_disable_clocks(phy); +-} +- +-static const struct phy_ops ops = { +- .init = omap_usb_init, +- .exit = omap_usb_exit, +- .power_on = omap_usb_power_on, +- .power_off = omap_usb_power_off, +- .owner = THIS_MODULE, +-}; +- +-static const struct usb_phy_data omap_usb2_data = { +- .label = "omap_usb2", +- .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS, +- .mask = OMAP_DEV_PHY_PD, +- .power_off = OMAP_DEV_PHY_PD, +-}; +- +-static const struct usb_phy_data omap5_usb2_data = { +- .label = "omap5_usb2", +- .flags = 0, +- .mask = OMAP_DEV_PHY_PD, +- .power_off = OMAP_DEV_PHY_PD, +-}; +- +-static const struct usb_phy_data dra7x_usb2_data = { +- .label = "dra7x_usb2", +- .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, +- .mask = OMAP_DEV_PHY_PD, +- .power_off = OMAP_DEV_PHY_PD, +-}; +- +-static const struct usb_phy_data dra7x_usb2_phy2_data = { +- .label = "dra7x_usb2_phy2", +- .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, +- .mask = OMAP_USB2_PHY_PD, +- .power_off = OMAP_USB2_PHY_PD, +-}; +- +-static const struct usb_phy_data am437x_usb2_data = { +- .label = "am437x_usb2", +- .flags = 0, +- .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD | +- AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, +- .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, +- .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, +-}; +- +-static const struct of_device_id omap_usb2_id_table[] = { +- { +- .compatible = "ti,omap-usb2", +- .data = &omap_usb2_data, +- }, +- { +- .compatible = "ti,omap5-usb2", +- .data = &omap5_usb2_data, +- }, +- { +- .compatible = "ti,dra7x-usb2", +- .data = &dra7x_usb2_data, +- }, +- { +- .compatible = "ti,dra7x-usb2-phy2", +- .data = &dra7x_usb2_phy2_data, +- }, +- { +- .compatible = "ti,am437x-usb2", +- .data = &am437x_usb2_data, +- }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, omap_usb2_id_table); +- +-static int omap_usb2_probe(struct platform_device *pdev) +-{ +- struct omap_usb *phy; +- struct phy *generic_phy; +- struct resource *res; +- struct phy_provider *phy_provider; +- struct usb_otg *otg; +- struct device_node *node = pdev->dev.of_node; +- struct device_node *control_node; +- struct platform_device *control_pdev; +- const struct of_device_id *of_id; +- struct usb_phy_data *phy_data; +- +- of_id = of_match_device(omap_usb2_id_table, &pdev->dev); +- +- if (!of_id) +- return -EINVAL; +- +- phy_data = (struct usb_phy_data *)of_id->data; +- +- phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) +- return -ENOMEM; +- +- otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); +- if (!otg) +- return -ENOMEM; +- +- phy->dev = &pdev->dev; +- +- phy->phy.dev = phy->dev; +- phy->phy.label = phy_data->label; +- phy->phy.otg = otg; +- phy->phy.type = USB_PHY_TYPE_USB2; +- phy->mask = phy_data->mask; +- phy->power_on = phy_data->power_on; +- phy->power_off = phy_data->power_off; +- +- if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- phy->phy_base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(phy->phy_base)) +- return PTR_ERR(phy->phy_base); +- phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT; +- } +- +- phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node, +- "syscon-phy-power"); +- if (IS_ERR(phy->syscon_phy_power)) { +- dev_dbg(&pdev->dev, +- "can't get syscon-phy-power, using control device\n"); +- phy->syscon_phy_power = NULL; +- +- control_node = of_parse_phandle(node, "ctrl-module", 0); +- if (!control_node) { +- dev_err(&pdev->dev, +- "Failed to get control device phandle\n"); +- return -EINVAL; +- } +- +- control_pdev = of_find_device_by_node(control_node); +- if (!control_pdev) { +- dev_err(&pdev->dev, "Failed to get control device\n"); +- return -EINVAL; +- } +- phy->control_dev = &control_pdev->dev; +- } else { +- if (of_property_read_u32_index(node, +- "syscon-phy-power", 1, +- &phy->power_reg)) { +- dev_err(&pdev->dev, +- "couldn't get power reg. offset\n"); +- return -EINVAL; +- } +- } +- +- otg->set_host = omap_usb_set_host; +- otg->set_peripheral = omap_usb_set_peripheral; +- if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) +- otg->set_vbus = omap_usb_set_vbus; +- if (phy_data->flags & OMAP_USB2_HAS_START_SRP) +- otg->start_srp = omap_usb_start_srp; +- otg->usb_phy = &phy->phy; +- +- platform_set_drvdata(pdev, phy); +- pm_runtime_enable(phy->dev); +- +- generic_phy = devm_phy_create(phy->dev, NULL, &ops); +- if (IS_ERR(generic_phy)) { +- pm_runtime_disable(phy->dev); +- return PTR_ERR(generic_phy); +- } +- +- phy_set_drvdata(generic_phy, phy); +- omap_usb_power_off(generic_phy); +- +- phy_provider = devm_of_phy_provider_register(phy->dev, +- of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) { +- pm_runtime_disable(phy->dev); +- return PTR_ERR(phy_provider); +- } +- +- phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); +- if (IS_ERR(phy->wkupclk)) { +- dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n"); +- phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); +- if (IS_ERR(phy->wkupclk)) { +- dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); +- pm_runtime_disable(phy->dev); +- return PTR_ERR(phy->wkupclk); +- } else { +- dev_warn(&pdev->dev, +- "found usb_phy_cm_clk32k, please fix DTS\n"); +- } +- } +- clk_prepare(phy->wkupclk); +- +- phy->optclk = devm_clk_get(phy->dev, "refclk"); +- if (IS_ERR(phy->optclk)) { +- dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n"); +- phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); +- if (IS_ERR(phy->optclk)) { +- dev_dbg(&pdev->dev, +- "unable to get usb_otg_ss_refclk960m\n"); +- } else { +- dev_warn(&pdev->dev, +- "found usb_otg_ss_refclk960m, please fix DTS\n"); +- } +- } +- +- if (!IS_ERR(phy->optclk)) +- clk_prepare(phy->optclk); +- +- usb_add_phy_dev(&phy->phy); +- +- return 0; +-} +- +-static int omap_usb2_remove(struct platform_device *pdev) +-{ +- struct omap_usb *phy = platform_get_drvdata(pdev); +- +- clk_unprepare(phy->wkupclk); +- if (!IS_ERR(phy->optclk)) +- clk_unprepare(phy->optclk); +- usb_remove_phy(&phy->phy); +- pm_runtime_disable(phy->dev); +- +- return 0; +-} +- +-static struct platform_driver omap_usb2_driver = { +- .probe = omap_usb2_probe, +- .remove = omap_usb2_remove, +- .driver = { +- .name = "omap-usb2", +- .of_match_table = omap_usb2_id_table, +- }, +-}; +- +-module_platform_driver(omap_usb2_driver); +- +-MODULE_ALIAS("platform:omap_usb2"); +-MODULE_AUTHOR("Texas Instruments Inc."); +-MODULE_DESCRIPTION("OMAP USB2 phy driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-pxa-28nm-hsic.c b/drivers/phy/phy-pxa-28nm-hsic.c +deleted file mode 100644 +index 234aacf4db20..000000000000 +--- a/drivers/phy/phy-pxa-28nm-hsic.c ++++ /dev/null +@@ -1,220 +0,0 @@ +-/* +- * Copyright (C) 2015 Linaro, Ltd. +- * Rob Herring +- * +- * Based on vendor driver: +- * Copyright (C) 2013 Marvell Inc. +- * Author: Chao Xie +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define PHY_28NM_HSIC_CTRL 0x08 +-#define PHY_28NM_HSIC_IMPCAL_CAL 0x18 +-#define PHY_28NM_HSIC_PLL_CTRL01 0x1c +-#define PHY_28NM_HSIC_PLL_CTRL2 0x20 +-#define PHY_28NM_HSIC_INT 0x28 +- +-#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 +-#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 +-#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 +- +-#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10) +-#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15) +-#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7) +-#define S2H_DRV_SE0_4RESUME BIT(14) +-#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27) +- +-#define PHY_28NM_HSIC_CONNECT_INT BIT(1) +-#define PHY_28NM_HSIC_HS_READY_INT BIT(2) +- +-struct mv_hsic_phy { +- struct phy *phy; +- struct platform_device *pdev; +- void __iomem *base; +- struct clk *clk; +-}; +- +-static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +-{ +- timeout += jiffies; +- while (time_is_after_eq_jiffies(timeout)) { +- if ((readl(reg) & mask) == mask) +- return true; +- msleep(1); +- } +- return false; +-} +- +-static int mv_hsic_phy_init(struct phy *phy) +-{ +- struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); +- struct platform_device *pdev = mv_phy->pdev; +- void __iomem *base = mv_phy->base; +- +- clk_prepare_enable(mv_phy->clk); +- +- /* Set reference clock */ +- writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT | +- 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT | +- 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT, +- base + PHY_28NM_HSIC_PLL_CTRL01); +- +- /* Turn on PLL */ +- writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | +- PHY_28NM_HSIC_S2H_PU_PLL, +- base + PHY_28NM_HSIC_PLL_CTRL2); +- +- /* Make sure PHY PLL is locked */ +- if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, +- PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { +- dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); +- clk_disable_unprepare(mv_phy->clk); +- return -ETIMEDOUT; +- } +- +- return 0; +-} +- +-static int mv_hsic_phy_power_on(struct phy *phy) +-{ +- struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); +- struct platform_device *pdev = mv_phy->pdev; +- void __iomem *base = mv_phy->base; +- u32 reg; +- +- reg = readl(base + PHY_28NM_HSIC_CTRL); +- /* Avoid SE0 state when resume for some device will take it as reset */ +- reg &= ~S2H_DRV_SE0_4RESUME; +- reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */ +- writel(reg, base + PHY_28NM_HSIC_CTRL); +- +- /* +- * Calibration Timing +- * ____________________________ +- * CAL START ___| +- * ____________________ +- * CAL_DONE ___________| +- * | 400us | +- */ +- +- /* Make sure PHY Calibration is ready */ +- if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, +- PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { +- dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); +- return -ETIMEDOUT; +- } +- +- /* Waiting for HSIC connect int*/ +- if (!wait_for_reg(base + PHY_28NM_HSIC_INT, +- PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { +- dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); +- return -ETIMEDOUT; +- } +- +- return 0; +-} +- +-static int mv_hsic_phy_power_off(struct phy *phy) +-{ +- struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); +- void __iomem *base = mv_phy->base; +- +- writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN, +- base + PHY_28NM_HSIC_CTRL); +- +- return 0; +-} +- +-static int mv_hsic_phy_exit(struct phy *phy) +-{ +- struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); +- void __iomem *base = mv_phy->base; +- +- /* Turn off PLL */ +- writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & +- ~PHY_28NM_HSIC_S2H_PU_PLL, +- base + PHY_28NM_HSIC_PLL_CTRL2); +- +- clk_disable_unprepare(mv_phy->clk); +- return 0; +-} +- +- +-static const struct phy_ops hsic_ops = { +- .init = mv_hsic_phy_init, +- .power_on = mv_hsic_phy_power_on, +- .power_off = mv_hsic_phy_power_off, +- .exit = mv_hsic_phy_exit, +- .owner = THIS_MODULE, +-}; +- +-static int mv_hsic_phy_probe(struct platform_device *pdev) +-{ +- struct phy_provider *phy_provider; +- struct mv_hsic_phy *mv_phy; +- struct resource *r; +- +- mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); +- if (!mv_phy) +- return -ENOMEM; +- +- mv_phy->pdev = pdev; +- +- mv_phy->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(mv_phy->clk)) { +- dev_err(&pdev->dev, "failed to get clock.\n"); +- return PTR_ERR(mv_phy->clk); +- } +- +- r = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- mv_phy->base = devm_ioremap_resource(&pdev->dev, r); +- if (IS_ERR(mv_phy->base)) +- return PTR_ERR(mv_phy->base); +- +- mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops); +- if (IS_ERR(mv_phy->phy)) +- return PTR_ERR(mv_phy->phy); +- +- phy_set_drvdata(mv_phy->phy, mv_phy); +- +- phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id mv_hsic_phy_dt_match[] = { +- { .compatible = "marvell,pxa1928-hsic-phy", }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); +- +-static struct platform_driver mv_hsic_phy_driver = { +- .probe = mv_hsic_phy_probe, +- .driver = { +- .name = "mv-hsic-phy", +- .of_match_table = of_match_ptr(mv_hsic_phy_dt_match), +- }, +-}; +-module_platform_driver(mv_hsic_phy_driver); +- +-MODULE_AUTHOR("Rob Herring "); +-MODULE_DESCRIPTION("Marvell HSIC phy driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-pxa-28nm-usb2.c b/drivers/phy/phy-pxa-28nm-usb2.c +deleted file mode 100644 +index 37e9c8ca4983..000000000000 +--- a/drivers/phy/phy-pxa-28nm-usb2.c ++++ /dev/null +@@ -1,355 +0,0 @@ +-/* +- * Copyright (C) 2015 Linaro, Ltd. +- * Rob Herring +- * +- * Based on vendor driver: +- * Copyright (C) 2013 Marvell Inc. +- * Author: Chao Xie +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* USB PXA1928 PHY mapping */ +-#define PHY_28NM_PLL_REG0 0x0 +-#define PHY_28NM_PLL_REG1 0x4 +-#define PHY_28NM_CAL_REG 0x8 +-#define PHY_28NM_TX_REG0 0x0c +-#define PHY_28NM_TX_REG1 0x10 +-#define PHY_28NM_RX_REG0 0x14 +-#define PHY_28NM_RX_REG1 0x18 +-#define PHY_28NM_DIG_REG0 0x1c +-#define PHY_28NM_DIG_REG1 0x20 +-#define PHY_28NM_TEST_REG0 0x24 +-#define PHY_28NM_TEST_REG1 0x28 +-#define PHY_28NM_MOC_REG 0x2c +-#define PHY_28NM_PHY_RESERVE 0x30 +-#define PHY_28NM_OTG_REG 0x34 +-#define PHY_28NM_CHRG_DET 0x38 +-#define PHY_28NM_CTRL_REG0 0xc4 +-#define PHY_28NM_CTRL_REG1 0xc8 +-#define PHY_28NM_CTRL_REG2 0xd4 +-#define PHY_28NM_CTRL_REG3 0xdc +- +-/* PHY_28NM_PLL_REG0 */ +-#define PHY_28NM_PLL_READY BIT(31) +- +-#define PHY_28NM_PLL_SELLPFR_SHIFT 28 +-#define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28) +- +-#define PHY_28NM_PLL_FBDIV_SHIFT 16 +-#define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16) +- +-#define PHY_28NM_PLL_ICP_SHIFT 8 +-#define PHY_28NM_PLL_ICP_MASK (0x7 << 8) +- +-#define PHY_28NM_PLL_REFDIV_SHIFT 0 +-#define PHY_28NM_PLL_REFDIV_MASK 0x7f +- +-/* PHY_28NM_PLL_REG1 */ +-#define PHY_28NM_PLL_PU_BY_REG BIT(1) +- +-#define PHY_28NM_PLL_PU_PLL BIT(0) +- +-/* PHY_28NM_CAL_REG */ +-#define PHY_28NM_PLL_PLLCAL_DONE BIT(31) +- +-#define PHY_28NM_PLL_IMPCAL_DONE BIT(23) +- +-#define PHY_28NM_PLL_KVCO_SHIFT 16 +-#define PHY_28NM_PLL_KVCO_MASK (0x7 << 16) +- +-#define PHY_28NM_PLL_CAL12_SHIFT 20 +-#define PHY_28NM_PLL_CAL12_MASK (0x3 << 20) +- +-#define PHY_28NM_IMPCAL_VTH_SHIFT 8 +-#define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8) +- +-#define PHY_28NM_PLLCAL_START_SHIFT 22 +-#define PHY_28NM_IMPCAL_START_SHIFT 13 +- +-/* PHY_28NM_TX_REG0 */ +-#define PHY_28NM_TX_PU_BY_REG BIT(25) +- +-#define PHY_28NM_TX_PU_ANA BIT(24) +- +-#define PHY_28NM_TX_AMP_SHIFT 20 +-#define PHY_28NM_TX_AMP_MASK (0x7 << 20) +- +-/* PHY_28NM_RX_REG0 */ +-#define PHY_28NM_RX_SQ_THRESH_SHIFT 0 +-#define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0) +- +-/* PHY_28NM_RX_REG1 */ +-#define PHY_28NM_RX_SQCAL_DONE BIT(31) +- +-/* PHY_28NM_DIG_REG0 */ +-#define PHY_28NM_DIG_BITSTAFFING_ERR BIT(31) +-#define PHY_28NM_DIG_SYNC_ERR BIT(30) +- +-#define PHY_28NM_DIG_SQ_FILT_SHIFT 16 +-#define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16) +- +-#define PHY_28NM_DIG_SQ_BLK_SHIFT 12 +-#define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12) +- +-#define PHY_28NM_DIG_SYNC_NUM_SHIFT 0 +-#define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0) +- +-#define PHY_28NM_PLL_LOCK_BYPASS BIT(7) +- +-/* PHY_28NM_OTG_REG */ +-#define PHY_28NM_OTG_CONTROL_BY_PIN BIT(5) +-#define PHY_28NM_OTG_PU_OTG BIT(4) +- +-#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13 +-#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12 +-#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10 +-#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8 +-#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7 +-#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6 +-#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5 +-#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4 +-#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3 +-#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2 +-#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0 +- +-#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4 +-#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2 +- +-#define PHY_28NM_CTRL3_OVERWRITE BIT(0) +-#define PHY_28NM_CTRL3_VBUS_VALID BIT(4) +-#define PHY_28NM_CTRL3_AVALID BIT(5) +-#define PHY_28NM_CTRL3_BVALID BIT(6) +- +-struct mv_usb2_phy { +- struct phy *phy; +- struct platform_device *pdev; +- void __iomem *base; +- struct clk *clk; +-}; +- +-static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +-{ +- timeout += jiffies; +- while (time_is_after_eq_jiffies(timeout)) { +- if ((readl(reg) & mask) == mask) +- return true; +- msleep(1); +- } +- return false; +-} +- +-static int mv_usb2_phy_28nm_init(struct phy *phy) +-{ +- struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); +- struct platform_device *pdev = mv_phy->pdev; +- void __iomem *base = mv_phy->base; +- u32 reg; +- int ret; +- +- clk_prepare_enable(mv_phy->clk); +- +- /* PHY_28NM_PLL_REG0 */ +- reg = readl(base + PHY_28NM_PLL_REG0) & +- ~(PHY_28NM_PLL_SELLPFR_MASK | PHY_28NM_PLL_FBDIV_MASK +- | PHY_28NM_PLL_ICP_MASK | PHY_28NM_PLL_REFDIV_MASK); +- writel(reg | (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT +- | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT +- | 0x3 << PHY_28NM_PLL_ICP_SHIFT +- | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), +- base + PHY_28NM_PLL_REG0); +- +- /* PHY_28NM_PLL_REG1 */ +- reg = readl(base + PHY_28NM_PLL_REG1); +- writel(reg | PHY_28NM_PLL_PU_PLL | PHY_28NM_PLL_PU_BY_REG, +- base + PHY_28NM_PLL_REG1); +- +- /* PHY_28NM_TX_REG0 */ +- reg = readl(base + PHY_28NM_TX_REG0) & ~PHY_28NM_TX_AMP_MASK; +- writel(reg | PHY_28NM_TX_PU_BY_REG | 0x3 << PHY_28NM_TX_AMP_SHIFT | +- PHY_28NM_TX_PU_ANA, +- base + PHY_28NM_TX_REG0); +- +- /* PHY_28NM_RX_REG0 */ +- reg = readl(base + PHY_28NM_RX_REG0) & ~PHY_28NM_RX_SQ_THRESH_MASK; +- writel(reg | 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT, +- base + PHY_28NM_RX_REG0); +- +- /* PHY_28NM_DIG_REG0 */ +- reg = readl(base + PHY_28NM_DIG_REG0) & +- ~(PHY_28NM_DIG_BITSTAFFING_ERR | PHY_28NM_DIG_SYNC_ERR | +- PHY_28NM_DIG_SQ_FILT_MASK | PHY_28NM_DIG_SQ_BLK_MASK | +- PHY_28NM_DIG_SYNC_NUM_MASK); +- writel(reg | (0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT | +- PHY_28NM_PLL_LOCK_BYPASS), +- base + PHY_28NM_DIG_REG0); +- +- /* PHY_28NM_OTG_REG */ +- reg = readl(base + PHY_28NM_OTG_REG) | PHY_28NM_OTG_PU_OTG; +- writel(reg & ~PHY_28NM_OTG_CONTROL_BY_PIN, base + PHY_28NM_OTG_REG); +- +- /* +- * Calibration Timing +- * ____________________________ +- * CAL START ___| +- * ____________________ +- * CAL_DONE ___________| +- * | 400us | +- */ +- +- /* Make sure PHY Calibration is ready */ +- if (!wait_for_reg(base + PHY_28NM_CAL_REG, +- PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, +- HZ / 10)) { +- dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS."); +- ret = -ETIMEDOUT; +- goto err_clk; +- } +- if (!wait_for_reg(base + PHY_28NM_RX_REG1, +- PHY_28NM_RX_SQCAL_DONE, HZ / 10)) { +- dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS."); +- ret = -ETIMEDOUT; +- goto err_clk; +- } +- /* Make sure PHY PLL is ready */ +- if (!wait_for_reg(base + PHY_28NM_PLL_REG0, +- PHY_28NM_PLL_READY, HZ / 10)) { +- dev_warn(&pdev->dev, "PLL_READY not set after 100mS."); +- ret = -ETIMEDOUT; +- goto err_clk; +- } +- +- return 0; +-err_clk: +- clk_disable_unprepare(mv_phy->clk); +- return ret; +-} +- +-static int mv_usb2_phy_28nm_power_on(struct phy *phy) +-{ +- struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); +- void __iomem *base = mv_phy->base; +- +- writel(readl(base + PHY_28NM_CTRL_REG3) | +- (PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID | +- PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), +- base + PHY_28NM_CTRL_REG3); +- +- return 0; +-} +- +-static int mv_usb2_phy_28nm_power_off(struct phy *phy) +-{ +- struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); +- void __iomem *base = mv_phy->base; +- +- writel(readl(base + PHY_28NM_CTRL_REG3) | +- ~(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID +- | PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), +- base + PHY_28NM_CTRL_REG3); +- +- return 0; +-} +- +-static int mv_usb2_phy_28nm_exit(struct phy *phy) +-{ +- struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); +- void __iomem *base = mv_phy->base; +- unsigned int val; +- +- val = readw(base + PHY_28NM_PLL_REG1); +- val &= ~PHY_28NM_PLL_PU_PLL; +- writew(val, base + PHY_28NM_PLL_REG1); +- +- /* power down PHY Analog part */ +- val = readw(base + PHY_28NM_TX_REG0); +- val &= ~PHY_28NM_TX_PU_ANA; +- writew(val, base + PHY_28NM_TX_REG0); +- +- /* power down PHY OTG part */ +- val = readw(base + PHY_28NM_OTG_REG); +- val &= ~PHY_28NM_OTG_PU_OTG; +- writew(val, base + PHY_28NM_OTG_REG); +- +- clk_disable_unprepare(mv_phy->clk); +- return 0; +-} +- +-static const struct phy_ops usb_ops = { +- .init = mv_usb2_phy_28nm_init, +- .power_on = mv_usb2_phy_28nm_power_on, +- .power_off = mv_usb2_phy_28nm_power_off, +- .exit = mv_usb2_phy_28nm_exit, +- .owner = THIS_MODULE, +-}; +- +-static int mv_usb2_phy_probe(struct platform_device *pdev) +-{ +- struct phy_provider *phy_provider; +- struct mv_usb2_phy *mv_phy; +- struct resource *r; +- +- mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); +- if (!mv_phy) +- return -ENOMEM; +- +- mv_phy->pdev = pdev; +- +- mv_phy->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(mv_phy->clk)) { +- dev_err(&pdev->dev, "failed to get clock.\n"); +- return PTR_ERR(mv_phy->clk); +- } +- +- r = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- mv_phy->base = devm_ioremap_resource(&pdev->dev, r); +- if (IS_ERR(mv_phy->base)) +- return PTR_ERR(mv_phy->base); +- +- mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &usb_ops); +- if (IS_ERR(mv_phy->phy)) +- return PTR_ERR(mv_phy->phy); +- +- phy_set_drvdata(mv_phy->phy, mv_phy); +- +- phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id mv_usbphy_dt_match[] = { +- { .compatible = "marvell,pxa1928-usb-phy", }, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match); +- +-static struct platform_driver mv_usb2_phy_driver = { +- .probe = mv_usb2_phy_probe, +- .driver = { +- .name = "mv-usb2-phy", +- .of_match_table = of_match_ptr(mv_usbphy_dt_match), +- }, +-}; +-module_platform_driver(mv_usb2_phy_driver); +- +-MODULE_AUTHOR("Rob Herring "); +-MODULE_DESCRIPTION("Marvell USB2 phy driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c +deleted file mode 100644 +index 69ce2afac015..000000000000 +--- a/drivers/phy/phy-qcom-apq8064-sata.c ++++ /dev/null +@@ -1,287 +0,0 @@ +-/* +- * Copyright (c) 2014, The Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* PHY registers */ +-#define UNIPHY_PLL_REFCLK_CFG 0x000 +-#define UNIPHY_PLL_PWRGEN_CFG 0x014 +-#define UNIPHY_PLL_GLB_CFG 0x020 +-#define UNIPHY_PLL_SDM_CFG0 0x038 +-#define UNIPHY_PLL_SDM_CFG1 0x03C +-#define UNIPHY_PLL_SDM_CFG2 0x040 +-#define UNIPHY_PLL_SDM_CFG3 0x044 +-#define UNIPHY_PLL_SDM_CFG4 0x048 +-#define UNIPHY_PLL_SSC_CFG0 0x04C +-#define UNIPHY_PLL_SSC_CFG1 0x050 +-#define UNIPHY_PLL_SSC_CFG2 0x054 +-#define UNIPHY_PLL_SSC_CFG3 0x058 +-#define UNIPHY_PLL_LKDET_CFG0 0x05C +-#define UNIPHY_PLL_LKDET_CFG1 0x060 +-#define UNIPHY_PLL_LKDET_CFG2 0x064 +-#define UNIPHY_PLL_CAL_CFG0 0x06C +-#define UNIPHY_PLL_CAL_CFG8 0x08C +-#define UNIPHY_PLL_CAL_CFG9 0x090 +-#define UNIPHY_PLL_CAL_CFG10 0x094 +-#define UNIPHY_PLL_CAL_CFG11 0x098 +-#define UNIPHY_PLL_STATUS 0x0C0 +- +-#define SATA_PHY_SER_CTRL 0x100 +-#define SATA_PHY_TX_DRIV_CTRL0 0x104 +-#define SATA_PHY_TX_DRIV_CTRL1 0x108 +-#define SATA_PHY_TX_IMCAL0 0x11C +-#define SATA_PHY_TX_IMCAL2 0x124 +-#define SATA_PHY_RX_IMCAL0 0x128 +-#define SATA_PHY_EQUAL 0x13C +-#define SATA_PHY_OOB_TERM 0x144 +-#define SATA_PHY_CDR_CTRL0 0x148 +-#define SATA_PHY_CDR_CTRL1 0x14C +-#define SATA_PHY_CDR_CTRL2 0x150 +-#define SATA_PHY_CDR_CTRL3 0x154 +-#define SATA_PHY_PI_CTRL0 0x168 +-#define SATA_PHY_POW_DWN_CTRL0 0x180 +-#define SATA_PHY_POW_DWN_CTRL1 0x184 +-#define SATA_PHY_TX_DATA_CTRL 0x188 +-#define SATA_PHY_ALIGNP 0x1A4 +-#define SATA_PHY_TX_IMCAL_STAT 0x1E4 +-#define SATA_PHY_RX_IMCAL_STAT 0x1E8 +- +-#define UNIPHY_PLL_LOCK BIT(0) +-#define SATA_PHY_TX_CAL BIT(0) +-#define SATA_PHY_RX_CAL BIT(0) +- +-/* default timeout set to 1 sec */ +-#define TIMEOUT_MS 10000 +-#define DELAY_INTERVAL_US 100 +- +-struct qcom_apq8064_sata_phy { +- void __iomem *mmio; +- struct clk *cfg_clk; +- struct device *dev; +-}; +- +-/* Helper function to do poll and timeout */ +-static int read_poll_timeout(void __iomem *addr, u32 mask) +-{ +- unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); +- +- do { +- if (readl_relaxed(addr) & mask) +- return 0; +- +- usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); +- } while (!time_after(jiffies, timeout)); +- +- return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; +-} +- +-static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) +-{ +- struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); +- void __iomem *base = phy->mmio; +- int ret = 0; +- +- /* SATA phy initialization */ +- writel_relaxed(0x01, base + SATA_PHY_SER_CTRL); +- writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0); +- /* Make sure the power down happens before power up */ +- mb(); +- usleep_range(10, 60); +- +- writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); +- writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); +- writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); +- writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); +- writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2); +- +- /* Write UNIPHYPLL registers to configure PLL */ +- writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG); +- writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG); +- +- writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0); +- writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8); +- writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9); +- writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10); +- writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11); +- +- writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0); +- writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1); +- writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2); +- writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3); +- writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4); +- +- writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0); +- writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1); +- writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2); +- writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3); +- +- writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0); +- writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1); +- +- writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG); +- /* make sure global config LDO power down happens before power up */ +- mb(); +- +- writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG); +- writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2); +- +- /* PLL Lock wait */ +- ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); +- if (ret) { +- dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n"); +- return ret; +- } +- +- /* TX Calibration */ +- ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); +- if (ret) { +- dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n"); +- return ret; +- } +- +- /* RX Calibration */ +- ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); +- if (ret) { +- dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n"); +- return ret; +- } +- +- /* SATA phy calibrated succesfully, power up to functional mode */ +- writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); +- writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); +- writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); +- +- writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1); +- writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0); +- writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1); +- writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2); +- writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0); +- writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3); +- writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); +- +- writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL); +- writel_relaxed(0x43, base + SATA_PHY_ALIGNP); +- writel_relaxed(0x04, base + SATA_PHY_OOB_TERM); +- +- writel_relaxed(0x01, base + SATA_PHY_EQUAL); +- writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0); +- writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1); +- +- return 0; +-} +- +-static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy) +-{ +- struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); +- void __iomem *base = phy->mmio; +- +- /* Power down PHY */ +- writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0); +- writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1); +- +- /* Power down PLL block */ +- writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG); +- +- return 0; +-} +- +-static const struct phy_ops qcom_apq8064_sata_phy_ops = { +- .init = qcom_apq8064_sata_phy_init, +- .exit = qcom_apq8064_sata_phy_exit, +- .owner = THIS_MODULE, +-}; +- +-static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) +-{ +- struct qcom_apq8064_sata_phy *phy; +- struct device *dev = &pdev->dev; +- struct resource *res; +- struct phy_provider *phy_provider; +- struct phy *generic_phy; +- int ret; +- +- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- phy->mmio = devm_ioremap_resource(dev, res); +- if (IS_ERR(phy->mmio)) +- return PTR_ERR(phy->mmio); +- +- generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops); +- if (IS_ERR(generic_phy)) { +- dev_err(dev, "%s: failed to create phy\n", __func__); +- return PTR_ERR(generic_phy); +- } +- +- phy->dev = dev; +- phy_set_drvdata(generic_phy, phy); +- platform_set_drvdata(pdev, phy); +- +- phy->cfg_clk = devm_clk_get(dev, "cfg"); +- if (IS_ERR(phy->cfg_clk)) { +- dev_err(dev, "Failed to get sata cfg clock\n"); +- return PTR_ERR(phy->cfg_clk); +- } +- +- ret = clk_prepare_enable(phy->cfg_clk); +- if (ret) +- return ret; +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) { +- clk_disable_unprepare(phy->cfg_clk); +- dev_err(dev, "%s: failed to register phy\n", __func__); +- return PTR_ERR(phy_provider); +- } +- +- return 0; +-} +- +-static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev) +-{ +- struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev); +- +- clk_disable_unprepare(phy->cfg_clk); +- +- return 0; +-} +- +-static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = { +- { .compatible = "qcom,apq8064-sata-phy" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match); +- +-static struct platform_driver qcom_apq8064_sata_phy_driver = { +- .probe = qcom_apq8064_sata_phy_probe, +- .remove = qcom_apq8064_sata_phy_remove, +- .driver = { +- .name = "qcom-apq8064-sata-phy", +- .of_match_table = qcom_apq8064_sata_phy_of_match, +- } +-}; +-module_platform_driver(qcom_apq8064_sata_phy_driver); +- +-MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c +deleted file mode 100644 +index 0ad127cc9298..000000000000 +--- a/drivers/phy/phy-qcom-ipq806x-sata.c ++++ /dev/null +@@ -1,209 +0,0 @@ +-/* +- * Copyright (c) 2014, The Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-struct qcom_ipq806x_sata_phy { +- void __iomem *mmio; +- struct clk *cfg_clk; +- struct device *dev; +-}; +- +-#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) +- +-#define SATA_PHY_P0_PARAM0 0x200 +-#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) +-#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) +-#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) +-#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) +-#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) +-#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) +- +-#define SATA_PHY_P0_PARAM1 0x204 +-#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) +-#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) +-#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) +-#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) +-#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) +-#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) +-#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) +- +-#define SATA_PHY_P0_PARAM2 0x208 +-#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) +-#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) +- +-#define SATA_PHY_P0_PARAM3 0x20C +-#define SATA_PHY_SSC_EN 0x8 +-#define SATA_PHY_P0_PARAM4 0x210 +-#define SATA_PHY_REF_SSP_EN 0x2 +-#define SATA_PHY_RESET 0x1 +- +-static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) +-{ +- struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); +- u32 reg; +- +- /* Setting SSC_EN to 1 */ +- reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); +- reg = reg | SATA_PHY_SSC_EN; +- writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); +- +- reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & +- ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | +- SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | +- SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); +- reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); +- writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); +- +- reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & +- ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | +- SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | +- SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); +- reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | +- SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | +- SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); +- writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); +- +- reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & +- ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; +- reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); +- writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); +- +- /* Setting PHY_RESET to 1 */ +- reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); +- reg = reg | SATA_PHY_RESET; +- writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); +- +- /* Setting REF_SSP_EN to 1 */ +- reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); +- reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; +- writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); +- +- /* make sure all changes complete before we let the PHY out of reset */ +- mb(); +- +- /* sleep for max. 50us more to combine processor wakeups */ +- usleep_range(20, 20 + 50); +- +- /* Clearing PHY_RESET to 0 */ +- reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); +- reg = reg & ~SATA_PHY_RESET; +- writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); +- +- return 0; +-} +- +-static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) +-{ +- struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); +- u32 reg; +- +- /* Setting PHY_RESET to 1 */ +- reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); +- reg = reg | SATA_PHY_RESET; +- writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); +- +- return 0; +-} +- +-static const struct phy_ops qcom_ipq806x_sata_phy_ops = { +- .init = qcom_ipq806x_sata_phy_init, +- .exit = qcom_ipq806x_sata_phy_exit, +- .owner = THIS_MODULE, +-}; +- +-static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) +-{ +- struct qcom_ipq806x_sata_phy *phy; +- struct device *dev = &pdev->dev; +- struct resource *res; +- struct phy_provider *phy_provider; +- struct phy *generic_phy; +- int ret; +- +- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- phy->mmio = devm_ioremap_resource(dev, res); +- if (IS_ERR(phy->mmio)) +- return PTR_ERR(phy->mmio); +- +- generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops); +- if (IS_ERR(generic_phy)) { +- dev_err(dev, "%s: failed to create phy\n", __func__); +- return PTR_ERR(generic_phy); +- } +- +- phy->dev = dev; +- phy_set_drvdata(generic_phy, phy); +- platform_set_drvdata(pdev, phy); +- +- phy->cfg_clk = devm_clk_get(dev, "cfg"); +- if (IS_ERR(phy->cfg_clk)) { +- dev_err(dev, "Failed to get sata cfg clock\n"); +- return PTR_ERR(phy->cfg_clk); +- } +- +- ret = clk_prepare_enable(phy->cfg_clk); +- if (ret) +- return ret; +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) { +- clk_disable_unprepare(phy->cfg_clk); +- dev_err(dev, "%s: failed to register phy\n", __func__); +- return PTR_ERR(phy_provider); +- } +- +- return 0; +-} +- +-static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) +-{ +- struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); +- +- clk_disable_unprepare(phy->cfg_clk); +- +- return 0; +-} +- +-static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { +- { .compatible = "qcom,ipq806x-sata-phy" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); +- +-static struct platform_driver qcom_ipq806x_sata_phy_driver = { +- .probe = qcom_ipq806x_sata_phy_probe, +- .remove = qcom_ipq806x_sata_phy_remove, +- .driver = { +- .name = "qcom-ipq806x-sata-phy", +- .of_match_table = qcom_ipq806x_sata_phy_of_match, +- } +-}; +-module_platform_driver(qcom_ipq806x_sata_phy_driver); +- +-MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c +deleted file mode 100644 +index 78ca62897784..000000000000 +--- a/drivers/phy/phy-qcom-qmp.c ++++ /dev/null +@@ -1,1153 +0,0 @@ +-/* +- * Copyright (c) 2017, The Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-/* QMP PHY QSERDES COM registers */ +-#define QSERDES_COM_BG_TIMER 0x00c +-#define QSERDES_COM_SSC_EN_CENTER 0x010 +-#define QSERDES_COM_SSC_ADJ_PER1 0x014 +-#define QSERDES_COM_SSC_ADJ_PER2 0x018 +-#define QSERDES_COM_SSC_PER1 0x01c +-#define QSERDES_COM_SSC_PER2 0x020 +-#define QSERDES_COM_SSC_STEP_SIZE1 0x024 +-#define QSERDES_COM_SSC_STEP_SIZE2 0x028 +-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034 +-#define QSERDES_COM_CLK_ENABLE1 0x038 +-#define QSERDES_COM_SYS_CLK_CTRL 0x03c +-#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040 +-#define QSERDES_COM_PLL_IVCO 0x048 +-#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c +-#define QSERDES_COM_LOCK_CMP2_MODE0 0x050 +-#define QSERDES_COM_LOCK_CMP3_MODE0 0x054 +-#define QSERDES_COM_LOCK_CMP1_MODE1 0x058 +-#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c +-#define QSERDES_COM_LOCK_CMP3_MODE1 0x060 +-#define QSERDES_COM_BG_TRIM 0x070 +-#define QSERDES_COM_CLK_EP_DIV 0x074 +-#define QSERDES_COM_CP_CTRL_MODE0 0x078 +-#define QSERDES_COM_CP_CTRL_MODE1 0x07c +-#define QSERDES_COM_PLL_RCTRL_MODE0 0x084 +-#define QSERDES_COM_PLL_RCTRL_MODE1 0x088 +-#define QSERDES_COM_PLL_CCTRL_MODE0 0x090 +-#define QSERDES_COM_PLL_CCTRL_MODE1 0x094 +-#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac +-#define QSERDES_COM_RESETSM_CNTRL 0x0b4 +-#define QSERDES_COM_RESTRIM_CTRL 0x0bc +-#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 +-#define QSERDES_COM_LOCK_CMP_EN 0x0c8 +-#define QSERDES_COM_LOCK_CMP_CFG 0x0cc +-#define QSERDES_COM_DEC_START_MODE0 0x0d0 +-#define QSERDES_COM_DEC_START_MODE1 0x0d4 +-#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc +-#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0 +-#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4 +-#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 +-#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec +-#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 +-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 +-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c +-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 +-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114 +-#define QSERDES_COM_VCO_TUNE_CTRL 0x124 +-#define QSERDES_COM_VCO_TUNE_MAP 0x128 +-#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c +-#define QSERDES_COM_VCO_TUNE2_MODE0 0x130 +-#define QSERDES_COM_VCO_TUNE1_MODE1 0x134 +-#define QSERDES_COM_VCO_TUNE2_MODE1 0x138 +-#define QSERDES_COM_VCO_TUNE_TIMER1 0x144 +-#define QSERDES_COM_VCO_TUNE_TIMER2 0x148 +-#define QSERDES_COM_BG_CTRL 0x170 +-#define QSERDES_COM_CLK_SELECT 0x174 +-#define QSERDES_COM_HSCLK_SEL 0x178 +-#define QSERDES_COM_CORECLK_DIV 0x184 +-#define QSERDES_COM_CORE_CLK_EN 0x18c +-#define QSERDES_COM_C_READY_STATUS 0x190 +-#define QSERDES_COM_CMN_CONFIG 0x194 +-#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c +-#define QSERDES_COM_DEBUG_BUS0 0x1a0 +-#define QSERDES_COM_DEBUG_BUS1 0x1a4 +-#define QSERDES_COM_DEBUG_BUS2 0x1a8 +-#define QSERDES_COM_DEBUG_BUS3 0x1ac +-#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0 +-#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc +- +-/* QMP PHY TX registers */ +-#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054 +-#define QSERDES_TX_DEBUG_BUS_SEL 0x064 +-#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068 +-#define QSERDES_TX_LANE_MODE 0x094 +-#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac +- +-/* QMP PHY RX registers */ +-#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010 +-#define QSERDES_RX_UCDR_SO_GAIN 0x01c +-#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040 +-#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048 +-#define QSERDES_RX_RX_TERM_BW 0x090 +-#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4 +-#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8 +-#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc +-#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0 +-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8 +-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc +-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0 +-#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108 +-#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c +-#define QSERDES_RX_SIGDET_ENABLES 0x110 +-#define QSERDES_RX_SIGDET_CNTRL 0x114 +-#define QSERDES_RX_SIGDET_LVL 0x118 +-#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c +-#define QSERDES_RX_RX_BAND 0x120 +-#define QSERDES_RX_RX_INTERFACE_MODE 0x12c +- +-/* QMP PHY PCS registers */ +-#define QPHY_POWER_DOWN_CONTROL 0x04 +-#define QPHY_TXDEEMPH_M6DB_V0 0x24 +-#define QPHY_TXDEEMPH_M3P5DB_V0 0x28 +-#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54 +-#define QPHY_RX_IDLE_DTCT_CNTRL 0x58 +-#define QPHY_POWER_STATE_CONFIG1 0x60 +-#define QPHY_POWER_STATE_CONFIG2 0x64 +-#define QPHY_POWER_STATE_CONFIG4 0x6c +-#define QPHY_LOCK_DETECT_CONFIG1 0x80 +-#define QPHY_LOCK_DETECT_CONFIG2 0x84 +-#define QPHY_LOCK_DETECT_CONFIG3 0x88 +-#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0 +-#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4 +- +-/* QPHY_SW_RESET bit */ +-#define SW_RESET BIT(0) +-/* QPHY_POWER_DOWN_CONTROL */ +-#define SW_PWRDN BIT(0) +-#define REFCLK_DRV_DSBL BIT(1) +-/* QPHY_START_CONTROL bits */ +-#define SERDES_START BIT(0) +-#define PCS_START BIT(1) +-#define PLL_READY_GATE_EN BIT(3) +-/* QPHY_PCS_STATUS bit */ +-#define PHYSTATUS BIT(6) +-/* QPHY_COM_PCS_READY_STATUS bit */ +-#define PCS_READY BIT(0) +- +-#define PHY_INIT_COMPLETE_TIMEOUT 1000 +-#define POWER_DOWN_DELAY_US_MIN 10 +-#define POWER_DOWN_DELAY_US_MAX 11 +- +-#define MAX_PROP_NAME 32 +- +-struct qmp_phy_init_tbl { +- unsigned int offset; +- unsigned int val; +- /* +- * register part of layout ? +- * if yes, then offset gives index in the reg-layout +- */ +- int in_layout; +-}; +- +-#define QMP_PHY_INIT_CFG(o, v) \ +- { \ +- .offset = o, \ +- .val = v, \ +- } +- +-#define QMP_PHY_INIT_CFG_L(o, v) \ +- { \ +- .offset = o, \ +- .val = v, \ +- .in_layout = 1, \ +- } +- +-/* set of registers with offsets different per-PHY */ +-enum qphy_reg_layout { +- /* Common block control registers */ +- QPHY_COM_SW_RESET, +- QPHY_COM_POWER_DOWN_CONTROL, +- QPHY_COM_START_CONTROL, +- QPHY_COM_PCS_READY_STATUS, +- /* PCS registers */ +- QPHY_PLL_LOCK_CHK_DLY_TIME, +- QPHY_FLL_CNTRL1, +- QPHY_FLL_CNTRL2, +- QPHY_FLL_CNT_VAL_L, +- QPHY_FLL_CNT_VAL_H_TOL, +- QPHY_FLL_MAN_CODE, +- QPHY_SW_RESET, +- QPHY_START_CTRL, +- QPHY_PCS_READY_STATUS, +-}; +- +-static const unsigned int pciephy_regs_layout[] = { +- [QPHY_COM_SW_RESET] = 0x400, +- [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, +- [QPHY_COM_START_CONTROL] = 0x408, +- [QPHY_COM_PCS_READY_STATUS] = 0x448, +- [QPHY_PLL_LOCK_CHK_DLY_TIME] = 0xa8, +- [QPHY_FLL_CNTRL1] = 0xc4, +- [QPHY_FLL_CNTRL2] = 0xc8, +- [QPHY_FLL_CNT_VAL_L] = 0xcc, +- [QPHY_FLL_CNT_VAL_H_TOL] = 0xd0, +- [QPHY_FLL_MAN_CODE] = 0xd4, +- [QPHY_SW_RESET] = 0x00, +- [QPHY_START_CTRL] = 0x08, +- [QPHY_PCS_READY_STATUS] = 0x174, +-}; +- +-static const unsigned int usb3phy_regs_layout[] = { +- [QPHY_FLL_CNTRL1] = 0xc0, +- [QPHY_FLL_CNTRL2] = 0xc4, +- [QPHY_FLL_CNT_VAL_L] = 0xc8, +- [QPHY_FLL_CNT_VAL_H_TOL] = 0xcc, +- [QPHY_FLL_MAN_CODE] = 0xd0, +- [QPHY_SW_RESET] = 0x00, +- [QPHY_START_CTRL] = 0x08, +- [QPHY_PCS_READY_STATUS] = 0x17c, +-}; +- +-static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { +- QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), +- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), +- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), +- QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), +- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42), +- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), +- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), +- QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01), +- QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), +- QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), +- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09), +- QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), +- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), +- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), +- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), +- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a), +- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a), +- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), +- QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), +- QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), +- QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04), +- QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), +- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), +- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), +- QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), +- QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15), +- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), +- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), +- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), +- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), +- QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40), +-}; +- +-static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { +- QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), +- QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +-}; +- +-static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = { +- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18), +- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), +- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04), +- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), +- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), +- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19), +-}; +- +-static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = { +- QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c), +- QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00), +- QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), +- +- QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05), +- +- QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05), +- QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02), +- QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00), +- QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3), +- QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e), +-}; +- +-static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = { +- QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), +- QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), +- QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), +- QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), +- QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), +- QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), +- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), +- QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04), +- /* PLL and Loop filter settings */ +- QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), +- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), +- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), +- QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), +- QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), +- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), +- QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), +- QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), +- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), +- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), +- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), +- /* SSC settings */ +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), +- QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), +-}; +- +-static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = { +- QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), +- QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), +- QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +-}; +- +-static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = { +- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), +- QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), +- QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), +- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), +- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18), +- QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), +-}; +- +-static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { +- /* FLL settings */ +- QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03), +- QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02), +- QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09), +- QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42), +- QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85), +- +- /* Lock Det settings */ +- QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1), +- QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f), +- QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47), +- QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08), +-}; +- +-/* struct qmp_phy_cfg - per-PHY initialization config */ +-struct qmp_phy_cfg { +- /* phy-type - PCIE/UFS/USB */ +- unsigned int type; +- /* number of lanes provided by phy */ +- int nlanes; +- +- /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ +- const struct qmp_phy_init_tbl *serdes_tbl; +- int serdes_tbl_num; +- const struct qmp_phy_init_tbl *tx_tbl; +- int tx_tbl_num; +- const struct qmp_phy_init_tbl *rx_tbl; +- int rx_tbl_num; +- const struct qmp_phy_init_tbl *pcs_tbl; +- int pcs_tbl_num; +- +- /* clock ids to be requested */ +- const char * const *clk_list; +- int num_clks; +- /* resets to be requested */ +- const char * const *reset_list; +- int num_resets; +- /* regulators to be requested */ +- const char * const *vreg_list; +- int num_vregs; +- +- /* array of registers with different offsets */ +- const unsigned int *regs; +- +- unsigned int start_ctrl; +- unsigned int pwrdn_ctrl; +- unsigned int mask_pcs_ready; +- unsigned int mask_com_pcs_ready; +- +- /* true, if PHY has a separate PHY_COM control block */ +- bool has_phy_com_ctrl; +- /* true, if PHY has a reset for individual lanes */ +- bool has_lane_rst; +- /* true, if PHY needs delay after POWER_DOWN */ +- bool has_pwrdn_delay; +- /* power_down delay in usec */ +- int pwrdn_delay_min; +- int pwrdn_delay_max; +-}; +- +-/** +- * struct qmp_phy - per-lane phy descriptor +- * +- * @phy: generic phy +- * @tx: iomapped memory space for lane's tx +- * @rx: iomapped memory space for lane's rx +- * @pcs: iomapped memory space for lane's pcs +- * @pipe_clk: pipe lock +- * @index: lane index +- * @qmp: QMP phy to which this lane belongs +- * @lane_rst: lane's reset controller +- */ +-struct qmp_phy { +- struct phy *phy; +- void __iomem *tx; +- void __iomem *rx; +- void __iomem *pcs; +- struct clk *pipe_clk; +- unsigned int index; +- struct qcom_qmp *qmp; +- struct reset_control *lane_rst; +-}; +- +-/** +- * struct qcom_qmp - structure holding QMP phy block attributes +- * +- * @dev: device +- * @serdes: iomapped memory space for phy's serdes +- * +- * @clks: array of clocks required by phy +- * @resets: array of resets required by phy +- * @vregs: regulator supplies bulk data +- * +- * @cfg: phy specific configuration +- * @phys: array of per-lane phy descriptors +- * @phy_mutex: mutex lock for PHY common block initialization +- * @init_count: phy common block initialization count +- */ +-struct qcom_qmp { +- struct device *dev; +- void __iomem *serdes; +- +- struct clk **clks; +- struct reset_control **resets; +- struct regulator_bulk_data *vregs; +- +- const struct qmp_phy_cfg *cfg; +- struct qmp_phy **phys; +- +- struct mutex phy_mutex; +- int init_count; +-}; +- +-static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +-{ +- u32 reg; +- +- reg = readl(base + offset); +- reg |= val; +- writel(reg, base + offset); +- +- /* ensure that above write is through */ +- readl(base + offset); +-} +- +-static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +-{ +- u32 reg; +- +- reg = readl(base + offset); +- reg &= ~val; +- writel(reg, base + offset); +- +- /* ensure that above write is through */ +- readl(base + offset); +-} +- +-/* list of clocks required by phy */ +-static const char * const msm8996_phy_clk_l[] = { +- "aux", "cfg_ahb", "ref", +-}; +- +-/* list of resets */ +-static const char * const msm8996_pciephy_reset_l[] = { +- "phy", "common", "cfg", +-}; +- +-static const char * const msm8996_usb3phy_reset_l[] = { +- "phy", "common", +-}; +- +-/* list of regulators */ +-static const char * const msm8996_phy_vreg_l[] = { +- "vdda-phy", "vdda-pll", +-}; +- +-static const struct qmp_phy_cfg msm8996_pciephy_cfg = { +- .type = PHY_TYPE_PCIE, +- .nlanes = 3, +- +- .serdes_tbl = msm8996_pcie_serdes_tbl, +- .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl), +- .tx_tbl = msm8996_pcie_tx_tbl, +- .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl), +- .rx_tbl = msm8996_pcie_rx_tbl, +- .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl), +- .pcs_tbl = msm8996_pcie_pcs_tbl, +- .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl), +- .clk_list = msm8996_phy_clk_l, +- .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), +- .reset_list = msm8996_pciephy_reset_l, +- .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l), +- .vreg_list = msm8996_phy_vreg_l, +- .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), +- .regs = pciephy_regs_layout, +- +- .start_ctrl = PCS_START | PLL_READY_GATE_EN, +- .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, +- .mask_com_pcs_ready = PCS_READY, +- +- .has_phy_com_ctrl = true, +- .has_lane_rst = true, +- .has_pwrdn_delay = true, +- .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, +- .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +-}; +- +-static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { +- .type = PHY_TYPE_USB3, +- .nlanes = 1, +- +- .serdes_tbl = msm8996_usb3_serdes_tbl, +- .serdes_tbl_num = ARRAY_SIZE(msm8996_usb3_serdes_tbl), +- .tx_tbl = msm8996_usb3_tx_tbl, +- .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), +- .rx_tbl = msm8996_usb3_rx_tbl, +- .rx_tbl_num = ARRAY_SIZE(msm8996_usb3_rx_tbl), +- .pcs_tbl = msm8996_usb3_pcs_tbl, +- .pcs_tbl_num = ARRAY_SIZE(msm8996_usb3_pcs_tbl), +- .clk_list = msm8996_phy_clk_l, +- .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), +- .reset_list = msm8996_usb3phy_reset_l, +- .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), +- .vreg_list = msm8996_phy_vreg_l, +- .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), +- .regs = usb3phy_regs_layout, +- +- .start_ctrl = SERDES_START | PCS_START, +- .pwrdn_ctrl = SW_PWRDN, +- .mask_pcs_ready = PHYSTATUS, +-}; +- +-static void qcom_qmp_phy_configure(void __iomem *base, +- const unsigned int *regs, +- const struct qmp_phy_init_tbl tbl[], +- int num) +-{ +- int i; +- const struct qmp_phy_init_tbl *t = tbl; +- +- if (!t) +- return; +- +- for (i = 0; i < num; i++, t++) { +- if (t->in_layout) +- writel(t->val, base + regs[t->offset]); +- else +- writel(t->val, base + t->offset); +- } +-} +- +-static int qcom_qmp_phy_poweron(struct phy *phy) +-{ +- struct qmp_phy *qphy = phy_get_drvdata(phy); +- struct qcom_qmp *qmp = qphy->qmp; +- int num = qmp->cfg->num_vregs; +- int ret; +- +- dev_vdbg(&phy->dev, "Powering on QMP phy\n"); +- +- /* turn on regulator supplies */ +- ret = regulator_bulk_enable(num, qmp->vregs); +- if (ret) { +- dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); +- return ret; +- } +- +- ret = clk_prepare_enable(qphy->pipe_clk); +- if (ret) { +- dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret); +- regulator_bulk_disable(num, qmp->vregs); +- return ret; +- } +- +- return 0; +-} +- +-static int qcom_qmp_phy_poweroff(struct phy *phy) +-{ +- struct qmp_phy *qphy = phy_get_drvdata(phy); +- struct qcom_qmp *qmp = qphy->qmp; +- +- clk_disable_unprepare(qphy->pipe_clk); +- +- regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs); +- +- return 0; +-} +- +-static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp) +-{ +- const struct qmp_phy_cfg *cfg = qmp->cfg; +- void __iomem *serdes = qmp->serdes; +- int ret, i; +- +- mutex_lock(&qmp->phy_mutex); +- if (qmp->init_count++) { +- mutex_unlock(&qmp->phy_mutex); +- return 0; +- } +- +- for (i = 0; i < cfg->num_resets; i++) { +- ret = reset_control_deassert(qmp->resets[i]); +- if (ret) { +- dev_err(qmp->dev, "%s reset deassert failed\n", +- qmp->cfg->reset_list[i]); +- while (--i >= 0) +- reset_control_assert(qmp->resets[i]); +- goto err_rst; +- } +- } +- +- if (cfg->has_phy_com_ctrl) +- qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], +- SW_PWRDN); +- +- /* Serdes configuration */ +- qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl, +- cfg->serdes_tbl_num); +- +- if (cfg->has_phy_com_ctrl) { +- void __iomem *status; +- unsigned int mask, val; +- +- qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); +- qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], +- SERDES_START | PCS_START); +- +- status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS]; +- mask = cfg->mask_com_pcs_ready; +- +- ret = readl_poll_timeout(status, val, (val & mask), 10, +- PHY_INIT_COMPLETE_TIMEOUT); +- if (ret) { +- dev_err(qmp->dev, +- "phy common block init timed-out\n"); +- goto err_com_init; +- } +- } +- +- mutex_unlock(&qmp->phy_mutex); +- +- return 0; +- +-err_com_init: +- while (--i >= 0) +- reset_control_assert(qmp->resets[i]); +-err_rst: +- mutex_unlock(&qmp->phy_mutex); +- return ret; +-} +- +-static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp) +-{ +- const struct qmp_phy_cfg *cfg = qmp->cfg; +- void __iomem *serdes = qmp->serdes; +- int i = cfg->num_resets; +- +- mutex_lock(&qmp->phy_mutex); +- if (--qmp->init_count) { +- mutex_unlock(&qmp->phy_mutex); +- return 0; +- } +- +- if (cfg->has_phy_com_ctrl) { +- qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], +- SERDES_START | PCS_START); +- qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], +- SW_RESET); +- qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], +- SW_PWRDN); +- } +- +- while (--i >= 0) +- reset_control_assert(qmp->resets[i]); +- +- mutex_unlock(&qmp->phy_mutex); +- +- return 0; +-} +- +-/* PHY Initialization */ +-static int qcom_qmp_phy_init(struct phy *phy) +-{ +- struct qmp_phy *qphy = phy_get_drvdata(phy); +- struct qcom_qmp *qmp = qphy->qmp; +- const struct qmp_phy_cfg *cfg = qmp->cfg; +- void __iomem *tx = qphy->tx; +- void __iomem *rx = qphy->rx; +- void __iomem *pcs = qphy->pcs; +- void __iomem *status; +- unsigned int mask, val; +- int ret, i; +- +- dev_vdbg(qmp->dev, "Initializing QMP phy\n"); +- +- for (i = 0; i < qmp->cfg->num_clks; i++) { +- ret = clk_prepare_enable(qmp->clks[i]); +- if (ret) { +- dev_err(qmp->dev, "failed to enable %s clk, err=%d\n", +- qmp->cfg->clk_list[i], ret); +- while (--i >= 0) +- clk_disable_unprepare(qmp->clks[i]); +- } +- } +- +- ret = qcom_qmp_phy_com_init(qmp); +- if (ret) +- goto err_com_init; +- +- if (cfg->has_lane_rst) { +- ret = reset_control_deassert(qphy->lane_rst); +- if (ret) { +- dev_err(qmp->dev, "lane%d reset deassert failed\n", +- qphy->index); +- goto err_lane_rst; +- } +- } +- +- /* Tx, Rx, and PCS configurations */ +- qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); +- qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); +- qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); +- +- /* +- * Pull out PHY from POWER DOWN state. +- * This is active low enable signal to power-down PHY. +- */ +- qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); +- +- if (cfg->has_pwrdn_delay) +- usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); +- +- /* start SerDes and Phy-Coding-Sublayer */ +- qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); +- +- /* Pull PHY out of reset state */ +- qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); +- +- status = pcs + cfg->regs[QPHY_PCS_READY_STATUS]; +- mask = cfg->mask_pcs_ready; +- +- ret = readl_poll_timeout(status, val, !(val & mask), 1, +- PHY_INIT_COMPLETE_TIMEOUT); +- if (ret) { +- dev_err(qmp->dev, "phy initialization timed-out\n"); +- goto err_pcs_ready; +- } +- +- return ret; +- +-err_pcs_ready: +- if (cfg->has_lane_rst) +- reset_control_assert(qphy->lane_rst); +-err_lane_rst: +- qcom_qmp_phy_com_exit(qmp); +-err_com_init: +- while (--i >= 0) +- clk_disable_unprepare(qmp->clks[i]); +- +- return ret; +-} +- +-static int qcom_qmp_phy_exit(struct phy *phy) +-{ +- struct qmp_phy *qphy = phy_get_drvdata(phy); +- struct qcom_qmp *qmp = qphy->qmp; +- const struct qmp_phy_cfg *cfg = qmp->cfg; +- int i = cfg->num_clks; +- +- /* PHY reset */ +- qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); +- +- /* stop SerDes and Phy-Coding-Sublayer */ +- qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); +- +- /* Put PHY into POWER DOWN state: active low */ +- qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); +- +- if (cfg->has_lane_rst) +- reset_control_assert(qphy->lane_rst); +- +- qcom_qmp_phy_com_exit(qmp); +- +- while (--i >= 0) +- clk_disable_unprepare(qmp->clks[i]); +- +- return 0; +-} +- +-static int qcom_qmp_phy_vreg_init(struct device *dev) +-{ +- struct qcom_qmp *qmp = dev_get_drvdata(dev); +- int num = qmp->cfg->num_vregs; +- int i; +- +- qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); +- if (!qmp->vregs) +- return -ENOMEM; +- +- for (i = 0; i < num; i++) +- qmp->vregs[i].supply = qmp->cfg->vreg_list[i]; +- +- return devm_regulator_bulk_get(dev, num, qmp->vregs); +-} +- +-static int qcom_qmp_phy_reset_init(struct device *dev) +-{ +- struct qcom_qmp *qmp = dev_get_drvdata(dev); +- int i; +- +- qmp->resets = devm_kcalloc(dev, qmp->cfg->num_resets, +- sizeof(*qmp->resets), GFP_KERNEL); +- if (!qmp->resets) +- return -ENOMEM; +- +- for (i = 0; i < qmp->cfg->num_resets; i++) { +- struct reset_control *rst; +- const char *name = qmp->cfg->reset_list[i]; +- +- rst = devm_reset_control_get(dev, name); +- if (IS_ERR(rst)) { +- dev_err(dev, "failed to get %s reset\n", name); +- return PTR_ERR(rst); +- } +- qmp->resets[i] = rst; +- } +- +- return 0; +-} +- +-static int qcom_qmp_phy_clk_init(struct device *dev) +-{ +- struct qcom_qmp *qmp = dev_get_drvdata(dev); +- int ret, i; +- +- qmp->clks = devm_kcalloc(dev, qmp->cfg->num_clks, +- sizeof(*qmp->clks), GFP_KERNEL); +- if (!qmp->clks) +- return -ENOMEM; +- +- for (i = 0; i < qmp->cfg->num_clks; i++) { +- struct clk *_clk; +- const char *name = qmp->cfg->clk_list[i]; +- +- _clk = devm_clk_get(dev, name); +- if (IS_ERR(_clk)) { +- ret = PTR_ERR(_clk); +- if (ret != -EPROBE_DEFER) +- dev_err(dev, "failed to get %s clk, %d\n", +- name, ret); +- return ret; +- } +- qmp->clks[i] = _clk; +- } +- +- return 0; +-} +- +-/* +- * Register a fixed rate pipe clock. +- * +- * The _pipe_clksrc generated by PHY goes to the GCC that gate +- * controls it. The _pipe_clk coming out of the GCC is requested +- * by the PHY driver for its operations. +- * We register the _pipe_clksrc here. The gcc driver takes care +- * of assigning this _pipe_clksrc as parent to _pipe_clk. +- * Below picture shows this relationship. +- * +- * +---------------+ +- * | PHY block |<<---------------------------------------+ +- * | | | +- * | +-------+ | +-----+ | +- * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ +- * clk | +-------+ | +-----+ +- * +---------------+ +- */ +-static int phy_pipe_clk_register(struct qcom_qmp *qmp, int id) +-{ +- char name[24]; +- struct clk_fixed_rate *fixed; +- struct clk_init_data init = { }; +- +- switch (qmp->cfg->type) { +- case PHY_TYPE_USB3: +- snprintf(name, sizeof(name), "usb3_phy_pipe_clk_src"); +- break; +- case PHY_TYPE_PCIE: +- snprintf(name, sizeof(name), "pcie_%d_pipe_clk_src", id); +- break; +- default: +- /* not all phys register pipe clocks, so return success */ +- return 0; +- } +- +- fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); +- if (!fixed) +- return -ENOMEM; +- +- init.name = name; +- init.ops = &clk_fixed_rate_ops; +- +- /* controllers using QMP phys use 125MHz pipe clock interface */ +- fixed->fixed_rate = 125000000; +- fixed->hw.init = &init; +- +- return devm_clk_hw_register(qmp->dev, &fixed->hw); +-} +- +-static const struct phy_ops qcom_qmp_phy_gen_ops = { +- .init = qcom_qmp_phy_init, +- .exit = qcom_qmp_phy_exit, +- .power_on = qcom_qmp_phy_poweron, +- .power_off = qcom_qmp_phy_poweroff, +- .owner = THIS_MODULE, +-}; +- +-static +-int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) +-{ +- struct qcom_qmp *qmp = dev_get_drvdata(dev); +- struct phy *generic_phy; +- struct qmp_phy *qphy; +- char prop_name[MAX_PROP_NAME]; +- int ret; +- +- qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); +- if (!qphy) +- return -ENOMEM; +- +- /* +- * Get memory resources for each phy lane: +- * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. +- */ +- qphy->tx = of_iomap(np, 0); +- if (!qphy->tx) +- return -ENOMEM; +- +- qphy->rx = of_iomap(np, 1); +- if (!qphy->rx) +- return -ENOMEM; +- +- qphy->pcs = of_iomap(np, 2); +- if (!qphy->pcs) +- return -ENOMEM; +- +- /* +- * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3 +- * based phys, so they essentially have pipe clock. So, +- * we return error in case phy is USB3 or PIPE type. +- * Otherwise, we initialize pipe clock to NULL for +- * all phys that don't need this. +- */ +- snprintf(prop_name, sizeof(prop_name), "pipe%d", id); +- qphy->pipe_clk = of_clk_get_by_name(np, prop_name); +- if (IS_ERR(qphy->pipe_clk)) { +- if (qmp->cfg->type == PHY_TYPE_PCIE || +- qmp->cfg->type == PHY_TYPE_USB3) { +- ret = PTR_ERR(qphy->pipe_clk); +- if (ret != -EPROBE_DEFER) +- dev_err(dev, +- "failed to get lane%d pipe_clk, %d\n", +- id, ret); +- return ret; +- } +- qphy->pipe_clk = NULL; +- } +- +- /* Get lane reset, if any */ +- if (qmp->cfg->has_lane_rst) { +- snprintf(prop_name, sizeof(prop_name), "lane%d", id); +- qphy->lane_rst = of_reset_control_get(np, prop_name); +- if (IS_ERR(qphy->lane_rst)) { +- dev_err(dev, "failed to get lane%d reset\n", id); +- return PTR_ERR(qphy->lane_rst); +- } +- } +- +- generic_phy = devm_phy_create(dev, np, &qcom_qmp_phy_gen_ops); +- if (IS_ERR(generic_phy)) { +- ret = PTR_ERR(generic_phy); +- dev_err(dev, "failed to create qphy %d\n", ret); +- return ret; +- } +- +- qphy->phy = generic_phy; +- qphy->index = id; +- qphy->qmp = qmp; +- qmp->phys[id] = qphy; +- phy_set_drvdata(generic_phy, qphy); +- +- return 0; +-} +- +-static const struct of_device_id qcom_qmp_phy_of_match_table[] = { +- { +- .compatible = "qcom,msm8996-qmp-pcie-phy", +- .data = &msm8996_pciephy_cfg, +- }, { +- .compatible = "qcom,msm8996-qmp-usb3-phy", +- .data = &msm8996_usb3phy_cfg, +- }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table); +- +-static int qcom_qmp_phy_probe(struct platform_device *pdev) +-{ +- struct qcom_qmp *qmp; +- struct device *dev = &pdev->dev; +- struct resource *res; +- struct device_node *child; +- struct phy_provider *phy_provider; +- void __iomem *base; +- int num, id; +- int ret; +- +- qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); +- if (!qmp) +- return -ENOMEM; +- +- qmp->dev = dev; +- dev_set_drvdata(dev, qmp); +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(dev, res); +- if (IS_ERR(base)) +- return PTR_ERR(base); +- +- /* per PHY serdes; usually located at base address */ +- qmp->serdes = base; +- +- mutex_init(&qmp->phy_mutex); +- +- /* Get the specific init parameters of QMP phy */ +- qmp->cfg = of_device_get_match_data(dev); +- +- ret = qcom_qmp_phy_clk_init(dev); +- if (ret) +- return ret; +- +- ret = qcom_qmp_phy_reset_init(dev); +- if (ret) +- return ret; +- +- ret = qcom_qmp_phy_vreg_init(dev); +- if (ret) { +- dev_err(dev, "failed to get regulator supplies\n"); +- return ret; +- } +- +- num = of_get_available_child_count(dev->of_node); +- /* do we have a rogue child node ? */ +- if (num > qmp->cfg->nlanes) +- return -EINVAL; +- +- qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); +- if (!qmp->phys) +- return -ENOMEM; +- +- id = 0; +- for_each_available_child_of_node(dev->of_node, child) { +- /* Create per-lane phy */ +- ret = qcom_qmp_phy_create(dev, child, id); +- if (ret) { +- dev_err(dev, "failed to create lane%d phy, %d\n", +- id, ret); +- return ret; +- } +- +- /* +- * Register the pipe clock provided by phy. +- * See function description to see details of this pipe clock. +- */ +- ret = phy_pipe_clk_register(qmp, id); +- if (ret) { +- dev_err(qmp->dev, +- "failed to register pipe clock source\n"); +- return ret; +- } +- id++; +- } +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (!IS_ERR(phy_provider)) +- dev_info(dev, "Registered Qcom-QMP phy\n"); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static struct platform_driver qcom_qmp_phy_driver = { +- .probe = qcom_qmp_phy_probe, +- .driver = { +- .name = "qcom-qmp-phy", +- .of_match_table = qcom_qmp_phy_of_match_table, +- }, +-}; +- +-module_platform_driver(qcom_qmp_phy_driver); +- +-MODULE_AUTHOR("Vivek Gautam "); +-MODULE_DESCRIPTION("Qualcomm QMP PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-qcom-qusb2.c b/drivers/phy/phy-qcom-qusb2.c +deleted file mode 100644 +index 6c575244c0fb..000000000000 +--- a/drivers/phy/phy-qcom-qusb2.c ++++ /dev/null +@@ -1,493 +0,0 @@ +-/* +- * Copyright (c) 2017, The Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define QUSB2PHY_PLL_TEST 0x04 +-#define CLK_REF_SEL BIT(7) +- +-#define QUSB2PHY_PLL_TUNE 0x08 +-#define QUSB2PHY_PLL_USER_CTL1 0x0c +-#define QUSB2PHY_PLL_USER_CTL2 0x10 +-#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c +-#define QUSB2PHY_PLL_PWR_CTRL 0x18 +- +-#define QUSB2PHY_PLL_STATUS 0x38 +-#define PLL_LOCKED BIT(5) +- +-#define QUSB2PHY_PORT_TUNE1 0x80 +-#define QUSB2PHY_PORT_TUNE2 0x84 +-#define QUSB2PHY_PORT_TUNE3 0x88 +-#define QUSB2PHY_PORT_TUNE4 0x8c +-#define QUSB2PHY_PORT_TUNE5 0x90 +-#define QUSB2PHY_PORT_TEST2 0x9c +- +-#define QUSB2PHY_PORT_POWERDOWN 0xb4 +-#define CLAMP_N_EN BIT(5) +-#define FREEZIO_N BIT(1) +-#define POWER_DOWN BIT(0) +- +-#define QUSB2PHY_REFCLK_ENABLE BIT(0) +- +-#define PHY_CLK_SCHEME_SEL BIT(0) +- +-struct qusb2_phy_init_tbl { +- unsigned int offset; +- unsigned int val; +-}; +- +-#define QUSB2_PHY_INIT_CFG(o, v) \ +- { \ +- .offset = o, \ +- .val = v, \ +- } +- +-static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), +- QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), +-}; +- +-struct qusb2_phy_cfg { +- const struct qusb2_phy_init_tbl *tbl; +- /* number of entries in the table */ +- unsigned int tbl_num; +- /* offset to PHY_CLK_SCHEME register in TCSR map */ +- unsigned int clk_scheme_offset; +-}; +- +-static const struct qusb2_phy_cfg msm8996_phy_cfg = { +- .tbl = msm8996_init_tbl, +- .tbl_num = ARRAY_SIZE(msm8996_init_tbl), +-}; +- +-static const char * const qusb2_phy_vreg_names[] = { +- "vdda-pll", "vdda-phy-dpdm", +-}; +- +-#define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) +- +-/** +- * struct qusb2_phy - structure holding qusb2 phy attributes +- * +- * @phy: generic phy +- * @base: iomapped memory space for qubs2 phy +- * +- * @cfg_ahb_clk: AHB2PHY interface clock +- * @ref_clk: phy reference clock +- * @iface_clk: phy interface clock +- * @phy_reset: phy reset control +- * @vregs: regulator supplies bulk data +- * +- * @tcsr: TCSR syscon register map +- * @cell: nvmem cell containing phy tuning value +- * +- * @cfg: phy config data +- * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme +- */ +-struct qusb2_phy { +- struct phy *phy; +- void __iomem *base; +- +- struct clk *cfg_ahb_clk; +- struct clk *ref_clk; +- struct clk *iface_clk; +- struct reset_control *phy_reset; +- struct regulator_bulk_data vregs[QUSB2_NUM_VREGS]; +- +- struct regmap *tcsr; +- struct nvmem_cell *cell; +- +- const struct qusb2_phy_cfg *cfg; +- bool has_se_clk_scheme; +-}; +- +-static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val) +-{ +- u32 reg; +- +- reg = readl(base + offset); +- reg |= val; +- writel(reg, base + offset); +- +- /* Ensure above write is completed */ +- readl(base + offset); +-} +- +-static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val) +-{ +- u32 reg; +- +- reg = readl(base + offset); +- reg &= ~val; +- writel(reg, base + offset); +- +- /* Ensure above write is completed */ +- readl(base + offset); +-} +- +-static inline +-void qcom_qusb2_phy_configure(void __iomem *base, +- const struct qusb2_phy_init_tbl tbl[], int num) +-{ +- int i; +- +- for (i = 0; i < num; i++) +- writel(tbl[i].val, base + tbl[i].offset); +-} +- +-/* +- * Fetches HS Tx tuning value from nvmem and sets the +- * QUSB2PHY_PORT_TUNE2 register. +- * For error case, skip setting the value and use the default value. +- */ +-static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) +-{ +- struct device *dev = &qphy->phy->dev; +- u8 *val; +- +- /* +- * Read efuse register having TUNE2 parameter's high nibble. +- * If efuse register shows value as 0x0, or if we fail to find +- * a valid efuse register settings, then use default value +- * as 0xB for high nibble that we have already set while +- * configuring phy. +- */ +- val = nvmem_cell_read(qphy->cell, NULL); +- if (IS_ERR(val) || !val[0]) { +- dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); +- return; +- } +- +- /* Fused TUNE2 value is the higher nibble only */ +- qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4); +-} +- +-static int qusb2_phy_poweron(struct phy *phy) +-{ +- struct qusb2_phy *qphy = phy_get_drvdata(phy); +- int num = ARRAY_SIZE(qphy->vregs); +- int ret; +- +- dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__); +- +- /* turn on regulator supplies */ +- ret = regulator_bulk_enable(num, qphy->vregs); +- if (ret) +- return ret; +- +- ret = clk_prepare_enable(qphy->iface_clk); +- if (ret) { +- dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret); +- regulator_bulk_disable(num, qphy->vregs); +- return ret; +- } +- +- return 0; +-} +- +-static int qusb2_phy_poweroff(struct phy *phy) +-{ +- struct qusb2_phy *qphy = phy_get_drvdata(phy); +- +- clk_disable_unprepare(qphy->iface_clk); +- +- regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs); +- +- return 0; +-} +- +-static int qusb2_phy_init(struct phy *phy) +-{ +- struct qusb2_phy *qphy = phy_get_drvdata(phy); +- unsigned int val; +- unsigned int clk_scheme; +- int ret; +- +- dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__); +- +- /* enable ahb interface clock to program phy */ +- ret = clk_prepare_enable(qphy->cfg_ahb_clk); +- if (ret) { +- dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); +- return ret; +- } +- +- /* Perform phy reset */ +- ret = reset_control_assert(qphy->phy_reset); +- if (ret) { +- dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret); +- goto disable_ahb_clk; +- } +- +- /* 100 us delay to keep PHY in reset mode */ +- usleep_range(100, 150); +- +- ret = reset_control_deassert(qphy->phy_reset); +- if (ret) { +- dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret); +- goto disable_ahb_clk; +- } +- +- /* Disable the PHY */ +- qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, +- CLAMP_N_EN | FREEZIO_N | POWER_DOWN); +- +- /* save reset value to override reference clock scheme later */ +- val = readl(qphy->base + QUSB2PHY_PLL_TEST); +- +- qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl, +- qphy->cfg->tbl_num); +- +- /* Set efuse value for tuning the PHY */ +- qusb2_phy_set_tune2_param(qphy); +- +- /* Enable the PHY */ +- qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN); +- +- /* Required to get phy pll lock successfully */ +- usleep_range(150, 160); +- +- /* Default is single-ended clock on msm8996 */ +- qphy->has_se_clk_scheme = true; +- /* +- * read TCSR_PHY_CLK_SCHEME register to check if single-ended +- * clock scheme is selected. If yes, then disable differential +- * ref_clk and use single-ended clock, otherwise use differential +- * ref_clk only. +- */ +- if (qphy->tcsr) { +- ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset, +- &clk_scheme); +- if (ret) { +- dev_err(&phy->dev, "failed to read clk scheme reg\n"); +- goto assert_phy_reset; +- } +- +- /* is it a differential clock scheme ? */ +- if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) { +- dev_vdbg(&phy->dev, "%s(): select differential clk\n", +- __func__); +- qphy->has_se_clk_scheme = false; +- } else { +- dev_vdbg(&phy->dev, "%s(): select single-ended clk\n", +- __func__); +- } +- } +- +- if (!qphy->has_se_clk_scheme) { +- val &= ~CLK_REF_SEL; +- ret = clk_prepare_enable(qphy->ref_clk); +- if (ret) { +- dev_err(&phy->dev, "failed to enable ref clk, %d\n", +- ret); +- goto assert_phy_reset; +- } +- } else { +- val |= CLK_REF_SEL; +- } +- +- writel(val, qphy->base + QUSB2PHY_PLL_TEST); +- +- /* ensure above write is through */ +- readl(qphy->base + QUSB2PHY_PLL_TEST); +- +- /* Required to get phy pll lock successfully */ +- usleep_range(100, 110); +- +- val = readb(qphy->base + QUSB2PHY_PLL_STATUS); +- if (!(val & PLL_LOCKED)) { +- dev_err(&phy->dev, +- "QUSB2PHY pll lock failed: status reg = %x\n", val); +- ret = -EBUSY; +- goto disable_ref_clk; +- } +- +- return 0; +- +-disable_ref_clk: +- if (!qphy->has_se_clk_scheme) +- clk_disable_unprepare(qphy->ref_clk); +-assert_phy_reset: +- reset_control_assert(qphy->phy_reset); +-disable_ahb_clk: +- clk_disable_unprepare(qphy->cfg_ahb_clk); +- return ret; +-} +- +-static int qusb2_phy_exit(struct phy *phy) +-{ +- struct qusb2_phy *qphy = phy_get_drvdata(phy); +- +- /* Disable the PHY */ +- qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, +- CLAMP_N_EN | FREEZIO_N | POWER_DOWN); +- +- if (!qphy->has_se_clk_scheme) +- clk_disable_unprepare(qphy->ref_clk); +- +- reset_control_assert(qphy->phy_reset); +- +- clk_disable_unprepare(qphy->cfg_ahb_clk); +- +- return 0; +-} +- +-static const struct phy_ops qusb2_phy_gen_ops = { +- .init = qusb2_phy_init, +- .exit = qusb2_phy_exit, +- .power_on = qusb2_phy_poweron, +- .power_off = qusb2_phy_poweroff, +- .owner = THIS_MODULE, +-}; +- +-static const struct of_device_id qusb2_phy_of_match_table[] = { +- { +- .compatible = "qcom,msm8996-qusb2-phy", +- .data = &msm8996_phy_cfg, +- }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table); +- +-static int qusb2_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct qusb2_phy *qphy; +- struct phy_provider *phy_provider; +- struct phy *generic_phy; +- struct resource *res; +- int ret, i; +- int num; +- +- qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); +- if (!qphy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- qphy->base = devm_ioremap_resource(dev, res); +- if (IS_ERR(qphy->base)) +- return PTR_ERR(qphy->base); +- +- qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb"); +- if (IS_ERR(qphy->cfg_ahb_clk)) { +- ret = PTR_ERR(qphy->cfg_ahb_clk); +- if (ret != -EPROBE_DEFER) +- dev_err(dev, "failed to get cfg ahb clk, %d\n", ret); +- return ret; +- } +- +- qphy->ref_clk = devm_clk_get(dev, "ref"); +- if (IS_ERR(qphy->ref_clk)) { +- ret = PTR_ERR(qphy->ref_clk); +- if (ret != -EPROBE_DEFER) +- dev_err(dev, "failed to get ref clk, %d\n", ret); +- return ret; +- } +- +- qphy->iface_clk = devm_clk_get(dev, "iface"); +- if (IS_ERR(qphy->iface_clk)) { +- ret = PTR_ERR(qphy->iface_clk); +- if (ret == -EPROBE_DEFER) +- return ret; +- qphy->iface_clk = NULL; +- dev_dbg(dev, "failed to get iface clk, %d\n", ret); +- } +- +- qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0); +- if (IS_ERR(qphy->phy_reset)) { +- dev_err(dev, "failed to get phy core reset\n"); +- return PTR_ERR(qphy->phy_reset); +- } +- +- num = ARRAY_SIZE(qphy->vregs); +- for (i = 0; i < num; i++) +- qphy->vregs[i].supply = qusb2_phy_vreg_names[i]; +- +- ret = devm_regulator_bulk_get(dev, num, qphy->vregs); +- if (ret) { +- dev_err(dev, "failed to get regulator supplies\n"); +- return ret; +- } +- +- /* Get the specific init parameters of QMP phy */ +- qphy->cfg = of_device_get_match_data(dev); +- +- qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node, +- "qcom,tcsr-syscon"); +- if (IS_ERR(qphy->tcsr)) { +- dev_dbg(dev, "failed to lookup TCSR regmap\n"); +- qphy->tcsr = NULL; +- } +- +- qphy->cell = devm_nvmem_cell_get(dev, NULL); +- if (IS_ERR(qphy->cell)) { +- if (PTR_ERR(qphy->cell) == -EPROBE_DEFER) +- return -EPROBE_DEFER; +- qphy->cell = NULL; +- dev_dbg(dev, "failed to lookup tune2 hstx trim value\n"); +- } +- +- generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops); +- if (IS_ERR(generic_phy)) { +- ret = PTR_ERR(generic_phy); +- dev_err(dev, "failed to create phy, %d\n", ret); +- return ret; +- } +- qphy->phy = generic_phy; +- +- dev_set_drvdata(dev, qphy); +- phy_set_drvdata(generic_phy, qphy); +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (!IS_ERR(phy_provider)) +- dev_info(dev, "Registered Qcom-QUSB2 phy\n"); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static struct platform_driver qusb2_phy_driver = { +- .probe = qusb2_phy_probe, +- .driver = { +- .name = "qcom-qusb2-phy", +- .of_match_table = qusb2_phy_of_match_table, +- }, +-}; +- +-module_platform_driver(qusb2_phy_driver); +- +-MODULE_AUTHOR("Vivek Gautam "); +-MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h +deleted file mode 100644 +index 13b02b7de30b..000000000000 +--- a/drivers/phy/phy-qcom-ufs-i.h ++++ /dev/null +@@ -1,155 +0,0 @@ +-/* +- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#ifndef UFS_QCOM_PHY_I_H_ +-#define UFS_QCOM_PHY_I_H_ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ +-({ \ +- ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \ +- might_sleep_if(timeout_us); \ +- for (;;) { \ +- (val) = readl(addr); \ +- if (cond) \ +- break; \ +- if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \ +- (val) = readl(addr); \ +- break; \ +- } \ +- if (sleep_us) \ +- usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \ +- } \ +- (cond) ? 0 : -ETIMEDOUT; \ +-}) +- +-#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ +- { \ +- .reg_offset = reg, \ +- .cfg_value = val, \ +- } +- +-#define UFS_QCOM_PHY_NAME_LEN 30 +- +-enum { +- MASK_SERDES_START = 0x1, +- MASK_PCS_READY = 0x1, +-}; +- +-enum { +- OFFSET_SERDES_START = 0x0, +-}; +- +-struct ufs_qcom_phy_stored_attributes { +- u32 att; +- u32 value; +-}; +- +- +-struct ufs_qcom_phy_calibration { +- u32 reg_offset; +- u32 cfg_value; +-}; +- +-struct ufs_qcom_phy_vreg { +- const char *name; +- struct regulator *reg; +- int max_uA; +- int min_uV; +- int max_uV; +- bool enabled; +-}; +- +-struct ufs_qcom_phy { +- struct list_head list; +- struct device *dev; +- void __iomem *mmio; +- void __iomem *dev_ref_clk_ctrl_mmio; +- struct clk *tx_iface_clk; +- struct clk *rx_iface_clk; +- bool is_iface_clk_enabled; +- struct clk *ref_clk_src; +- struct clk *ref_clk_parent; +- struct clk *ref_clk; +- bool is_ref_clk_enabled; +- bool is_dev_ref_clk_enabled; +- struct ufs_qcom_phy_vreg vdda_pll; +- struct ufs_qcom_phy_vreg vdda_phy; +- struct ufs_qcom_phy_vreg vddp_ref_clk; +- unsigned int quirks; +- +- /* +- * If UFS link is put into Hibern8 and if UFS PHY analog hardware is +- * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8 +- * exit might fail even after powering on UFS PHY analog hardware. +- * Enabling this quirk will help to solve above issue by doing +- * custom PHY settings just before PHY analog power collapse. +- */ +- #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0) +- +- u8 host_ctrl_rev_major; +- u16 host_ctrl_rev_minor; +- u16 host_ctrl_rev_step; +- +- char name[UFS_QCOM_PHY_NAME_LEN]; +- struct ufs_qcom_phy_calibration *cached_regs; +- int cached_regs_table_size; +- bool is_powered_on; +- struct ufs_qcom_phy_specific_ops *phy_spec_ops; +-}; +- +-/** +- * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a +- * specific implementation per phy. Each UFS phy, should implement +- * those functions according to its spec and requirements +- * @calibrate_phy: pointer to a function that calibrate the phy +- * @start_serdes: pointer to a function that starts the serdes +- * @is_physical_coding_sublayer_ready: pointer to a function that +- * checks pcs readiness. returns 0 for success and non-zero for error. +- * @set_tx_lane_enable: pointer to a function that enable tx lanes +- * @power_control: pointer to a function that controls analog rail of phy +- * and writes to QSERDES_RX_SIGDET_CNTRL attribute +- */ +-struct ufs_qcom_phy_specific_ops { +- int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B); +- void (*start_serdes)(struct ufs_qcom_phy *phy); +- int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); +- void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); +- void (*power_control)(struct ufs_qcom_phy *phy, bool val); +-}; +- +-struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy); +-int ufs_qcom_phy_power_on(struct phy *generic_phy); +-int ufs_qcom_phy_power_off(struct phy *generic_phy); +-int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common); +-int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common); +-int ufs_qcom_phy_remove(struct phy *generic_phy, +- struct ufs_qcom_phy *ufs_qcom_phy); +-struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, +- struct ufs_qcom_phy *common_cfg, +- const struct phy_ops *ufs_qcom_phy_gen_ops, +- struct ufs_qcom_phy_specific_ops *phy_spec_ops); +-int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, +- struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, +- struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, +- bool is_rate_B); +-#endif +diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c +deleted file mode 100644 +index 12a1b498dc4b..000000000000 +--- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c ++++ /dev/null +@@ -1,178 +0,0 @@ +-/* +- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include "phy-qcom-ufs-qmp-14nm.h" +- +-#define UFS_PHY_NAME "ufs_phy_qmp_14nm" +-#define UFS_PHY_VDDA_PHY_UV (925000) +- +-static +-int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, +- bool is_rate_B) +-{ +- int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); +- int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); +- int err; +- +- err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, +- tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); +- +- if (err) +- dev_err(ufs_qcom_phy->dev, +- "%s: ufs_qcom_phy_calibrate() failed %d\n", +- __func__, err); +- return err; +-} +- +-static +-void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) +-{ +- phy_common->quirks = +- UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; +-} +- +-static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) +-{ +- return 0; +-} +- +-static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) +-{ +- return 0; +-} +- +-static +-void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) +-{ +- writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); +- /* +- * Before any transactions involving PHY, ensure PHY knows +- * that it's analog rail is powered ON (or OFF). +- */ +- mb(); +-} +- +-static inline +-void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) +-{ +- /* +- * 14nm PHY does not have TX_LANE_ENABLE register. +- * Implement this function so as not to propagate error to caller. +- */ +-} +- +-static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) +-{ +- u32 tmp; +- +- tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); +- tmp &= ~MASK_SERDES_START; +- tmp |= (1 << OFFSET_SERDES_START); +- writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); +- /* Ensure register value is committed */ +- mb(); +-} +- +-static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) +-{ +- int err = 0; +- u32 val; +- +- err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, +- val, (val & MASK_PCS_READY), 10, 1000000); +- if (err) +- dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", +- __func__, err); +- return err; +-} +- +-static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { +- .init = ufs_qcom_phy_qmp_14nm_init, +- .exit = ufs_qcom_phy_qmp_14nm_exit, +- .power_on = ufs_qcom_phy_power_on, +- .power_off = ufs_qcom_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { +- .calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate, +- .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, +- .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, +- .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, +- .power_control = ufs_qcom_phy_qmp_14nm_power_control, +-}; +- +-static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct phy *generic_phy; +- struct ufs_qcom_phy_qmp_14nm *phy; +- struct ufs_qcom_phy *phy_common; +- int err = 0; +- +- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) { +- err = -ENOMEM; +- goto out; +- } +- phy_common = &phy->common_cfg; +- +- generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, +- &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops); +- +- if (!generic_phy) { +- err = -EIO; +- goto out; +- } +- +- err = ufs_qcom_phy_init_clks(phy_common); +- if (err) +- goto out; +- +- err = ufs_qcom_phy_init_vregulators(phy_common); +- if (err) +- goto out; +- +- phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; +- phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; +- +- ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); +- +- phy_set_drvdata(generic_phy, phy); +- +- strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); +- +-out: +- return err; +-} +- +-static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = { +- {.compatible = "qcom,ufs-phy-qmp-14nm"}, +- {.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"}, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match); +- +-static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = { +- .probe = ufs_qcom_phy_qmp_14nm_probe, +- .driver = { +- .of_match_table = ufs_qcom_phy_qmp_14nm_of_match, +- .name = "ufs_qcom_phy_qmp_14nm", +- }, +-}; +- +-module_platform_driver(ufs_qcom_phy_qmp_14nm_driver); +- +-MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/phy-qcom-ufs-qmp-14nm.h +deleted file mode 100644 +index 3aefdbacbcd0..000000000000 +--- a/drivers/phy/phy-qcom-ufs-qmp-14nm.h ++++ /dev/null +@@ -1,177 +0,0 @@ +-/* +- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#ifndef UFS_QCOM_PHY_QMP_14NM_H_ +-#define UFS_QCOM_PHY_QMP_14NM_H_ +- +-#include "phy-qcom-ufs-i.h" +- +-/* QCOM UFS PHY control registers */ +-#define COM_OFF(x) (0x000 + x) +-#define PHY_OFF(x) (0xC00 + x) +-#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) +-#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) +- +-/* UFS PHY QSERDES COM registers */ +-#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) +-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) +-#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) +-#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C) +-#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50) +-#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54) +-#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58) +-#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C) +-#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60) +-#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) +-#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) +-#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) +-#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88) +-#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90) +-#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) +-#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) +-#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) +-#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) +-#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) +-#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) +-#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4) +-#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC) +-#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0) +-#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4) +-#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8) +-#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC) +-#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0) +-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108) +-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C) +-#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110) +-#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114) +-#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124) +-#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128) +-#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C) +-#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) +-#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) +-#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) +-#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) +-#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) +-#define QSERDES_COM_CLK_SELECT COM_OFF(0x174) +-#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) +-#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) +-#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) +-#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) +-#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) +-#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) +- +-/* UFS PHY registers */ +-#define UFS_PHY_PHY_START PHY_OFF(0x00) +-#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) +-#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) +- +-/* UFS PHY TX registers */ +-#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68) +-#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94) +- +-/* UFS PHY RX registers */ +-#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40) +-#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90) +-#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4) +-#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8) +-#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC) +-#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0) +-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8) +-#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114) +-#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118) +-#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C) +-#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C) +- +-/* +- * This structure represents the 14nm specific phy. +- * common_cfg MUST remain the first field in this structure +- * in case extra fields are added. This way, when calling +- * get_ufs_qcom_phy() of generic phy, we can extract the +- * common phy structure (struct ufs_qcom_phy) out of it +- * regardless of the relevant specific phy. +- */ +-struct ufs_qcom_phy_qmp_14nm { +- struct ufs_qcom_phy common_cfg; +-}; +- +-static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { +- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), +- +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), +- +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), +-}; +- +-static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), +-}; +- +-#endif +diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c +deleted file mode 100644 +index 4f68acb58b73..000000000000 +--- a/drivers/phy/phy-qcom-ufs-qmp-20nm.c ++++ /dev/null +@@ -1,232 +0,0 @@ +-/* +- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include "phy-qcom-ufs-qmp-20nm.h" +- +-#define UFS_PHY_NAME "ufs_phy_qmp_20nm" +- +-static +-int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, +- bool is_rate_B) +-{ +- struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; +- int tbl_size_A, tbl_size_B; +- u8 major = ufs_qcom_phy->host_ctrl_rev_major; +- u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; +- u16 step = ufs_qcom_phy->host_ctrl_rev_step; +- int err; +- +- if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) { +- tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0); +- tbl_A = phy_cal_table_rate_A_1_2_0; +- } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) { +- tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); +- tbl_A = phy_cal_table_rate_A_1_3_0; +- } else { +- dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n", +- __func__); +- err = -ENODEV; +- goto out; +- } +- +- tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); +- tbl_B = phy_cal_table_rate_B; +- +- err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, +- tbl_B, tbl_size_B, is_rate_B); +- +- if (err) +- dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", +- __func__, err); +- +-out: +- return err; +-} +- +-static +-void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) +-{ +- phy_common->quirks = +- UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; +-} +- +-static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy) +-{ +- return 0; +-} +- +-static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) +-{ +- return 0; +-} +- +-static +-void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val) +-{ +- bool hibern8_exit_after_pwr_collapse = phy->quirks & +- UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; +- +- if (val) { +- writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); +- /* +- * Before any transactions involving PHY, ensure PHY knows +- * that it's analog rail is powered ON. +- */ +- mb(); +- +- if (hibern8_exit_after_pwr_collapse) { +- /* +- * Give atleast 1us delay after restoring PHY analog +- * power. +- */ +- usleep_range(1, 2); +- writel_relaxed(0x0A, phy->mmio + +- QSERDES_COM_SYSCLK_EN_SEL_TXBAND); +- writel_relaxed(0x08, phy->mmio + +- QSERDES_COM_SYSCLK_EN_SEL_TXBAND); +- /* +- * Make sure workaround is deactivated before proceeding +- * with normal PHY operations. +- */ +- mb(); +- } +- } else { +- if (hibern8_exit_after_pwr_collapse) { +- writel_relaxed(0x0A, phy->mmio + +- QSERDES_COM_SYSCLK_EN_SEL_TXBAND); +- writel_relaxed(0x02, phy->mmio + +- QSERDES_COM_SYSCLK_EN_SEL_TXBAND); +- /* +- * Make sure that above workaround is activated before +- * PHY analog power collapse. +- */ +- mb(); +- } +- +- writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); +- /* +- * ensure that PHY knows its PHY analog rail is going +- * to be powered down +- */ +- mb(); +- } +-} +- +-static +-void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) +-{ +- writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK, +- phy->mmio + UFS_PHY_TX_LANE_ENABLE); +- mb(); +-} +- +-static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy) +-{ +- u32 tmp; +- +- tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); +- tmp &= ~MASK_SERDES_START; +- tmp |= (1 << OFFSET_SERDES_START); +- writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); +- mb(); +-} +- +-static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) +-{ +- int err = 0; +- u32 val; +- +- err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, +- val, (val & MASK_PCS_READY), 10, 1000000); +- if (err) +- dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", +- __func__, err); +- return err; +-} +- +-static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { +- .init = ufs_qcom_phy_qmp_20nm_init, +- .exit = ufs_qcom_phy_qmp_20nm_exit, +- .power_on = ufs_qcom_phy_power_on, +- .power_off = ufs_qcom_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { +- .calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate, +- .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, +- .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, +- .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, +- .power_control = ufs_qcom_phy_qmp_20nm_power_control, +-}; +- +-static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct phy *generic_phy; +- struct ufs_qcom_phy_qmp_20nm *phy; +- struct ufs_qcom_phy *phy_common; +- int err = 0; +- +- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) { +- err = -ENOMEM; +- goto out; +- } +- phy_common = &phy->common_cfg; +- +- generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, +- &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops); +- +- if (!generic_phy) { +- err = -EIO; +- goto out; +- } +- +- err = ufs_qcom_phy_init_clks(phy_common); +- if (err) +- goto out; +- +- err = ufs_qcom_phy_init_vregulators(phy_common); +- if (err) +- goto out; +- +- ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common); +- +- phy_set_drvdata(generic_phy, phy); +- +- strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); +- +-out: +- return err; +-} +- +-static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = { +- {.compatible = "qcom,ufs-phy-qmp-20nm"}, +- {}, +-}; +-MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match); +- +-static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = { +- .probe = ufs_qcom_phy_qmp_20nm_probe, +- .driver = { +- .of_match_table = ufs_qcom_phy_qmp_20nm_of_match, +- .name = "ufs_qcom_phy_qmp_20nm", +- }, +-}; +- +-module_platform_driver(ufs_qcom_phy_qmp_20nm_driver); +- +-MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/phy-qcom-ufs-qmp-20nm.h +deleted file mode 100644 +index 4f3076bb3d71..000000000000 +--- a/drivers/phy/phy-qcom-ufs-qmp-20nm.h ++++ /dev/null +@@ -1,235 +0,0 @@ +-/* +- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#ifndef UFS_QCOM_PHY_QMP_20NM_H_ +-#define UFS_QCOM_PHY_QMP_20NM_H_ +- +-#include "phy-qcom-ufs-i.h" +- +-/* QCOM UFS PHY control registers */ +- +-#define COM_OFF(x) (0x000 + x) +-#define PHY_OFF(x) (0xC00 + x) +-#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) +-#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) +- +-/* UFS PHY PLL block registers */ +-#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0) +-#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04) +-#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14) +-#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24) +-#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28) +-#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30) +-#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34) +-#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38) +-#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C) +-#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48) +-#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C) +-#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50) +-#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90) +-#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94) +-#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98) +-#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C) +-#define QSERDES_COM_BGTC COM_OFF(0xA0) +-#define QSERDES_COM_DEC_START1 COM_OFF(0xAC) +-#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0) +-#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8) +-#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC) +-#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100) +-#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104) +-#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108) +-#define QSERDES_COM_DEC_START2 COM_OFF(0x10C) +-#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110) +-#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114) +-#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118) +- +-/* TX LANE n (0, 1) registers */ +-#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08) +-#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C) +-#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54) +- +-/* RX LANE n (0, 1) registers */ +-#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0) +-#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8) +-#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8) +-#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC) +-#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0) +-#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4) +-#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC) +-#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC) +-#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100) +- +-/* UFS PHY registers */ +-#define UFS_PHY_PHY_START PHY_OFF(0x00) +-#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4) +-#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44) +-#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08) +-#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C) +-#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10) +-#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14) +-#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34) +-#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38) +-#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C) +-#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40) +-#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68) +-#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28) +-#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C) +-#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48) +-#define UFS_PHY_PLL_CNTL PHY_OFF(0x50) +-#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54) +-#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C) +-#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58) +-#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60) +-#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64) +-#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C) +-#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4) +-#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0) +-#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8) +-#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4) +-#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC) +-#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8) +-#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC) +-#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100) +-#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c) +-#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160) +-#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7) +-#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6) +-#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5) +-#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4) +-#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3) +-#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2) +-#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1) +-#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0) +-#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164) +-#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168) +-#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C) +-#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170) +-#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174) +- +-#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3 +- +-/* +- * This structure represents the 20nm specific phy. +- * common_cfg MUST remain the first field in this structure +- * in case extra fields are added. This way, when calling +- * get_ufs_qcom_phy() of generic phy, we can extract the +- * common phy structure (struct ufs_qcom_phy) out of it +- * regardless of the relevant specific phy. +- */ +-struct ufs_qcom_phy_qmp_20nm { +- struct ufs_qcom_phy common_cfg; +-}; +- +-static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = { +- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), +- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), +-}; +- +-static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = { +- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), +- UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), +-}; +- +-static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65), +- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e), +-}; +- +-#endif +diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c +deleted file mode 100644 +index 43865ef340e2..000000000000 +--- a/drivers/phy/phy-qcom-ufs.c ++++ /dev/null +@@ -1,691 +0,0 @@ +-/* +- * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 and +- * only version 2 as published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include "phy-qcom-ufs-i.h" +- +-#define MAX_PROP_NAME 32 +-#define VDDA_PHY_MIN_UV 1000000 +-#define VDDA_PHY_MAX_UV 1000000 +-#define VDDA_PLL_MIN_UV 1800000 +-#define VDDA_PLL_MAX_UV 1800000 +-#define VDDP_REF_CLK_MIN_UV 1200000 +-#define VDDP_REF_CLK_MAX_UV 1200000 +- +-int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, +- struct ufs_qcom_phy_calibration *tbl_A, +- int tbl_size_A, +- struct ufs_qcom_phy_calibration *tbl_B, +- int tbl_size_B, bool is_rate_B) +-{ +- int i; +- int ret = 0; +- +- if (!tbl_A) { +- dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); +- ret = EINVAL; +- goto out; +- } +- +- for (i = 0; i < tbl_size_A; i++) +- writel_relaxed(tbl_A[i].cfg_value, +- ufs_qcom_phy->mmio + tbl_A[i].reg_offset); +- +- /* +- * In case we would like to work in rate B, we need +- * to override a registers that were configured in rate A table +- * with registers of rate B table. +- * table. +- */ +- if (is_rate_B) { +- if (!tbl_B) { +- dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", +- __func__); +- ret = EINVAL; +- goto out; +- } +- +- for (i = 0; i < tbl_size_B; i++) +- writel_relaxed(tbl_B[i].cfg_value, +- ufs_qcom_phy->mmio + tbl_B[i].reg_offset); +- } +- +- /* flush buffered writes */ +- mb(); +- +-out: +- return ret; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); +- +-/* +- * This assumes the embedded phy structure inside generic_phy is of type +- * struct ufs_qcom_phy. In order to function properly it's crucial +- * to keep the embedded struct "struct ufs_qcom_phy common_cfg" +- * as the first inside generic_phy. +- */ +-struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy) +-{ +- return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy); +-} +-EXPORT_SYMBOL_GPL(get_ufs_qcom_phy); +- +-static +-int ufs_qcom_phy_base_init(struct platform_device *pdev, +- struct ufs_qcom_phy *phy_common) +-{ +- struct device *dev = &pdev->dev; +- struct resource *res; +- int err = 0; +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); +- phy_common->mmio = devm_ioremap_resource(dev, res); +- if (IS_ERR((void const *)phy_common->mmio)) { +- err = PTR_ERR((void const *)phy_common->mmio); +- phy_common->mmio = NULL; +- dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", +- __func__, err); +- return err; +- } +- +- /* "dev_ref_clk_ctrl_mem" is optional resource */ +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +- "dev_ref_clk_ctrl_mem"); +- phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); +- if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) +- phy_common->dev_ref_clk_ctrl_mmio = NULL; +- +- return 0; +-} +- +-struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, +- struct ufs_qcom_phy *common_cfg, +- const struct phy_ops *ufs_qcom_phy_gen_ops, +- struct ufs_qcom_phy_specific_ops *phy_spec_ops) +-{ +- int err; +- struct device *dev = &pdev->dev; +- struct phy *generic_phy = NULL; +- struct phy_provider *phy_provider; +- +- err = ufs_qcom_phy_base_init(pdev, common_cfg); +- if (err) { +- dev_err(dev, "%s: phy base init failed %d\n", __func__, err); +- goto out; +- } +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) { +- err = PTR_ERR(phy_provider); +- dev_err(dev, "%s: failed to register phy %d\n", __func__, err); +- goto out; +- } +- +- generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops); +- if (IS_ERR(generic_phy)) { +- err = PTR_ERR(generic_phy); +- dev_err(dev, "%s: failed to create phy %d\n", __func__, err); +- generic_phy = NULL; +- goto out; +- } +- +- common_cfg->phy_spec_ops = phy_spec_ops; +- common_cfg->dev = dev; +- +-out: +- return generic_phy; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); +- +-static int __ufs_qcom_phy_clk_get(struct device *dev, +- const char *name, struct clk **clk_out, bool err_print) +-{ +- struct clk *clk; +- int err = 0; +- +- clk = devm_clk_get(dev, name); +- if (IS_ERR(clk)) { +- err = PTR_ERR(clk); +- if (err_print) +- dev_err(dev, "failed to get %s err %d", name, err); +- } else { +- *clk_out = clk; +- } +- +- return err; +-} +- +-static int ufs_qcom_phy_clk_get(struct device *dev, +- const char *name, struct clk **clk_out) +-{ +- return __ufs_qcom_phy_clk_get(dev, name, clk_out, true); +-} +- +-int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) +-{ +- int err; +- +- if (of_device_is_compatible(phy_common->dev->of_node, +- "qcom,msm8996-ufs-phy-qmp-14nm")) +- goto skip_txrx_clk; +- +- err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk", +- &phy_common->tx_iface_clk); +- if (err) +- goto out; +- +- err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk", +- &phy_common->rx_iface_clk); +- if (err) +- goto out; +- +-skip_txrx_clk: +- err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", +- &phy_common->ref_clk_src); +- if (err) +- goto out; +- +- /* +- * "ref_clk_parent" is optional hence don't abort init if it's not +- * found. +- */ +- __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent", +- &phy_common->ref_clk_parent, false); +- +- err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk", +- &phy_common->ref_clk); +- +-out: +- return err; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); +- +-static int ufs_qcom_phy_init_vreg(struct device *dev, +- struct ufs_qcom_phy_vreg *vreg, +- const char *name) +-{ +- int err = 0; +- +- char prop_name[MAX_PROP_NAME]; +- +- vreg->name = name; +- vreg->reg = devm_regulator_get(dev, name); +- if (IS_ERR(vreg->reg)) { +- err = PTR_ERR(vreg->reg); +- dev_err(dev, "failed to get %s, %d\n", name, err); +- goto out; +- } +- +- if (dev->of_node) { +- snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); +- err = of_property_read_u32(dev->of_node, +- prop_name, &vreg->max_uA); +- if (err && err != -EINVAL) { +- dev_err(dev, "%s: failed to read %s\n", +- __func__, prop_name); +- goto out; +- } else if (err == -EINVAL || !vreg->max_uA) { +- if (regulator_count_voltages(vreg->reg) > 0) { +- dev_err(dev, "%s: %s is mandatory\n", +- __func__, prop_name); +- goto out; +- } +- err = 0; +- } +- } +- +- if (!strcmp(name, "vdda-pll")) { +- vreg->max_uV = VDDA_PLL_MAX_UV; +- vreg->min_uV = VDDA_PLL_MIN_UV; +- } else if (!strcmp(name, "vdda-phy")) { +- vreg->max_uV = VDDA_PHY_MAX_UV; +- vreg->min_uV = VDDA_PHY_MIN_UV; +- } else if (!strcmp(name, "vddp-ref-clk")) { +- vreg->max_uV = VDDP_REF_CLK_MAX_UV; +- vreg->min_uV = VDDP_REF_CLK_MIN_UV; +- } +- +-out: +- return err; +-} +- +-int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) +-{ +- int err; +- +- err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll, +- "vdda-pll"); +- if (err) +- goto out; +- +- err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy, +- "vdda-phy"); +- +- if (err) +- goto out; +- +- err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, +- "vddp-ref-clk"); +- +-out: +- return err; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators); +- +-static int ufs_qcom_phy_cfg_vreg(struct device *dev, +- struct ufs_qcom_phy_vreg *vreg, bool on) +-{ +- int ret = 0; +- struct regulator *reg = vreg->reg; +- const char *name = vreg->name; +- int min_uV; +- int uA_load; +- +- if (regulator_count_voltages(reg) > 0) { +- min_uV = on ? vreg->min_uV : 0; +- ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); +- if (ret) { +- dev_err(dev, "%s: %s set voltage failed, err=%d\n", +- __func__, name, ret); +- goto out; +- } +- uA_load = on ? vreg->max_uA : 0; +- ret = regulator_set_load(reg, uA_load); +- if (ret >= 0) { +- /* +- * regulator_set_load() returns new regulator +- * mode upon success. +- */ +- ret = 0; +- } else { +- dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", +- __func__, name, uA_load, ret); +- goto out; +- } +- } +-out: +- return ret; +-} +- +-static int ufs_qcom_phy_enable_vreg(struct device *dev, +- struct ufs_qcom_phy_vreg *vreg) +-{ +- int ret = 0; +- +- if (!vreg || vreg->enabled) +- goto out; +- +- ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true); +- if (ret) { +- dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n", +- __func__, ret); +- goto out; +- } +- +- ret = regulator_enable(vreg->reg); +- if (ret) { +- dev_err(dev, "%s: enable failed, err=%d\n", +- __func__, ret); +- goto out; +- } +- +- vreg->enabled = true; +-out: +- return ret; +-} +- +-static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy) +-{ +- int ret = 0; +- +- if (phy->is_ref_clk_enabled) +- goto out; +- +- /* +- * reference clock is propagated in a daisy-chained manner from +- * source to phy, so ungate them at each stage. +- */ +- ret = clk_prepare_enable(phy->ref_clk_src); +- if (ret) { +- dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", +- __func__, ret); +- goto out; +- } +- +- /* +- * "ref_clk_parent" is optional clock hence make sure that clk reference +- * is available before trying to enable the clock. +- */ +- if (phy->ref_clk_parent) { +- ret = clk_prepare_enable(phy->ref_clk_parent); +- if (ret) { +- dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", +- __func__, ret); +- goto out_disable_src; +- } +- } +- +- ret = clk_prepare_enable(phy->ref_clk); +- if (ret) { +- dev_err(phy->dev, "%s: ref_clk enable failed %d\n", +- __func__, ret); +- goto out_disable_parent; +- } +- +- phy->is_ref_clk_enabled = true; +- goto out; +- +-out_disable_parent: +- if (phy->ref_clk_parent) +- clk_disable_unprepare(phy->ref_clk_parent); +-out_disable_src: +- clk_disable_unprepare(phy->ref_clk_src); +-out: +- return ret; +-} +- +-static int ufs_qcom_phy_disable_vreg(struct device *dev, +- struct ufs_qcom_phy_vreg *vreg) +-{ +- int ret = 0; +- +- if (!vreg || !vreg->enabled) +- goto out; +- +- ret = regulator_disable(vreg->reg); +- +- if (!ret) { +- /* ignore errors on applying disable config */ +- ufs_qcom_phy_cfg_vreg(dev, vreg, false); +- vreg->enabled = false; +- } else { +- dev_err(dev, "%s: %s disable failed, err=%d\n", +- __func__, vreg->name, ret); +- } +-out: +- return ret; +-} +- +-static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy) +-{ +- if (phy->is_ref_clk_enabled) { +- clk_disable_unprepare(phy->ref_clk); +- /* +- * "ref_clk_parent" is optional clock hence make sure that clk +- * reference is available before trying to disable the clock. +- */ +- if (phy->ref_clk_parent) +- clk_disable_unprepare(phy->ref_clk_parent); +- clk_disable_unprepare(phy->ref_clk_src); +- phy->is_ref_clk_enabled = false; +- } +-} +- +-#define UFS_REF_CLK_EN (1 << 5) +- +-static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable) +-{ +- struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy); +- +- if (phy->dev_ref_clk_ctrl_mmio && +- (enable ^ phy->is_dev_ref_clk_enabled)) { +- u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio); +- +- if (enable) +- temp |= UFS_REF_CLK_EN; +- else +- temp &= ~UFS_REF_CLK_EN; +- +- /* +- * If we are here to disable this clock immediately after +- * entering into hibern8, we need to make sure that device +- * ref_clk is active atleast 1us after the hibern8 enter. +- */ +- if (!enable) +- udelay(1); +- +- writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio); +- /* ensure that ref_clk is enabled/disabled before we return */ +- wmb(); +- /* +- * If we call hibern8 exit after this, we need to make sure that +- * device ref_clk is stable for atleast 1us before the hibern8 +- * exit command. +- */ +- if (enable) +- udelay(1); +- +- phy->is_dev_ref_clk_enabled = enable; +- } +-} +- +-void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy) +-{ +- ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true); +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_enable_dev_ref_clk); +- +-void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy) +-{ +- ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false); +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_disable_dev_ref_clk); +- +-/* Turn ON M-PHY RMMI interface clocks */ +-static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy) +-{ +- int ret = 0; +- +- if (phy->is_iface_clk_enabled) +- goto out; +- +- ret = clk_prepare_enable(phy->tx_iface_clk); +- if (ret) { +- dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", +- __func__, ret); +- goto out; +- } +- ret = clk_prepare_enable(phy->rx_iface_clk); +- if (ret) { +- clk_disable_unprepare(phy->tx_iface_clk); +- dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n", +- __func__, ret); +- goto out; +- } +- phy->is_iface_clk_enabled = true; +- +-out: +- return ret; +-} +- +-/* Turn OFF M-PHY RMMI interface clocks */ +-void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) +-{ +- if (phy->is_iface_clk_enabled) { +- clk_disable_unprepare(phy->tx_iface_clk); +- clk_disable_unprepare(phy->rx_iface_clk); +- phy->is_iface_clk_enabled = false; +- } +-} +- +-int ufs_qcom_phy_start_serdes(struct phy *generic_phy) +-{ +- struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); +- int ret = 0; +- +- if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { +- dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n", +- __func__); +- ret = -ENOTSUPP; +- } else { +- ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy); +- } +- +- return ret; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_start_serdes); +- +-int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) +-{ +- struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); +- int ret = 0; +- +- if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { +- dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", +- __func__); +- ret = -ENOTSUPP; +- } else { +- ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, +- tx_lanes); +- } +- +- return ret; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); +- +-void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, +- u8 major, u16 minor, u16 step) +-{ +- struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); +- +- ufs_qcom_phy->host_ctrl_rev_major = major; +- ufs_qcom_phy->host_ctrl_rev_minor = minor; +- ufs_qcom_phy->host_ctrl_rev_step = step; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); +- +-int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B) +-{ +- struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); +- int ret = 0; +- +- if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) { +- dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n", +- __func__); +- ret = -ENOTSUPP; +- } else { +- ret = ufs_qcom_phy->phy_spec_ops-> +- calibrate_phy(ufs_qcom_phy, is_rate_B); +- if (ret) +- dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n", +- __func__, ret); +- } +- +- return ret; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy); +- +-int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) +-{ +- struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); +- +- if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { +- dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", +- __func__); +- return -ENOTSUPP; +- } +- +- return ufs_qcom_phy->phy_spec_ops-> +- is_physical_coding_sublayer_ready(ufs_qcom_phy); +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_is_pcs_ready); +- +-int ufs_qcom_phy_power_on(struct phy *generic_phy) +-{ +- struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); +- struct device *dev = phy_common->dev; +- int err; +- +- if (phy_common->is_powered_on) +- return 0; +- +- err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); +- if (err) { +- dev_err(dev, "%s enable vdda_phy failed, err=%d\n", +- __func__, err); +- goto out; +- } +- +- phy_common->phy_spec_ops->power_control(phy_common, true); +- +- /* vdda_pll also enables ref clock LDOs so enable it first */ +- err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll); +- if (err) { +- dev_err(dev, "%s enable vdda_pll failed, err=%d\n", +- __func__, err); +- goto out_disable_phy; +- } +- +- err = ufs_qcom_phy_enable_iface_clk(phy_common); +- if (err) { +- dev_err(dev, "%s enable phy iface clock failed, err=%d\n", +- __func__, err); +- goto out_disable_pll; +- } +- +- err = ufs_qcom_phy_enable_ref_clk(phy_common); +- if (err) { +- dev_err(dev, "%s enable phy ref clock failed, err=%d\n", +- __func__, err); +- goto out_disable_iface_clk; +- } +- +- /* enable device PHY ref_clk pad rail */ +- if (phy_common->vddp_ref_clk.reg) { +- err = ufs_qcom_phy_enable_vreg(dev, +- &phy_common->vddp_ref_clk); +- if (err) { +- dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n", +- __func__, err); +- goto out_disable_ref_clk; +- } +- } +- +- phy_common->is_powered_on = true; +- goto out; +- +-out_disable_ref_clk: +- ufs_qcom_phy_disable_ref_clk(phy_common); +-out_disable_iface_clk: +- ufs_qcom_phy_disable_iface_clk(phy_common); +-out_disable_pll: +- ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll); +-out_disable_phy: +- ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy); +-out: +- return err; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on); +- +-int ufs_qcom_phy_power_off(struct phy *generic_phy) +-{ +- struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); +- +- if (!phy_common->is_powered_on) +- return 0; +- +- phy_common->phy_spec_ops->power_control(phy_common, false); +- +- if (phy_common->vddp_ref_clk.reg) +- ufs_qcom_phy_disable_vreg(phy_common->dev, +- &phy_common->vddp_ref_clk); +- ufs_qcom_phy_disable_ref_clk(phy_common); +- ufs_qcom_phy_disable_iface_clk(phy_common); +- +- ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); +- ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); +- phy_common->is_powered_on = false; +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); +diff --git a/drivers/phy/phy-qcom-usb-hs.c b/drivers/phy/phy-qcom-usb-hs.c +deleted file mode 100644 +index 4b20abc3ae2f..000000000000 +--- a/drivers/phy/phy-qcom-usb-hs.c ++++ /dev/null +@@ -1,295 +0,0 @@ +-/** +- * Copyright (C) 2016 Linaro Ltd +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define ULPI_PWR_CLK_MNG_REG 0x88 +-# define ULPI_PWR_OTG_COMP_DISABLE BIT(0) +- +-#define ULPI_MISC_A 0x96 +-# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) +-# define ULPI_MISC_A_VBUSVLDEXT BIT(0) +- +- +-struct ulpi_seq { +- u8 addr; +- u8 val; +-}; +- +-struct qcom_usb_hs_phy { +- struct ulpi *ulpi; +- struct phy *phy; +- struct clk *ref_clk; +- struct clk *sleep_clk; +- struct regulator *v1p8; +- struct regulator *v3p3; +- struct reset_control *reset; +- struct ulpi_seq *init_seq; +- struct extcon_dev *vbus_edev; +- struct notifier_block vbus_notify; +-}; +- +-static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode) +-{ +- struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); +- u8 addr; +- int ret; +- +- if (!uphy->vbus_edev) { +- u8 val = 0; +- +- switch (mode) { +- case PHY_MODE_USB_OTG: +- case PHY_MODE_USB_HOST: +- val |= ULPI_INT_IDGRD; +- case PHY_MODE_USB_DEVICE: +- val |= ULPI_INT_SESS_VALID; +- default: +- break; +- } +- +- ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val); +- if (ret) +- return ret; +- ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val); +- } else { +- switch (mode) { +- case PHY_MODE_USB_OTG: +- case PHY_MODE_USB_DEVICE: +- addr = ULPI_SET(ULPI_MISC_A); +- break; +- case PHY_MODE_USB_HOST: +- addr = ULPI_CLR(ULPI_MISC_A); +- break; +- default: +- return -EINVAL; +- } +- +- ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG), +- ULPI_PWR_OTG_COMP_DISABLE); +- if (ret) +- return ret; +- ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL); +- } +- +- return ret; +-} +- +-static int +-qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event, +- void *ptr) +-{ +- struct qcom_usb_hs_phy *uphy; +- u8 addr; +- +- uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify); +- +- if (event) +- addr = ULPI_SET(ULPI_MISC_A); +- else +- addr = ULPI_CLR(ULPI_MISC_A); +- +- return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT); +-} +- +-static int qcom_usb_hs_phy_power_on(struct phy *phy) +-{ +- struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); +- struct ulpi *ulpi = uphy->ulpi; +- const struct ulpi_seq *seq; +- int ret, state; +- +- ret = clk_prepare_enable(uphy->ref_clk); +- if (ret) +- return ret; +- +- ret = clk_prepare_enable(uphy->sleep_clk); +- if (ret) +- goto err_sleep; +- +- ret = regulator_set_load(uphy->v1p8, 50000); +- if (ret < 0) +- goto err_1p8; +- +- ret = regulator_enable(uphy->v1p8); +- if (ret) +- goto err_1p8; +- +- ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000, +- 3300000); +- if (ret) +- goto err_3p3; +- +- ret = regulator_set_load(uphy->v3p3, 50000); +- if (ret < 0) +- goto err_3p3; +- +- ret = regulator_enable(uphy->v3p3); +- if (ret) +- goto err_3p3; +- +- for (seq = uphy->init_seq; seq->addr; seq++) { +- ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr, +- seq->val); +- if (ret) +- goto err_ulpi; +- } +- +- if (uphy->reset) { +- ret = reset_control_reset(uphy->reset); +- if (ret) +- goto err_ulpi; +- } +- +- if (uphy->vbus_edev) { +- state = extcon_get_cable_state_(uphy->vbus_edev, EXTCON_USB); +- /* setup initial state */ +- qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state, +- uphy->vbus_edev); +- ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB, +- &uphy->vbus_notify); +- if (ret) +- goto err_ulpi; +- } +- +- return 0; +-err_ulpi: +- regulator_disable(uphy->v3p3); +-err_3p3: +- regulator_disable(uphy->v1p8); +-err_1p8: +- clk_disable_unprepare(uphy->sleep_clk); +-err_sleep: +- clk_disable_unprepare(uphy->ref_clk); +- return ret; +-} +- +-static int qcom_usb_hs_phy_power_off(struct phy *phy) +-{ +- int ret; +- struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); +- +- if (uphy->vbus_edev) { +- ret = extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB, +- &uphy->vbus_notify); +- if (ret) +- return ret; +- } +- +- regulator_disable(uphy->v3p3); +- regulator_disable(uphy->v1p8); +- clk_disable_unprepare(uphy->sleep_clk); +- clk_disable_unprepare(uphy->ref_clk); +- +- return 0; +-} +- +-static const struct phy_ops qcom_usb_hs_phy_ops = { +- .power_on = qcom_usb_hs_phy_power_on, +- .power_off = qcom_usb_hs_phy_power_off, +- .set_mode = qcom_usb_hs_phy_set_mode, +- .owner = THIS_MODULE, +-}; +- +-static int qcom_usb_hs_phy_probe(struct ulpi *ulpi) +-{ +- struct qcom_usb_hs_phy *uphy; +- struct phy_provider *p; +- struct clk *clk; +- struct regulator *reg; +- struct reset_control *reset; +- int size; +- int ret; +- +- uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); +- if (!uphy) +- return -ENOMEM; +- ulpi_set_drvdata(ulpi, uphy); +- uphy->ulpi = ulpi; +- +- size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq"); +- if (size < 0) +- size = 0; +- uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1, +- sizeof(*uphy->init_seq), GFP_KERNEL); +- if (!uphy->init_seq) +- return -ENOMEM; +- ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq", +- (u8 *)uphy->init_seq, size); +- if (ret && size) +- return ret; +- /* NUL terminate */ +- uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0; +- +- uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref"); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep"); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8"); +- if (IS_ERR(reg)) +- return PTR_ERR(reg); +- +- uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3"); +- if (IS_ERR(reg)) +- return PTR_ERR(reg); +- +- uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por"); +- if (IS_ERR(reset)) { +- if (PTR_ERR(reset) == -EPROBE_DEFER) +- return PTR_ERR(reset); +- uphy->reset = NULL; +- } +- +- uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, +- &qcom_usb_hs_phy_ops); +- if (IS_ERR(uphy->phy)) +- return PTR_ERR(uphy->phy); +- +- uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0); +- if (IS_ERR(uphy->vbus_edev)) { +- if (PTR_ERR(uphy->vbus_edev) != -ENODEV) +- return PTR_ERR(uphy->vbus_edev); +- uphy->vbus_edev = NULL; +- } +- +- uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier; +- phy_set_drvdata(uphy->phy, uphy); +- +- p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(p); +-} +- +-static const struct of_device_id qcom_usb_hs_phy_match[] = { +- { .compatible = "qcom,usb-hs-phy", }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match); +- +-static struct ulpi_driver qcom_usb_hs_phy_driver = { +- .probe = qcom_usb_hs_phy_probe, +- .driver = { +- .name = "qcom_usb_hs_phy", +- .of_match_table = qcom_usb_hs_phy_match, +- }, +-}; +-module_ulpi_driver(qcom_usb_hs_phy_driver); +- +-MODULE_DESCRIPTION("Qualcomm USB HS phy"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-qcom-usb-hsic.c b/drivers/phy/phy-qcom-usb-hsic.c +deleted file mode 100644 +index c110563a73cb..000000000000 +--- a/drivers/phy/phy-qcom-usb-hsic.c ++++ /dev/null +@@ -1,159 +0,0 @@ +-/** +- * Copyright (C) 2016 Linaro Ltd +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define ULPI_HSIC_CFG 0x30 +-#define ULPI_HSIC_IO_CAL 0x33 +- +-struct qcom_usb_hsic_phy { +- struct ulpi *ulpi; +- struct phy *phy; +- struct pinctrl *pctl; +- struct clk *phy_clk; +- struct clk *cal_clk; +- struct clk *cal_sleep_clk; +-}; +- +-static int qcom_usb_hsic_phy_power_on(struct phy *phy) +-{ +- struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); +- struct ulpi *ulpi = uphy->ulpi; +- struct pinctrl_state *pins_default; +- int ret; +- +- ret = clk_prepare_enable(uphy->phy_clk); +- if (ret) +- return ret; +- +- ret = clk_prepare_enable(uphy->cal_clk); +- if (ret) +- goto err_cal; +- +- ret = clk_prepare_enable(uphy->cal_sleep_clk); +- if (ret) +- goto err_sleep; +- +- /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ +- ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff); +- if (ret) +- goto err_ulpi; +- +- /* Enable periodic IO calibration in HSIC_CFG register */ +- ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8); +- if (ret) +- goto err_ulpi; +- +- /* Configure pins for HSIC functionality */ +- pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT); +- if (IS_ERR(pins_default)) +- return PTR_ERR(pins_default); +- +- ret = pinctrl_select_state(uphy->pctl, pins_default); +- if (ret) +- goto err_ulpi; +- +- /* Enable HSIC mode in HSIC_CFG register */ +- ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01); +- if (ret) +- goto err_ulpi; +- +- /* Disable auto-resume */ +- ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL), +- ULPI_IFC_CTRL_AUTORESUME); +- if (ret) +- goto err_ulpi; +- +- return ret; +-err_ulpi: +- clk_disable_unprepare(uphy->cal_sleep_clk); +-err_sleep: +- clk_disable_unprepare(uphy->cal_clk); +-err_cal: +- clk_disable_unprepare(uphy->phy_clk); +- return ret; +-} +- +-static int qcom_usb_hsic_phy_power_off(struct phy *phy) +-{ +- struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); +- +- clk_disable_unprepare(uphy->cal_sleep_clk); +- clk_disable_unprepare(uphy->cal_clk); +- clk_disable_unprepare(uphy->phy_clk); +- +- return 0; +-} +- +-static const struct phy_ops qcom_usb_hsic_phy_ops = { +- .power_on = qcom_usb_hsic_phy_power_on, +- .power_off = qcom_usb_hsic_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi) +-{ +- struct qcom_usb_hsic_phy *uphy; +- struct phy_provider *p; +- struct clk *clk; +- +- uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); +- if (!uphy) +- return -ENOMEM; +- ulpi_set_drvdata(ulpi, uphy); +- +- uphy->ulpi = ulpi; +- uphy->pctl = devm_pinctrl_get(&ulpi->dev); +- if (IS_ERR(uphy->pctl)) +- return PTR_ERR(uphy->pctl); +- +- uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy"); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal"); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep"); +- if (IS_ERR(clk)) +- return PTR_ERR(clk); +- +- uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, +- &qcom_usb_hsic_phy_ops); +- if (IS_ERR(uphy->phy)) +- return PTR_ERR(uphy->phy); +- phy_set_drvdata(uphy->phy, uphy); +- +- p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(p); +-} +- +-static const struct of_device_id qcom_usb_hsic_phy_match[] = { +- { .compatible = "qcom,usb-hsic-phy", }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); +- +-static struct ulpi_driver qcom_usb_hsic_phy_driver = { +- .probe = qcom_usb_hsic_phy_probe, +- .driver = { +- .name = "qcom_usb_hsic_phy", +- .of_match_table = qcom_usb_hsic_phy_match, +- }, +-}; +-module_ulpi_driver(qcom_usb_hsic_phy_driver); +- +-MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c +deleted file mode 100644 +index 97d4dd6ea924..000000000000 +--- a/drivers/phy/phy-rcar-gen2.c ++++ /dev/null +@@ -1,337 +0,0 @@ +-/* +- * Renesas R-Car Gen2 PHY driver +- * +- * Copyright (C) 2014 Renesas Solutions Corp. +- * Copyright (C) 2014 Cogent Embedded, Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define USBHS_LPSTS 0x02 +-#define USBHS_UGCTRL 0x80 +-#define USBHS_UGCTRL2 0x84 +-#define USBHS_UGSTS 0x88 /* From technical update */ +- +-/* Low Power Status register (LPSTS) */ +-#define USBHS_LPSTS_SUSPM 0x4000 +- +-/* USB General control register (UGCTRL) */ +-#define USBHS_UGCTRL_CONNECT 0x00000004 +-#define USBHS_UGCTRL_PLLRESET 0x00000001 +- +-/* USB General control register 2 (UGCTRL2) */ +-#define USBHS_UGCTRL2_USB2SEL 0x80000000 +-#define USBHS_UGCTRL2_USB2SEL_PCI 0x00000000 +-#define USBHS_UGCTRL2_USB2SEL_USB30 0x80000000 +-#define USBHS_UGCTRL2_USB0SEL 0x00000030 +-#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010 +-#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030 +- +-/* USB General status register (UGSTS) */ +-#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */ +- +-#define PHYS_PER_CHANNEL 2 +- +-struct rcar_gen2_phy { +- struct phy *phy; +- struct rcar_gen2_channel *channel; +- int number; +- u32 select_value; +-}; +- +-struct rcar_gen2_channel { +- struct device_node *of_node; +- struct rcar_gen2_phy_driver *drv; +- struct rcar_gen2_phy phys[PHYS_PER_CHANNEL]; +- int selected_phy; +- u32 select_mask; +-}; +- +-struct rcar_gen2_phy_driver { +- void __iomem *base; +- struct clk *clk; +- spinlock_t lock; +- int num_channels; +- struct rcar_gen2_channel *channels; +-}; +- +-static int rcar_gen2_phy_init(struct phy *p) +-{ +- struct rcar_gen2_phy *phy = phy_get_drvdata(p); +- struct rcar_gen2_channel *channel = phy->channel; +- struct rcar_gen2_phy_driver *drv = channel->drv; +- unsigned long flags; +- u32 ugctrl2; +- +- /* +- * Try to acquire exclusive access to PHY. The first driver calling +- * phy_init() on a given channel wins, and all attempts to use another +- * PHY on this channel will fail until phy_exit() is called by the first +- * driver. Achieving this with cmpxcgh() should be SMP-safe. +- */ +- if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1) +- return -EBUSY; +- +- clk_prepare_enable(drv->clk); +- +- spin_lock_irqsave(&drv->lock, flags); +- ugctrl2 = readl(drv->base + USBHS_UGCTRL2); +- ugctrl2 &= ~channel->select_mask; +- ugctrl2 |= phy->select_value; +- writel(ugctrl2, drv->base + USBHS_UGCTRL2); +- spin_unlock_irqrestore(&drv->lock, flags); +- return 0; +-} +- +-static int rcar_gen2_phy_exit(struct phy *p) +-{ +- struct rcar_gen2_phy *phy = phy_get_drvdata(p); +- struct rcar_gen2_channel *channel = phy->channel; +- +- clk_disable_unprepare(channel->drv->clk); +- +- channel->selected_phy = -1; +- +- return 0; +-} +- +-static int rcar_gen2_phy_power_on(struct phy *p) +-{ +- struct rcar_gen2_phy *phy = phy_get_drvdata(p); +- struct rcar_gen2_phy_driver *drv = phy->channel->drv; +- void __iomem *base = drv->base; +- unsigned long flags; +- u32 value; +- int err = 0, i; +- +- /* Skip if it's not USBHS */ +- if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) +- return 0; +- +- spin_lock_irqsave(&drv->lock, flags); +- +- /* Power on USBHS PHY */ +- value = readl(base + USBHS_UGCTRL); +- value &= ~USBHS_UGCTRL_PLLRESET; +- writel(value, base + USBHS_UGCTRL); +- +- value = readw(base + USBHS_LPSTS); +- value |= USBHS_LPSTS_SUSPM; +- writew(value, base + USBHS_LPSTS); +- +- for (i = 0; i < 20; i++) { +- value = readl(base + USBHS_UGSTS); +- if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { +- value = readl(base + USBHS_UGCTRL); +- value |= USBHS_UGCTRL_CONNECT; +- writel(value, base + USBHS_UGCTRL); +- goto out; +- } +- udelay(1); +- } +- +- /* Timed out waiting for the PLL lock */ +- err = -ETIMEDOUT; +- +-out: +- spin_unlock_irqrestore(&drv->lock, flags); +- +- return err; +-} +- +-static int rcar_gen2_phy_power_off(struct phy *p) +-{ +- struct rcar_gen2_phy *phy = phy_get_drvdata(p); +- struct rcar_gen2_phy_driver *drv = phy->channel->drv; +- void __iomem *base = drv->base; +- unsigned long flags; +- u32 value; +- +- /* Skip if it's not USBHS */ +- if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) +- return 0; +- +- spin_lock_irqsave(&drv->lock, flags); +- +- /* Power off USBHS PHY */ +- value = readl(base + USBHS_UGCTRL); +- value &= ~USBHS_UGCTRL_CONNECT; +- writel(value, base + USBHS_UGCTRL); +- +- value = readw(base + USBHS_LPSTS); +- value &= ~USBHS_LPSTS_SUSPM; +- writew(value, base + USBHS_LPSTS); +- +- value = readl(base + USBHS_UGCTRL); +- value |= USBHS_UGCTRL_PLLRESET; +- writel(value, base + USBHS_UGCTRL); +- +- spin_unlock_irqrestore(&drv->lock, flags); +- +- return 0; +-} +- +-static const struct phy_ops rcar_gen2_phy_ops = { +- .init = rcar_gen2_phy_init, +- .exit = rcar_gen2_phy_exit, +- .power_on = rcar_gen2_phy_power_on, +- .power_off = rcar_gen2_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static const struct of_device_id rcar_gen2_phy_match_table[] = { +- { .compatible = "renesas,usb-phy-r8a7790" }, +- { .compatible = "renesas,usb-phy-r8a7791" }, +- { .compatible = "renesas,usb-phy-r8a7794" }, +- { .compatible = "renesas,rcar-gen2-usb-phy" }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); +- +-static struct phy *rcar_gen2_phy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct rcar_gen2_phy_driver *drv; +- struct device_node *np = args->np; +- int i; +- +- drv = dev_get_drvdata(dev); +- if (!drv) +- return ERR_PTR(-EINVAL); +- +- for (i = 0; i < drv->num_channels; i++) { +- if (np == drv->channels[i].of_node) +- break; +- } +- +- if (i >= drv->num_channels || args->args[0] >= 2) +- return ERR_PTR(-ENODEV); +- +- return drv->channels[i].phys[args->args[0]].phy; +-} +- +-static const u32 select_mask[] = { +- [0] = USBHS_UGCTRL2_USB0SEL, +- [2] = USBHS_UGCTRL2_USB2SEL, +-}; +- +-static const u32 select_value[][PHYS_PER_CHANNEL] = { +- [0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB }, +- [2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 }, +-}; +- +-static int rcar_gen2_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct rcar_gen2_phy_driver *drv; +- struct phy_provider *provider; +- struct device_node *np; +- struct resource *res; +- void __iomem *base; +- struct clk *clk; +- int i = 0; +- +- if (!dev->of_node) { +- dev_err(dev, +- "This driver is required to be instantiated from device tree\n"); +- return -EINVAL; +- } +- +- clk = devm_clk_get(dev, "usbhs"); +- if (IS_ERR(clk)) { +- dev_err(dev, "Can't get USBHS clock\n"); +- return PTR_ERR(clk); +- } +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(dev, res); +- if (IS_ERR(base)) +- return PTR_ERR(base); +- +- drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); +- if (!drv) +- return -ENOMEM; +- +- spin_lock_init(&drv->lock); +- +- drv->clk = clk; +- drv->base = base; +- +- drv->num_channels = of_get_child_count(dev->of_node); +- drv->channels = devm_kcalloc(dev, drv->num_channels, +- sizeof(struct rcar_gen2_channel), +- GFP_KERNEL); +- if (!drv->channels) +- return -ENOMEM; +- +- for_each_child_of_node(dev->of_node, np) { +- struct rcar_gen2_channel *channel = drv->channels + i; +- u32 channel_num; +- int error, n; +- +- channel->of_node = np; +- channel->drv = drv; +- channel->selected_phy = -1; +- +- error = of_property_read_u32(np, "reg", &channel_num); +- if (error || channel_num > 2) { +- dev_err(dev, "Invalid \"reg\" property\n"); +- return error; +- } +- channel->select_mask = select_mask[channel_num]; +- +- for (n = 0; n < PHYS_PER_CHANNEL; n++) { +- struct rcar_gen2_phy *phy = &channel->phys[n]; +- +- phy->channel = channel; +- phy->number = n; +- phy->select_value = select_value[channel_num][n]; +- +- phy->phy = devm_phy_create(dev, NULL, +- &rcar_gen2_phy_ops); +- if (IS_ERR(phy->phy)) { +- dev_err(dev, "Failed to create PHY\n"); +- return PTR_ERR(phy->phy); +- } +- phy_set_drvdata(phy->phy, phy); +- } +- +- i++; +- } +- +- provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate); +- if (IS_ERR(provider)) { +- dev_err(dev, "Failed to register PHY provider\n"); +- return PTR_ERR(provider); +- } +- +- dev_set_drvdata(dev, drv); +- +- return 0; +-} +- +-static struct platform_driver rcar_gen2_phy_driver = { +- .driver = { +- .name = "phy_rcar_gen2", +- .of_match_table = rcar_gen2_phy_match_table, +- }, +- .probe = rcar_gen2_phy_probe, +-}; +- +-module_platform_driver(rcar_gen2_phy_driver); +- +-MODULE_LICENSE("GPL v2"); +-MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY"); +-MODULE_AUTHOR("Sergei Shtylyov "); +diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c +deleted file mode 100644 +index 54c34298a000..000000000000 +--- a/drivers/phy/phy-rcar-gen3-usb2.c ++++ /dev/null +@@ -1,507 +0,0 @@ +-/* +- * Renesas R-Car Gen3 for USB2.0 PHY driver +- * +- * Copyright (C) 2015 Renesas Electronics Corporation +- * +- * This is based on the phy-rcar-gen2 driver: +- * Copyright (C) 2014 Renesas Solutions Corp. +- * Copyright (C) 2014 Cogent Embedded, Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/******* USB2.0 Host registers (original offset is +0x200) *******/ +-#define USB2_INT_ENABLE 0x000 +-#define USB2_USBCTR 0x00c +-#define USB2_SPD_RSM_TIMSET 0x10c +-#define USB2_OC_TIMSET 0x110 +-#define USB2_COMMCTRL 0x600 +-#define USB2_OBINTSTA 0x604 +-#define USB2_OBINTEN 0x608 +-#define USB2_VBCTRL 0x60c +-#define USB2_LINECTRL1 0x610 +-#define USB2_ADPCTRL 0x630 +- +-/* INT_ENABLE */ +-#define USB2_INT_ENABLE_UCOM_INTEN BIT(3) +-#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) +-#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) +-#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \ +- USB2_INT_ENABLE_USBH_INTB_EN | \ +- USB2_INT_ENABLE_USBH_INTA_EN) +- +-/* USBCTR */ +-#define USB2_USBCTR_DIRPD BIT(2) +-#define USB2_USBCTR_PLL_RST BIT(1) +- +-/* SPD_RSM_TIMSET */ +-#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b +- +-/* OC_TIMSET */ +-#define USB2_OC_TIMSET_INIT 0x000209ab +- +-/* COMMCTRL */ +-#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */ +- +-/* OBINTSTA and OBINTEN */ +-#define USB2_OBINT_SESSVLDCHG BIT(12) +-#define USB2_OBINT_IDDIGCHG BIT(11) +-#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \ +- USB2_OBINT_IDDIGCHG) +- +-/* VBCTRL */ +-#define USB2_VBCTRL_DRVVBUSSEL BIT(8) +- +-/* LINECTRL1 */ +-#define USB2_LINECTRL1_DPRPD_EN BIT(19) +-#define USB2_LINECTRL1_DP_RPD BIT(18) +-#define USB2_LINECTRL1_DMRPD_EN BIT(17) +-#define USB2_LINECTRL1_DM_RPD BIT(16) +-#define USB2_LINECTRL1_OPMODE_NODRV BIT(6) +- +-/* ADPCTRL */ +-#define USB2_ADPCTRL_OTGSESSVLD BIT(20) +-#define USB2_ADPCTRL_IDDIG BIT(19) +-#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ +-#define USB2_ADPCTRL_DRVVBUS BIT(4) +- +-struct rcar_gen3_chan { +- void __iomem *base; +- struct extcon_dev *extcon; +- struct phy *phy; +- struct regulator *vbus; +- struct work_struct work; +- bool extcon_host; +- bool has_otg; +-}; +- +-static void rcar_gen3_phy_usb2_work(struct work_struct *work) +-{ +- struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan, +- work); +- +- if (ch->extcon_host) { +- extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, true); +- extcon_set_state_sync(ch->extcon, EXTCON_USB, false); +- } else { +- extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, false); +- extcon_set_state_sync(ch->extcon, EXTCON_USB, true); +- } +-} +- +-static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) +-{ +- void __iomem *usb2_base = ch->base; +- u32 val = readl(usb2_base + USB2_COMMCTRL); +- +- dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host); +- if (host) +- val &= ~USB2_COMMCTRL_OTG_PERI; +- else +- val |= USB2_COMMCTRL_OTG_PERI; +- writel(val, usb2_base + USB2_COMMCTRL); +-} +- +-static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) +-{ +- void __iomem *usb2_base = ch->base; +- u32 val = readl(usb2_base + USB2_LINECTRL1); +- +- dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm); +- val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); +- if (dp) +- val |= USB2_LINECTRL1_DP_RPD; +- if (dm) +- val |= USB2_LINECTRL1_DM_RPD; +- writel(val, usb2_base + USB2_LINECTRL1); +-} +- +-static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) +-{ +- void __iomem *usb2_base = ch->base; +- u32 val = readl(usb2_base + USB2_ADPCTRL); +- +- dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus); +- if (vbus) +- val |= USB2_ADPCTRL_DRVVBUS; +- else +- val &= ~USB2_ADPCTRL_DRVVBUS; +- writel(val, usb2_base + USB2_ADPCTRL); +-} +- +-static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch) +-{ +- rcar_gen3_set_linectrl(ch, 1, 1); +- rcar_gen3_set_host_mode(ch, 1); +- rcar_gen3_enable_vbus_ctrl(ch, 1); +- +- ch->extcon_host = true; +- schedule_work(&ch->work); +-} +- +-static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) +-{ +- rcar_gen3_set_linectrl(ch, 0, 1); +- rcar_gen3_set_host_mode(ch, 0); +- rcar_gen3_enable_vbus_ctrl(ch, 0); +- +- ch->extcon_host = false; +- schedule_work(&ch->work); +-} +- +-static void rcar_gen3_init_for_b_host(struct rcar_gen3_chan *ch) +-{ +- void __iomem *usb2_base = ch->base; +- u32 val; +- +- val = readl(usb2_base + USB2_LINECTRL1); +- writel(val | USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); +- +- rcar_gen3_set_linectrl(ch, 1, 1); +- rcar_gen3_set_host_mode(ch, 1); +- rcar_gen3_enable_vbus_ctrl(ch, 0); +- +- val = readl(usb2_base + USB2_LINECTRL1); +- writel(val & ~USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); +-} +- +-static void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch) +-{ +- rcar_gen3_set_linectrl(ch, 0, 1); +- rcar_gen3_set_host_mode(ch, 0); +- rcar_gen3_enable_vbus_ctrl(ch, 1); +-} +- +-static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) +-{ +- void __iomem *usb2_base = ch->base; +- u32 val; +- +- val = readl(usb2_base + USB2_OBINTEN); +- writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); +- +- rcar_gen3_enable_vbus_ctrl(ch, 0); +- rcar_gen3_init_for_host(ch); +- +- writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); +-} +- +-static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) +-{ +- return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); +-} +- +-static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) +-{ +- if (!rcar_gen3_check_id(ch)) +- rcar_gen3_init_for_host(ch); +- else +- rcar_gen3_init_for_peri(ch); +-} +- +-static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) +-{ +- return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); +-} +- +-static ssize_t role_store(struct device *dev, struct device_attribute *attr, +- const char *buf, size_t count) +-{ +- struct rcar_gen3_chan *ch = dev_get_drvdata(dev); +- bool is_b_device, is_host, new_mode_is_host; +- +- if (!ch->has_otg || !ch->phy->init_count) +- return -EIO; +- +- /* +- * is_b_device: true is B-Device. false is A-Device. +- * If {new_mode_}is_host: true is Host mode. false is Peripheral mode. +- */ +- is_b_device = rcar_gen3_check_id(ch); +- is_host = rcar_gen3_is_host(ch); +- if (!strncmp(buf, "host", strlen("host"))) +- new_mode_is_host = true; +- else if (!strncmp(buf, "peripheral", strlen("peripheral"))) +- new_mode_is_host = false; +- else +- return -EINVAL; +- +- /* If current and new mode is the same, this returns the error */ +- if (is_host == new_mode_is_host) +- return -EINVAL; +- +- if (new_mode_is_host) { /* And is_host must be false */ +- if (!is_b_device) /* A-Peripheral */ +- rcar_gen3_init_from_a_peri_to_a_host(ch); +- else /* B-Peripheral */ +- rcar_gen3_init_for_b_host(ch); +- } else { /* And is_host must be true */ +- if (!is_b_device) /* A-Host */ +- rcar_gen3_init_for_a_peri(ch); +- else /* B-Host */ +- rcar_gen3_init_for_peri(ch); +- } +- +- return count; +-} +- +-static ssize_t role_show(struct device *dev, struct device_attribute *attr, +- char *buf) +-{ +- struct rcar_gen3_chan *ch = dev_get_drvdata(dev); +- +- if (!ch->has_otg || !ch->phy->init_count) +- return -EIO; +- +- return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : +- "peripheral"); +-} +-static DEVICE_ATTR_RW(role); +- +-static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) +-{ +- void __iomem *usb2_base = ch->base; +- u32 val; +- +- val = readl(usb2_base + USB2_VBCTRL); +- writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); +- writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); +- val = readl(usb2_base + USB2_OBINTEN); +- writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); +- val = readl(usb2_base + USB2_ADPCTRL); +- writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); +- val = readl(usb2_base + USB2_LINECTRL1); +- rcar_gen3_set_linectrl(ch, 0, 0); +- writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN, +- usb2_base + USB2_LINECTRL1); +- +- rcar_gen3_device_recognition(ch); +-} +- +-static int rcar_gen3_phy_usb2_init(struct phy *p) +-{ +- struct rcar_gen3_chan *channel = phy_get_drvdata(p); +- void __iomem *usb2_base = channel->base; +- +- /* Initialize USB2 part */ +- writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); +- writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); +- writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); +- +- /* Initialize otg part */ +- if (channel->has_otg) +- rcar_gen3_init_otg(channel); +- +- return 0; +-} +- +-static int rcar_gen3_phy_usb2_exit(struct phy *p) +-{ +- struct rcar_gen3_chan *channel = phy_get_drvdata(p); +- +- writel(0, channel->base + USB2_INT_ENABLE); +- +- return 0; +-} +- +-static int rcar_gen3_phy_usb2_power_on(struct phy *p) +-{ +- struct rcar_gen3_chan *channel = phy_get_drvdata(p); +- void __iomem *usb2_base = channel->base; +- u32 val; +- int ret; +- +- if (channel->vbus) { +- ret = regulator_enable(channel->vbus); +- if (ret) +- return ret; +- } +- +- val = readl(usb2_base + USB2_USBCTR); +- val |= USB2_USBCTR_PLL_RST; +- writel(val, usb2_base + USB2_USBCTR); +- val &= ~USB2_USBCTR_PLL_RST; +- writel(val, usb2_base + USB2_USBCTR); +- +- return 0; +-} +- +-static int rcar_gen3_phy_usb2_power_off(struct phy *p) +-{ +- struct rcar_gen3_chan *channel = phy_get_drvdata(p); +- int ret = 0; +- +- if (channel->vbus) +- ret = regulator_disable(channel->vbus); +- +- return ret; +-} +- +-static const struct phy_ops rcar_gen3_phy_usb2_ops = { +- .init = rcar_gen3_phy_usb2_init, +- .exit = rcar_gen3_phy_usb2_exit, +- .power_on = rcar_gen3_phy_usb2_power_on, +- .power_off = rcar_gen3_phy_usb2_power_off, +- .owner = THIS_MODULE, +-}; +- +-static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) +-{ +- struct rcar_gen3_chan *ch = _ch; +- void __iomem *usb2_base = ch->base; +- u32 status = readl(usb2_base + USB2_OBINTSTA); +- irqreturn_t ret = IRQ_NONE; +- +- if (status & USB2_OBINT_BITS) { +- dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status); +- writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); +- rcar_gen3_device_recognition(ch); +- ret = IRQ_HANDLED; +- } +- +- return ret; +-} +- +-static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { +- { .compatible = "renesas,usb2-phy-r8a7795" }, +- { .compatible = "renesas,usb2-phy-r8a7796" }, +- { .compatible = "renesas,rcar-gen3-usb2-phy" }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); +- +-static const unsigned int rcar_gen3_phy_cable[] = { +- EXTCON_USB, +- EXTCON_USB_HOST, +- EXTCON_NONE, +-}; +- +-static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct rcar_gen3_chan *channel; +- struct phy_provider *provider; +- struct resource *res; +- int irq, ret = 0; +- +- if (!dev->of_node) { +- dev_err(dev, "This driver needs device tree\n"); +- return -EINVAL; +- } +- +- channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); +- if (!channel) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- channel->base = devm_ioremap_resource(dev, res); +- if (IS_ERR(channel->base)) +- return PTR_ERR(channel->base); +- +- /* call request_irq for OTG */ +- irq = platform_get_irq(pdev, 0); +- if (irq >= 0) { +- int ret; +- +- INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); +- irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, +- IRQF_SHARED, dev_name(dev), channel); +- if (irq < 0) +- dev_err(dev, "No irq handler (%d)\n", irq); +- channel->has_otg = true; +- channel->extcon = devm_extcon_dev_allocate(dev, +- rcar_gen3_phy_cable); +- if (IS_ERR(channel->extcon)) +- return PTR_ERR(channel->extcon); +- +- ret = devm_extcon_dev_register(dev, channel->extcon); +- if (ret < 0) { +- dev_err(dev, "Failed to register extcon\n"); +- return ret; +- } +- } +- +- /* +- * devm_phy_create() will call pm_runtime_enable(&phy->dev); +- * And then, phy-core will manage runtime pm for this device. +- */ +- pm_runtime_enable(dev); +- channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops); +- if (IS_ERR(channel->phy)) { +- dev_err(dev, "Failed to create USB2 PHY\n"); +- ret = PTR_ERR(channel->phy); +- goto error; +- } +- +- channel->vbus = devm_regulator_get_optional(dev, "vbus"); +- if (IS_ERR(channel->vbus)) { +- if (PTR_ERR(channel->vbus) == -EPROBE_DEFER) { +- ret = PTR_ERR(channel->vbus); +- goto error; +- } +- channel->vbus = NULL; +- } +- +- platform_set_drvdata(pdev, channel); +- phy_set_drvdata(channel->phy, channel); +- +- provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (IS_ERR(provider)) { +- dev_err(dev, "Failed to register PHY provider\n"); +- ret = PTR_ERR(provider); +- goto error; +- } else if (channel->has_otg) { +- int ret; +- +- ret = device_create_file(dev, &dev_attr_role); +- if (ret < 0) +- goto error; +- } +- +- return 0; +- +-error: +- pm_runtime_disable(dev); +- +- return ret; +-} +- +-static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) +-{ +- struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); +- +- if (channel->has_otg) +- device_remove_file(&pdev->dev, &dev_attr_role); +- +- pm_runtime_disable(&pdev->dev); +- +- return 0; +-}; +- +-static struct platform_driver rcar_gen3_phy_usb2_driver = { +- .driver = { +- .name = "phy_rcar_gen3_usb2", +- .of_match_table = rcar_gen3_phy_usb2_match_table, +- }, +- .probe = rcar_gen3_phy_usb2_probe, +- .remove = rcar_gen3_phy_usb2_remove, +-}; +-module_platform_driver(rcar_gen3_phy_usb2_driver); +- +-MODULE_LICENSE("GPL v2"); +-MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY"); +-MODULE_AUTHOR("Yoshihiro Shimoda "); +diff --git a/drivers/phy/phy-rockchip-dp.c b/drivers/phy/phy-rockchip-dp.c +deleted file mode 100644 +index 8b267a746576..000000000000 +--- a/drivers/phy/phy-rockchip-dp.c ++++ /dev/null +@@ -1,154 +0,0 @@ +-/* +- * Rockchip DP PHY driver +- * +- * Copyright (C) 2016 FuZhou Rockchip Co., Ltd. +- * Author: Yakir Yang +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define GRF_SOC_CON12 0x0274 +- +-#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK BIT(20) +-#define GRF_EDP_REF_CLK_SEL_INTER BIT(4) +- +-#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK BIT(21) +-#define GRF_EDP_PHY_SIDDQ_ON 0 +-#define GRF_EDP_PHY_SIDDQ_OFF BIT(5) +- +-struct rockchip_dp_phy { +- struct device *dev; +- struct regmap *grf; +- struct clk *phy_24m; +-}; +- +-static int rockchip_set_phy_state(struct phy *phy, bool enable) +-{ +- struct rockchip_dp_phy *dp = phy_get_drvdata(phy); +- int ret; +- +- if (enable) { +- ret = regmap_write(dp->grf, GRF_SOC_CON12, +- GRF_EDP_PHY_SIDDQ_HIWORD_MASK | +- GRF_EDP_PHY_SIDDQ_ON); +- if (ret < 0) { +- dev_err(dp->dev, "Can't enable PHY power %d\n", ret); +- return ret; +- } +- +- ret = clk_prepare_enable(dp->phy_24m); +- } else { +- clk_disable_unprepare(dp->phy_24m); +- +- ret = regmap_write(dp->grf, GRF_SOC_CON12, +- GRF_EDP_PHY_SIDDQ_HIWORD_MASK | +- GRF_EDP_PHY_SIDDQ_OFF); +- } +- +- return ret; +-} +- +-static int rockchip_dp_phy_power_on(struct phy *phy) +-{ +- return rockchip_set_phy_state(phy, true); +-} +- +-static int rockchip_dp_phy_power_off(struct phy *phy) +-{ +- return rockchip_set_phy_state(phy, false); +-} +- +-static const struct phy_ops rockchip_dp_phy_ops = { +- .power_on = rockchip_dp_phy_power_on, +- .power_off = rockchip_dp_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int rockchip_dp_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct device_node *np = dev->of_node; +- struct phy_provider *phy_provider; +- struct rockchip_dp_phy *dp; +- struct phy *phy; +- int ret; +- +- if (!np) +- return -ENODEV; +- +- if (!dev->parent || !dev->parent->of_node) +- return -ENODEV; +- +- dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); +- if (!dp) +- return -ENOMEM; +- +- dp->dev = dev; +- +- dp->phy_24m = devm_clk_get(dev, "24m"); +- if (IS_ERR(dp->phy_24m)) { +- dev_err(dev, "cannot get clock 24m\n"); +- return PTR_ERR(dp->phy_24m); +- } +- +- ret = clk_set_rate(dp->phy_24m, 24000000); +- if (ret < 0) { +- dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret); +- return ret; +- } +- +- dp->grf = syscon_node_to_regmap(dev->parent->of_node); +- if (IS_ERR(dp->grf)) { +- dev_err(dev, "rk3288-dp needs the General Register Files syscon\n"); +- return PTR_ERR(dp->grf); +- } +- +- ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER | +- GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK); +- if (ret != 0) { +- dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret); +- return ret; +- } +- +- phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create phy\n"); +- return PTR_ERR(phy); +- } +- phy_set_drvdata(phy, dp); +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id rockchip_dp_phy_dt_ids[] = { +- { .compatible = "rockchip,rk3288-dp-phy" }, +- {} +-}; +- +-MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids); +- +-static struct platform_driver rockchip_dp_phy_driver = { +- .probe = rockchip_dp_phy_probe, +- .driver = { +- .name = "rockchip-dp-phy", +- .of_match_table = rockchip_dp_phy_dt_ids, +- }, +-}; +- +-module_platform_driver(rockchip_dp_phy_driver); +- +-MODULE_AUTHOR("Yakir Yang "); +-MODULE_DESCRIPTION("Rockchip DP PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c +deleted file mode 100644 +index f1b24f18e9b2..000000000000 +--- a/drivers/phy/phy-rockchip-emmc.c ++++ /dev/null +@@ -1,383 +0,0 @@ +-/* +- * Rockchip emmc PHY driver +- * +- * Copyright (C) 2016 Shawn Lin +- * Copyright (C) 2016 ROCKCHIP, Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* +- * The higher 16-bit of this register is used for write protection +- * only if BIT(x + 16) set to 1 the BIT(x) can be written. +- */ +-#define HIWORD_UPDATE(val, mask, shift) \ +- ((val) << (shift) | (mask) << ((shift) + 16)) +- +-/* Register definition */ +-#define GRF_EMMCPHY_CON0 0x0 +-#define GRF_EMMCPHY_CON1 0x4 +-#define GRF_EMMCPHY_CON2 0x8 +-#define GRF_EMMCPHY_CON3 0xc +-#define GRF_EMMCPHY_CON4 0x10 +-#define GRF_EMMCPHY_CON5 0x14 +-#define GRF_EMMCPHY_CON6 0x18 +-#define GRF_EMMCPHY_STATUS 0x20 +- +-#define PHYCTRL_PDB_MASK 0x1 +-#define PHYCTRL_PDB_SHIFT 0x0 +-#define PHYCTRL_PDB_PWR_ON 0x1 +-#define PHYCTRL_PDB_PWR_OFF 0x0 +-#define PHYCTRL_ENDLL_MASK 0x1 +-#define PHYCTRL_ENDLL_SHIFT 0x1 +-#define PHYCTRL_ENDLL_ENABLE 0x1 +-#define PHYCTRL_ENDLL_DISABLE 0x0 +-#define PHYCTRL_CALDONE_MASK 0x1 +-#define PHYCTRL_CALDONE_SHIFT 0x6 +-#define PHYCTRL_CALDONE_DONE 0x1 +-#define PHYCTRL_CALDONE_GOING 0x0 +-#define PHYCTRL_DLLRDY_MASK 0x1 +-#define PHYCTRL_DLLRDY_SHIFT 0x5 +-#define PHYCTRL_DLLRDY_DONE 0x1 +-#define PHYCTRL_DLLRDY_GOING 0x0 +-#define PHYCTRL_FREQSEL_200M 0x0 +-#define PHYCTRL_FREQSEL_50M 0x1 +-#define PHYCTRL_FREQSEL_100M 0x2 +-#define PHYCTRL_FREQSEL_150M 0x3 +-#define PHYCTRL_FREQSEL_MASK 0x3 +-#define PHYCTRL_FREQSEL_SHIFT 0xc +-#define PHYCTRL_DR_MASK 0x7 +-#define PHYCTRL_DR_SHIFT 0x4 +-#define PHYCTRL_DR_50OHM 0x0 +-#define PHYCTRL_DR_33OHM 0x1 +-#define PHYCTRL_DR_66OHM 0x2 +-#define PHYCTRL_DR_100OHM 0x3 +-#define PHYCTRL_DR_40OHM 0x4 +-#define PHYCTRL_OTAPDLYENA 0x1 +-#define PHYCTRL_OTAPDLYENA_MASK 0x1 +-#define PHYCTRL_OTAPDLYENA_SHIFT 0xb +-#define PHYCTRL_OTAPDLYSEL_MASK 0xf +-#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 +- +-struct rockchip_emmc_phy { +- unsigned int reg_offset; +- struct regmap *reg_base; +- struct clk *emmcclk; +-}; +- +-static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) +-{ +- struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); +- unsigned int caldone; +- unsigned int dllrdy; +- unsigned int freqsel = PHYCTRL_FREQSEL_200M; +- unsigned long rate; +- unsigned long timeout; +- +- /* +- * Keep phyctrl_pdb and phyctrl_endll low to allow +- * initialization of CALIO state M/C DFFs +- */ +- regmap_write(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_CON6, +- HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, +- PHYCTRL_PDB_MASK, +- PHYCTRL_PDB_SHIFT)); +- regmap_write(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_CON6, +- HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, +- PHYCTRL_ENDLL_MASK, +- PHYCTRL_ENDLL_SHIFT)); +- +- /* Already finish power_off above */ +- if (on_off == PHYCTRL_PDB_PWR_OFF) +- return 0; +- +- rate = clk_get_rate(rk_phy->emmcclk); +- +- if (rate != 0) { +- unsigned long ideal_rate; +- unsigned long diff; +- +- switch (rate) { +- case 1 ... 74999999: +- ideal_rate = 50000000; +- freqsel = PHYCTRL_FREQSEL_50M; +- break; +- case 75000000 ... 124999999: +- ideal_rate = 100000000; +- freqsel = PHYCTRL_FREQSEL_100M; +- break; +- case 125000000 ... 174999999: +- ideal_rate = 150000000; +- freqsel = PHYCTRL_FREQSEL_150M; +- break; +- default: +- ideal_rate = 200000000; +- break; +- } +- +- diff = (rate > ideal_rate) ? +- rate - ideal_rate : ideal_rate - rate; +- +- /* +- * In order for tuning delays to be accurate we need to be +- * pretty spot on for the DLL range, so warn if we're too +- * far off. Also warn if we're above the 200 MHz max. Don't +- * warn for really slow rates since we won't be tuning then. +- */ +- if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) +- dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); +- } +- +- /* +- * According to the user manual, calpad calibration +- * cycle takes more than 2us without the minimal recommended +- * value, so we may need a little margin here +- */ +- udelay(3); +- regmap_write(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_CON6, +- HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON, +- PHYCTRL_PDB_MASK, +- PHYCTRL_PDB_SHIFT)); +- +- /* +- * According to the user manual, it asks driver to +- * wait 5us for calpad busy trimming +- */ +- udelay(5); +- regmap_read(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_STATUS, +- &caldone); +- caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; +- if (caldone != PHYCTRL_CALDONE_DONE) { +- pr_err("rockchip_emmc_phy_power: caldone timeout.\n"); +- return -ETIMEDOUT; +- } +- +- /* Set the frequency of the DLL operation */ +- regmap_write(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_CON0, +- HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, +- PHYCTRL_FREQSEL_SHIFT)); +- +- /* Turn on the DLL */ +- regmap_write(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_CON6, +- HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, +- PHYCTRL_ENDLL_MASK, +- PHYCTRL_ENDLL_SHIFT)); +- +- /* +- * We turned on the DLL even though the rate was 0 because we the +- * clock might be turned on later. ...but we can't wait for the DLL +- * to lock when the rate is 0 because it will never lock with no +- * input clock. +- * +- * Technically we should be checking the lock later when the clock +- * is turned on, but for now we won't. +- */ +- if (rate == 0) +- return 0; +- +- /* +- * After enabling analog DLL circuits docs say that we need 10.2 us if +- * our source clock is at 50 MHz and that lock time scales linearly +- * with clock speed. If we are powering on the PHY and the card clock +- * is super slow (like 100 kHZ) this could take as long as 5.1 ms as +- * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms +- * Hopefully we won't be running at 100 kHz, but we should still make +- * sure we wait long enough. +- * +- * NOTE: There appear to be corner cases where the DLL seems to take +- * extra long to lock for reasons that aren't understood. In some +- * extreme cases we've seen it take up to over 10ms (!). We'll be +- * generous and give it 50ms. We still busy wait here because: +- * - In most cases it should be super fast. +- * - This is not called lots during normal operation so it shouldn't +- * be a power or performance problem to busy wait. We expect it +- * only at boot / resume. In both cases, eMMC is probably on the +- * critical path so busy waiting a little extra time should be OK. +- */ +- timeout = jiffies + msecs_to_jiffies(50); +- do { +- udelay(1); +- +- regmap_read(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_STATUS, +- &dllrdy); +- dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; +- if (dllrdy == PHYCTRL_DLLRDY_DONE) +- break; +- } while (!time_after(jiffies, timeout)); +- +- if (dllrdy != PHYCTRL_DLLRDY_DONE) { +- pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); +- return -ETIMEDOUT; +- } +- +- return 0; +-} +- +-static int rockchip_emmc_phy_init(struct phy *phy) +-{ +- struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); +- int ret = 0; +- +- /* +- * We purposely get the clock here and not in probe to avoid the +- * circular dependency problem. We expect: +- * - PHY driver to probe +- * - SDHCI driver to start probe +- * - SDHCI driver to register it's clock +- * - SDHCI driver to get the PHY +- * - SDHCI driver to init the PHY +- * +- * The clock is optional, so upon any error we just set to NULL. +- * +- * NOTE: we don't do anything special for EPROBE_DEFER here. Given the +- * above expected use case, EPROBE_DEFER isn't sensible to expect, so +- * it's just like any other error. +- */ +- rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); +- if (IS_ERR(rk_phy->emmcclk)) { +- dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); +- rk_phy->emmcclk = NULL; +- } +- +- return ret; +-} +- +-static int rockchip_emmc_phy_exit(struct phy *phy) +-{ +- struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); +- +- clk_put(rk_phy->emmcclk); +- +- return 0; +-} +- +-static int rockchip_emmc_phy_power_off(struct phy *phy) +-{ +- /* Power down emmc phy analog blocks */ +- return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); +-} +- +-static int rockchip_emmc_phy_power_on(struct phy *phy) +-{ +- struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); +- +- /* Drive impedance: 50 Ohm */ +- regmap_write(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_CON6, +- HIWORD_UPDATE(PHYCTRL_DR_50OHM, +- PHYCTRL_DR_MASK, +- PHYCTRL_DR_SHIFT)); +- +- /* Output tap delay: enable */ +- regmap_write(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_CON0, +- HIWORD_UPDATE(PHYCTRL_OTAPDLYENA, +- PHYCTRL_OTAPDLYENA_MASK, +- PHYCTRL_OTAPDLYENA_SHIFT)); +- +- /* Output tap delay */ +- regmap_write(rk_phy->reg_base, +- rk_phy->reg_offset + GRF_EMMCPHY_CON0, +- HIWORD_UPDATE(4, +- PHYCTRL_OTAPDLYSEL_MASK, +- PHYCTRL_OTAPDLYSEL_SHIFT)); +- +- /* Power up emmc phy analog blocks */ +- return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); +-} +- +-static const struct phy_ops ops = { +- .init = rockchip_emmc_phy_init, +- .exit = rockchip_emmc_phy_exit, +- .power_on = rockchip_emmc_phy_power_on, +- .power_off = rockchip_emmc_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int rockchip_emmc_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct rockchip_emmc_phy *rk_phy; +- struct phy *generic_phy; +- struct phy_provider *phy_provider; +- struct regmap *grf; +- unsigned int reg_offset; +- +- if (!dev->parent || !dev->parent->of_node) +- return -ENODEV; +- +- grf = syscon_node_to_regmap(dev->parent->of_node); +- if (IS_ERR(grf)) { +- dev_err(dev, "Missing rockchip,grf property\n"); +- return PTR_ERR(grf); +- } +- +- rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); +- if (!rk_phy) +- return -ENOMEM; +- +- if (of_property_read_u32(dev->of_node, "reg", ®_offset)) { +- dev_err(dev, "missing reg property in node %s\n", +- dev->of_node->name); +- return -EINVAL; +- } +- +- rk_phy->reg_offset = reg_offset; +- rk_phy->reg_base = grf; +- +- generic_phy = devm_phy_create(dev, dev->of_node, &ops); +- if (IS_ERR(generic_phy)) { +- dev_err(dev, "failed to create PHY\n"); +- return PTR_ERR(generic_phy); +- } +- +- phy_set_drvdata(generic_phy, rk_phy); +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id rockchip_emmc_phy_dt_ids[] = { +- { .compatible = "rockchip,rk3399-emmc-phy" }, +- {} +-}; +- +-MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids); +- +-static struct platform_driver rockchip_emmc_driver = { +- .probe = rockchip_emmc_phy_probe, +- .driver = { +- .name = "rockchip-emmc-phy", +- .of_match_table = rockchip_emmc_phy_dt_ids, +- }, +-}; +- +-module_platform_driver(rockchip_emmc_driver); +- +-MODULE_AUTHOR("Shawn Lin "); +-MODULE_DESCRIPTION("Rockchip EMMC PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c +deleted file mode 100644 +index 8efe78a49916..000000000000 +--- a/drivers/phy/phy-rockchip-inno-usb2.c ++++ /dev/null +@@ -1,1284 +0,0 @@ +-/* +- * Rockchip USB2.0 PHY with Innosilicon IP block driver +- * +- * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define BIT_WRITEABLE_SHIFT 16 +-#define SCHEDULE_DELAY (60 * HZ) +-#define OTG_SCHEDULE_DELAY (2 * HZ) +- +-enum rockchip_usb2phy_port_id { +- USB2PHY_PORT_OTG, +- USB2PHY_PORT_HOST, +- USB2PHY_NUM_PORTS, +-}; +- +-enum rockchip_usb2phy_host_state { +- PHY_STATE_HS_ONLINE = 0, +- PHY_STATE_DISCONNECT = 1, +- PHY_STATE_CONNECT = 2, +- PHY_STATE_FS_LS_ONLINE = 4, +-}; +- +-/** +- * Different states involved in USB charger detection. +- * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection +- * process is not yet started. +- * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. +- * USB_CHG_STATE_DCD_DONE Data pin contact is detected. +- * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects +- * between SDP and DCP/CDP). +- * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects +- * between DCP and CDP). +- * USB_CHG_STATE_DETECTED USB charger type is determined. +- */ +-enum usb_chg_state { +- USB_CHG_STATE_UNDEFINED = 0, +- USB_CHG_STATE_WAIT_FOR_DCD, +- USB_CHG_STATE_DCD_DONE, +- USB_CHG_STATE_PRIMARY_DONE, +- USB_CHG_STATE_SECONDARY_DONE, +- USB_CHG_STATE_DETECTED, +-}; +- +-static const unsigned int rockchip_usb2phy_extcon_cable[] = { +- EXTCON_USB, +- EXTCON_USB_HOST, +- EXTCON_CHG_USB_SDP, +- EXTCON_CHG_USB_CDP, +- EXTCON_CHG_USB_DCP, +- EXTCON_CHG_USB_SLOW, +- EXTCON_NONE, +-}; +- +-struct usb2phy_reg { +- unsigned int offset; +- unsigned int bitend; +- unsigned int bitstart; +- unsigned int disable; +- unsigned int enable; +-}; +- +-/** +- * struct rockchip_chg_det_reg: usb charger detect registers +- * @cp_det: charging port detected successfully. +- * @dcp_det: dedicated charging port detected successfully. +- * @dp_det: assert data pin connect successfully. +- * @idm_sink_en: open dm sink curren. +- * @idp_sink_en: open dp sink current. +- * @idp_src_en: open dm source current. +- * @rdm_pdwn_en: open dm pull down resistor. +- * @vdm_src_en: open dm voltage source. +- * @vdp_src_en: open dp voltage source. +- * @opmode: utmi operational mode. +- */ +-struct rockchip_chg_det_reg { +- struct usb2phy_reg cp_det; +- struct usb2phy_reg dcp_det; +- struct usb2phy_reg dp_det; +- struct usb2phy_reg idm_sink_en; +- struct usb2phy_reg idp_sink_en; +- struct usb2phy_reg idp_src_en; +- struct usb2phy_reg rdm_pdwn_en; +- struct usb2phy_reg vdm_src_en; +- struct usb2phy_reg vdp_src_en; +- struct usb2phy_reg opmode; +-}; +- +-/** +- * struct rockchip_usb2phy_port_cfg: usb-phy port configuration. +- * @phy_sus: phy suspend register. +- * @bvalid_det_en: vbus valid rise detection enable register. +- * @bvalid_det_st: vbus valid rise detection status register. +- * @bvalid_det_clr: vbus valid rise detection clear register. +- * @ls_det_en: linestate detection enable register. +- * @ls_det_st: linestate detection state register. +- * @ls_det_clr: linestate detection clear register. +- * @utmi_avalid: utmi vbus avalid status register. +- * @utmi_bvalid: utmi vbus bvalid status register. +- * @utmi_ls: utmi linestate state register. +- * @utmi_hstdet: utmi host disconnect register. +- */ +-struct rockchip_usb2phy_port_cfg { +- struct usb2phy_reg phy_sus; +- struct usb2phy_reg bvalid_det_en; +- struct usb2phy_reg bvalid_det_st; +- struct usb2phy_reg bvalid_det_clr; +- struct usb2phy_reg ls_det_en; +- struct usb2phy_reg ls_det_st; +- struct usb2phy_reg ls_det_clr; +- struct usb2phy_reg utmi_avalid; +- struct usb2phy_reg utmi_bvalid; +- struct usb2phy_reg utmi_ls; +- struct usb2phy_reg utmi_hstdet; +-}; +- +-/** +- * struct rockchip_usb2phy_cfg: usb-phy configuration. +- * @reg: the address offset of grf for usb-phy config. +- * @num_ports: specify how many ports that the phy has. +- * @clkout_ctl: keep on/turn off output clk of phy. +- * @chg_det: charger detection registers. +- */ +-struct rockchip_usb2phy_cfg { +- unsigned int reg; +- unsigned int num_ports; +- struct usb2phy_reg clkout_ctl; +- const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; +- const struct rockchip_chg_det_reg chg_det; +-}; +- +-/** +- * struct rockchip_usb2phy_port: usb-phy port data. +- * @port_id: flag for otg port or host port. +- * @suspended: phy suspended flag. +- * @utmi_avalid: utmi avalid status usage flag. +- * true - use avalid to get vbus status +- * flase - use bvalid to get vbus status +- * @vbus_attached: otg device vbus status. +- * @bvalid_irq: IRQ number assigned for vbus valid rise detection. +- * @ls_irq: IRQ number assigned for linestate detection. +- * @mutex: for register updating in sm_work. +- * @chg_work: charge detect work. +- * @otg_sm_work: OTG state machine work. +- * @sm_work: HOST state machine work. +- * @phy_cfg: port register configuration, assigned by driver data. +- * @event_nb: hold event notification callback. +- * @state: define OTG enumeration states before device reset. +- * @mode: the dr_mode of the controller. +- */ +-struct rockchip_usb2phy_port { +- struct phy *phy; +- unsigned int port_id; +- bool suspended; +- bool utmi_avalid; +- bool vbus_attached; +- int bvalid_irq; +- int ls_irq; +- struct mutex mutex; +- struct delayed_work chg_work; +- struct delayed_work otg_sm_work; +- struct delayed_work sm_work; +- const struct rockchip_usb2phy_port_cfg *port_cfg; +- struct notifier_block event_nb; +- enum usb_otg_state state; +- enum usb_dr_mode mode; +-}; +- +-/** +- * struct rockchip_usb2phy: usb2.0 phy driver data. +- * @grf: General Register Files regmap. +- * @clk: clock struct of phy input clk. +- * @clk480m: clock struct of phy output clk. +- * @clk_hw: clock struct of phy output clk management. +- * @chg_state: states involved in USB charger detection. +- * @chg_type: USB charger types. +- * @dcd_retries: The retry count used to track Data contact +- * detection process. +- * @edev: extcon device for notification registration +- * @phy_cfg: phy register configuration, assigned by driver data. +- * @ports: phy port instance. +- */ +-struct rockchip_usb2phy { +- struct device *dev; +- struct regmap *grf; +- struct clk *clk; +- struct clk *clk480m; +- struct clk_hw clk480m_hw; +- enum usb_chg_state chg_state; +- enum power_supply_type chg_type; +- u8 dcd_retries; +- struct extcon_dev *edev; +- const struct rockchip_usb2phy_cfg *phy_cfg; +- struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; +-}; +- +-static inline int property_enable(struct rockchip_usb2phy *rphy, +- const struct usb2phy_reg *reg, bool en) +-{ +- unsigned int val, mask, tmp; +- +- tmp = en ? reg->enable : reg->disable; +- mask = GENMASK(reg->bitend, reg->bitstart); +- val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); +- +- return regmap_write(rphy->grf, reg->offset, val); +-} +- +-static inline bool property_enabled(struct rockchip_usb2phy *rphy, +- const struct usb2phy_reg *reg) +-{ +- int ret; +- unsigned int tmp, orig; +- unsigned int mask = GENMASK(reg->bitend, reg->bitstart); +- +- ret = regmap_read(rphy->grf, reg->offset, &orig); +- if (ret) +- return false; +- +- tmp = (orig & mask) >> reg->bitstart; +- return tmp == reg->enable; +-} +- +-static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) +-{ +- struct rockchip_usb2phy *rphy = +- container_of(hw, struct rockchip_usb2phy, clk480m_hw); +- int ret; +- +- /* turn on 480m clk output if it is off */ +- if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) { +- ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true); +- if (ret) +- return ret; +- +- /* waiting for the clk become stable */ +- usleep_range(1200, 1300); +- } +- +- return 0; +-} +- +-static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) +-{ +- struct rockchip_usb2phy *rphy = +- container_of(hw, struct rockchip_usb2phy, clk480m_hw); +- +- /* turn off 480m clk output */ +- property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false); +-} +- +-static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) +-{ +- struct rockchip_usb2phy *rphy = +- container_of(hw, struct rockchip_usb2phy, clk480m_hw); +- +- return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl); +-} +- +-static unsigned long +-rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw, +- unsigned long parent_rate) +-{ +- return 480000000; +-} +- +-static const struct clk_ops rockchip_usb2phy_clkout_ops = { +- .prepare = rockchip_usb2phy_clk480m_prepare, +- .unprepare = rockchip_usb2phy_clk480m_unprepare, +- .is_prepared = rockchip_usb2phy_clk480m_prepared, +- .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate, +-}; +- +-static void rockchip_usb2phy_clk480m_unregister(void *data) +-{ +- struct rockchip_usb2phy *rphy = data; +- +- of_clk_del_provider(rphy->dev->of_node); +- clk_unregister(rphy->clk480m); +-} +- +-static int +-rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) +-{ +- struct device_node *node = rphy->dev->of_node; +- struct clk_init_data init; +- const char *clk_name; +- int ret; +- +- init.flags = 0; +- init.name = "clk_usbphy_480m"; +- init.ops = &rockchip_usb2phy_clkout_ops; +- +- /* optional override of the clockname */ +- of_property_read_string(node, "clock-output-names", &init.name); +- +- if (rphy->clk) { +- clk_name = __clk_get_name(rphy->clk); +- init.parent_names = &clk_name; +- init.num_parents = 1; +- } else { +- init.parent_names = NULL; +- init.num_parents = 0; +- } +- +- rphy->clk480m_hw.init = &init; +- +- /* register the clock */ +- rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw); +- if (IS_ERR(rphy->clk480m)) { +- ret = PTR_ERR(rphy->clk480m); +- goto err_ret; +- } +- +- ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m); +- if (ret < 0) +- goto err_clk_provider; +- +- ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, +- rphy); +- if (ret < 0) +- goto err_unreg_action; +- +- return 0; +- +-err_unreg_action: +- of_clk_del_provider(node); +-err_clk_provider: +- clk_unregister(rphy->clk480m); +-err_ret: +- return ret; +-} +- +-static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) +-{ +- int ret; +- struct device_node *node = rphy->dev->of_node; +- struct extcon_dev *edev; +- +- if (of_property_read_bool(node, "extcon")) { +- edev = extcon_get_edev_by_phandle(rphy->dev, 0); +- if (IS_ERR(edev)) { +- if (PTR_ERR(edev) != -EPROBE_DEFER) +- dev_err(rphy->dev, "Invalid or missing extcon\n"); +- return PTR_ERR(edev); +- } +- } else { +- /* Initialize extcon device */ +- edev = devm_extcon_dev_allocate(rphy->dev, +- rockchip_usb2phy_extcon_cable); +- +- if (IS_ERR(edev)) +- return -ENOMEM; +- +- ret = devm_extcon_dev_register(rphy->dev, edev); +- if (ret) { +- dev_err(rphy->dev, "failed to register extcon device\n"); +- return ret; +- } +- } +- +- rphy->edev = edev; +- +- return 0; +-} +- +-static int rockchip_usb2phy_init(struct phy *phy) +-{ +- struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); +- struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); +- int ret = 0; +- +- mutex_lock(&rport->mutex); +- +- if (rport->port_id == USB2PHY_PORT_OTG) { +- if (rport->mode != USB_DR_MODE_HOST) { +- /* clear bvalid status and enable bvalid detect irq */ +- ret = property_enable(rphy, +- &rport->port_cfg->bvalid_det_clr, +- true); +- if (ret) +- goto out; +- +- ret = property_enable(rphy, +- &rport->port_cfg->bvalid_det_en, +- true); +- if (ret) +- goto out; +- +- schedule_delayed_work(&rport->otg_sm_work, +- OTG_SCHEDULE_DELAY); +- } else { +- /* If OTG works in host only mode, do nothing. */ +- dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); +- } +- } else if (rport->port_id == USB2PHY_PORT_HOST) { +- /* clear linestate and enable linestate detect irq */ +- ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true); +- if (ret) +- goto out; +- +- ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true); +- if (ret) +- goto out; +- +- schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); +- } +- +-out: +- mutex_unlock(&rport->mutex); +- return ret; +-} +- +-static int rockchip_usb2phy_power_on(struct phy *phy) +-{ +- struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); +- struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); +- int ret; +- +- dev_dbg(&rport->phy->dev, "port power on\n"); +- +- if (!rport->suspended) +- return 0; +- +- ret = clk_prepare_enable(rphy->clk480m); +- if (ret) +- return ret; +- +- ret = property_enable(rphy, &rport->port_cfg->phy_sus, false); +- if (ret) +- return ret; +- +- rport->suspended = false; +- return 0; +-} +- +-static int rockchip_usb2phy_power_off(struct phy *phy) +-{ +- struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); +- struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); +- int ret; +- +- dev_dbg(&rport->phy->dev, "port power off\n"); +- +- if (rport->suspended) +- return 0; +- +- ret = property_enable(rphy, &rport->port_cfg->phy_sus, true); +- if (ret) +- return ret; +- +- rport->suspended = true; +- clk_disable_unprepare(rphy->clk480m); +- +- return 0; +-} +- +-static int rockchip_usb2phy_exit(struct phy *phy) +-{ +- struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); +- +- if (rport->port_id == USB2PHY_PORT_OTG && +- rport->mode != USB_DR_MODE_HOST) { +- cancel_delayed_work_sync(&rport->otg_sm_work); +- cancel_delayed_work_sync(&rport->chg_work); +- } else if (rport->port_id == USB2PHY_PORT_HOST) +- cancel_delayed_work_sync(&rport->sm_work); +- +- return 0; +-} +- +-static const struct phy_ops rockchip_usb2phy_ops = { +- .init = rockchip_usb2phy_init, +- .exit = rockchip_usb2phy_exit, +- .power_on = rockchip_usb2phy_power_on, +- .power_off = rockchip_usb2phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) +-{ +- struct rockchip_usb2phy_port *rport = +- container_of(work, struct rockchip_usb2phy_port, +- otg_sm_work.work); +- struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); +- static unsigned int cable; +- unsigned long delay; +- bool vbus_attach, sch_work, notify_charger; +- +- if (rport->utmi_avalid) +- vbus_attach = +- property_enabled(rphy, &rport->port_cfg->utmi_avalid); +- else +- vbus_attach = +- property_enabled(rphy, &rport->port_cfg->utmi_bvalid); +- +- sch_work = false; +- notify_charger = false; +- delay = OTG_SCHEDULE_DELAY; +- dev_dbg(&rport->phy->dev, "%s otg sm work\n", +- usb_otg_state_string(rport->state)); +- +- switch (rport->state) { +- case OTG_STATE_UNDEFINED: +- rport->state = OTG_STATE_B_IDLE; +- if (!vbus_attach) +- rockchip_usb2phy_power_off(rport->phy); +- /* fall through */ +- case OTG_STATE_B_IDLE: +- if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) { +- dev_dbg(&rport->phy->dev, "usb otg host connect\n"); +- rport->state = OTG_STATE_A_HOST; +- rockchip_usb2phy_power_on(rport->phy); +- return; +- } else if (vbus_attach) { +- dev_dbg(&rport->phy->dev, "vbus_attach\n"); +- switch (rphy->chg_state) { +- case USB_CHG_STATE_UNDEFINED: +- schedule_delayed_work(&rport->chg_work, 0); +- return; +- case USB_CHG_STATE_DETECTED: +- switch (rphy->chg_type) { +- case POWER_SUPPLY_TYPE_USB: +- dev_dbg(&rport->phy->dev, "sdp cable is connected\n"); +- rockchip_usb2phy_power_on(rport->phy); +- rport->state = OTG_STATE_B_PERIPHERAL; +- notify_charger = true; +- sch_work = true; +- cable = EXTCON_CHG_USB_SDP; +- break; +- case POWER_SUPPLY_TYPE_USB_DCP: +- dev_dbg(&rport->phy->dev, "dcp cable is connected\n"); +- rockchip_usb2phy_power_off(rport->phy); +- notify_charger = true; +- sch_work = true; +- cable = EXTCON_CHG_USB_DCP; +- break; +- case POWER_SUPPLY_TYPE_USB_CDP: +- dev_dbg(&rport->phy->dev, "cdp cable is connected\n"); +- rockchip_usb2phy_power_on(rport->phy); +- rport->state = OTG_STATE_B_PERIPHERAL; +- notify_charger = true; +- sch_work = true; +- cable = EXTCON_CHG_USB_CDP; +- break; +- default: +- break; +- } +- break; +- default: +- break; +- } +- } else { +- notify_charger = true; +- rphy->chg_state = USB_CHG_STATE_UNDEFINED; +- rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; +- } +- +- if (rport->vbus_attached != vbus_attach) { +- rport->vbus_attached = vbus_attach; +- +- if (notify_charger && rphy->edev) { +- extcon_set_cable_state_(rphy->edev, +- cable, vbus_attach); +- if (cable == EXTCON_CHG_USB_SDP) +- extcon_set_state_sync(rphy->edev, +- EXTCON_USB, +- vbus_attach); +- } +- } +- break; +- case OTG_STATE_B_PERIPHERAL: +- if (!vbus_attach) { +- dev_dbg(&rport->phy->dev, "usb disconnect\n"); +- rphy->chg_state = USB_CHG_STATE_UNDEFINED; +- rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; +- rport->state = OTG_STATE_B_IDLE; +- delay = 0; +- rockchip_usb2phy_power_off(rport->phy); +- } +- sch_work = true; +- break; +- case OTG_STATE_A_HOST: +- if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) { +- dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); +- rport->state = OTG_STATE_B_IDLE; +- rockchip_usb2phy_power_off(rport->phy); +- } +- break; +- default: +- break; +- } +- +- if (sch_work) +- schedule_delayed_work(&rport->otg_sm_work, delay); +-} +- +-static const char *chg_to_string(enum power_supply_type chg_type) +-{ +- switch (chg_type) { +- case POWER_SUPPLY_TYPE_USB: +- return "USB_SDP_CHARGER"; +- case POWER_SUPPLY_TYPE_USB_DCP: +- return "USB_DCP_CHARGER"; +- case POWER_SUPPLY_TYPE_USB_CDP: +- return "USB_CDP_CHARGER"; +- default: +- return "INVALID_CHARGER"; +- } +-} +- +-static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, +- bool en) +-{ +- property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); +- property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en); +-} +- +-static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, +- bool en) +-{ +- property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en); +- property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en); +-} +- +-static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, +- bool en) +-{ +- property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en); +- property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en); +-} +- +-#define CHG_DCD_POLL_TIME (100 * HZ / 1000) +-#define CHG_DCD_MAX_RETRIES 6 +-#define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) +-#define CHG_SECONDARY_DET_TIME (40 * HZ / 1000) +-static void rockchip_chg_detect_work(struct work_struct *work) +-{ +- struct rockchip_usb2phy_port *rport = +- container_of(work, struct rockchip_usb2phy_port, chg_work.work); +- struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); +- bool is_dcd, tmout, vout; +- unsigned long delay; +- +- dev_dbg(&rport->phy->dev, "chg detection work state = %d\n", +- rphy->chg_state); +- switch (rphy->chg_state) { +- case USB_CHG_STATE_UNDEFINED: +- if (!rport->suspended) +- rockchip_usb2phy_power_off(rport->phy); +- /* put the controller in non-driving mode */ +- property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false); +- /* Start DCD processing stage 1 */ +- rockchip_chg_enable_dcd(rphy, true); +- rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; +- rphy->dcd_retries = 0; +- delay = CHG_DCD_POLL_TIME; +- break; +- case USB_CHG_STATE_WAIT_FOR_DCD: +- /* get data contact detection status */ +- is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det); +- tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES; +- /* stage 2 */ +- if (is_dcd || tmout) { +- /* stage 4 */ +- /* Turn off DCD circuitry */ +- rockchip_chg_enable_dcd(rphy, false); +- /* Voltage Source on DP, Probe on DM */ +- rockchip_chg_enable_primary_det(rphy, true); +- delay = CHG_PRIMARY_DET_TIME; +- rphy->chg_state = USB_CHG_STATE_DCD_DONE; +- } else { +- /* stage 3 */ +- delay = CHG_DCD_POLL_TIME; +- } +- break; +- case USB_CHG_STATE_DCD_DONE: +- vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det); +- rockchip_chg_enable_primary_det(rphy, false); +- if (vout) { +- /* Voltage Source on DM, Probe on DP */ +- rockchip_chg_enable_secondary_det(rphy, true); +- delay = CHG_SECONDARY_DET_TIME; +- rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE; +- } else { +- if (rphy->dcd_retries == CHG_DCD_MAX_RETRIES) { +- /* floating charger found */ +- rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; +- rphy->chg_state = USB_CHG_STATE_DETECTED; +- delay = 0; +- } else { +- rphy->chg_type = POWER_SUPPLY_TYPE_USB; +- rphy->chg_state = USB_CHG_STATE_DETECTED; +- delay = 0; +- } +- } +- break; +- case USB_CHG_STATE_PRIMARY_DONE: +- vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det); +- /* Turn off voltage source */ +- rockchip_chg_enable_secondary_det(rphy, false); +- if (vout) +- rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; +- else +- rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; +- /* fall through */ +- case USB_CHG_STATE_SECONDARY_DONE: +- rphy->chg_state = USB_CHG_STATE_DETECTED; +- delay = 0; +- /* fall through */ +- case USB_CHG_STATE_DETECTED: +- /* put the controller in normal mode */ +- property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true); +- rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); +- dev_info(&rport->phy->dev, "charger = %s\n", +- chg_to_string(rphy->chg_type)); +- return; +- default: +- return; +- } +- +- schedule_delayed_work(&rport->chg_work, delay); +-} +- +-/* +- * The function manage host-phy port state and suspend/resume phy port +- * to save power. +- * +- * we rely on utmi_linestate and utmi_hostdisconnect to identify whether +- * devices is disconnect or not. Besides, we do not need care it is FS/LS +- * disconnected or HS disconnected, actually, we just only need get the +- * device is disconnected at last through rearm the delayed work, +- * to suspend the phy port in _PHY_STATE_DISCONNECT_ case. +- * +- * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke +- * some clk related APIs, so do not invoke it from interrupt context directly. +- */ +-static void rockchip_usb2phy_sm_work(struct work_struct *work) +-{ +- struct rockchip_usb2phy_port *rport = +- container_of(work, struct rockchip_usb2phy_port, sm_work.work); +- struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); +- unsigned int sh = rport->port_cfg->utmi_hstdet.bitend - +- rport->port_cfg->utmi_hstdet.bitstart + 1; +- unsigned int ul, uhd, state; +- unsigned int ul_mask, uhd_mask; +- int ret; +- +- mutex_lock(&rport->mutex); +- +- ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul); +- if (ret < 0) +- goto next_schedule; +- +- ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, +- &uhd); +- if (ret < 0) +- goto next_schedule; +- +- uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, +- rport->port_cfg->utmi_hstdet.bitstart); +- ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, +- rport->port_cfg->utmi_ls.bitstart); +- +- /* stitch on utmi_ls and utmi_hstdet as phy state */ +- state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | +- (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); +- +- switch (state) { +- case PHY_STATE_HS_ONLINE: +- dev_dbg(&rport->phy->dev, "HS online\n"); +- break; +- case PHY_STATE_FS_LS_ONLINE: +- /* +- * For FS/LS device, the online state share with connect state +- * from utmi_ls and utmi_hstdet register, so we distinguish +- * them via suspended flag. +- * +- * Plus, there are two cases, one is D- Line pull-up, and D+ +- * line pull-down, the state is 4; another is D+ line pull-up, +- * and D- line pull-down, the state is 2. +- */ +- if (!rport->suspended) { +- /* D- line pull-up, D+ line pull-down */ +- dev_dbg(&rport->phy->dev, "FS/LS online\n"); +- break; +- } +- /* fall through */ +- case PHY_STATE_CONNECT: +- if (rport->suspended) { +- dev_dbg(&rport->phy->dev, "Connected\n"); +- rockchip_usb2phy_power_on(rport->phy); +- rport->suspended = false; +- } else { +- /* D+ line pull-up, D- line pull-down */ +- dev_dbg(&rport->phy->dev, "FS/LS online\n"); +- } +- break; +- case PHY_STATE_DISCONNECT: +- if (!rport->suspended) { +- dev_dbg(&rport->phy->dev, "Disconnected\n"); +- rockchip_usb2phy_power_off(rport->phy); +- rport->suspended = true; +- } +- +- /* +- * activate the linestate detection to get the next device +- * plug-in irq. +- */ +- property_enable(rphy, &rport->port_cfg->ls_det_clr, true); +- property_enable(rphy, &rport->port_cfg->ls_det_en, true); +- +- /* +- * we don't need to rearm the delayed work when the phy port +- * is suspended. +- */ +- mutex_unlock(&rport->mutex); +- return; +- default: +- dev_dbg(&rport->phy->dev, "unknown phy state\n"); +- break; +- } +- +-next_schedule: +- mutex_unlock(&rport->mutex); +- schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); +-} +- +-static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) +-{ +- struct rockchip_usb2phy_port *rport = data; +- struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); +- +- if (!property_enabled(rphy, &rport->port_cfg->ls_det_st)) +- return IRQ_NONE; +- +- mutex_lock(&rport->mutex); +- +- /* disable linestate detect irq and clear its status */ +- property_enable(rphy, &rport->port_cfg->ls_det_en, false); +- property_enable(rphy, &rport->port_cfg->ls_det_clr, true); +- +- mutex_unlock(&rport->mutex); +- +- /* +- * In this case for host phy port, a new device is plugged in, +- * meanwhile, if the phy port is suspended, we need rearm the work to +- * resume it and mange its states; otherwise, we do nothing about that. +- */ +- if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST) +- rockchip_usb2phy_sm_work(&rport->sm_work.work); +- +- return IRQ_HANDLED; +-} +- +-static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) +-{ +- struct rockchip_usb2phy_port *rport = data; +- struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); +- +- if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st)) +- return IRQ_NONE; +- +- mutex_lock(&rport->mutex); +- +- /* clear bvalid detect irq pending status */ +- property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true); +- +- mutex_unlock(&rport->mutex); +- +- rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); +- +- return IRQ_HANDLED; +-} +- +-static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, +- struct rockchip_usb2phy_port *rport, +- struct device_node *child_np) +-{ +- int ret; +- +- rport->port_id = USB2PHY_PORT_HOST; +- rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST]; +- rport->suspended = true; +- +- mutex_init(&rport->mutex); +- INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work); +- +- rport->ls_irq = of_irq_get_byname(child_np, "linestate"); +- if (rport->ls_irq < 0) { +- dev_err(rphy->dev, "no linestate irq provided\n"); +- return rport->ls_irq; +- } +- +- ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, +- rockchip_usb2phy_linestate_irq, +- IRQF_ONESHOT, +- "rockchip_usb2phy", rport); +- if (ret) { +- dev_err(rphy->dev, "failed to request linestate irq handle\n"); +- return ret; +- } +- +- return 0; +-} +- +-static int rockchip_otg_event(struct notifier_block *nb, +- unsigned long event, void *ptr) +-{ +- struct rockchip_usb2phy_port *rport = +- container_of(nb, struct rockchip_usb2phy_port, event_nb); +- +- schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY); +- +- return NOTIFY_DONE; +-} +- +-static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, +- struct rockchip_usb2phy_port *rport, +- struct device_node *child_np) +-{ +- int ret; +- +- rport->port_id = USB2PHY_PORT_OTG; +- rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; +- rport->state = OTG_STATE_UNDEFINED; +- +- /* +- * set suspended flag to true, but actually don't +- * put phy in suspend mode, it aims to enable usb +- * phy and clock in power_on() called by usb controller +- * driver during probe. +- */ +- rport->suspended = true; +- rport->vbus_attached = false; +- +- mutex_init(&rport->mutex); +- +- rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); +- if (rport->mode == USB_DR_MODE_HOST) { +- ret = 0; +- goto out; +- } +- +- INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); +- INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); +- +- rport->utmi_avalid = +- of_property_read_bool(child_np, "rockchip,utmi-avalid"); +- +- rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); +- if (rport->bvalid_irq < 0) { +- dev_err(rphy->dev, "no vbus valid irq provided\n"); +- ret = rport->bvalid_irq; +- goto out; +- } +- +- ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL, +- rockchip_usb2phy_bvalid_irq, +- IRQF_ONESHOT, +- "rockchip_usb2phy_bvalid", rport); +- if (ret) { +- dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n"); +- goto out; +- } +- +- if (!IS_ERR(rphy->edev)) { +- rport->event_nb.notifier_call = rockchip_otg_event; +- +- ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, +- &rport->event_nb); +- if (ret) +- dev_err(rphy->dev, "register USB HOST notifier failed\n"); +- } +- +-out: +- return ret; +-} +- +-static int rockchip_usb2phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct device_node *np = dev->of_node; +- struct device_node *child_np; +- struct phy_provider *provider; +- struct rockchip_usb2phy *rphy; +- const struct rockchip_usb2phy_cfg *phy_cfgs; +- const struct of_device_id *match; +- unsigned int reg; +- int index, ret; +- +- rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL); +- if (!rphy) +- return -ENOMEM; +- +- match = of_match_device(dev->driver->of_match_table, dev); +- if (!match || !match->data) { +- dev_err(dev, "phy configs are not assigned!\n"); +- return -EINVAL; +- } +- +- if (!dev->parent || !dev->parent->of_node) +- return -EINVAL; +- +- rphy->grf = syscon_node_to_regmap(dev->parent->of_node); +- if (IS_ERR(rphy->grf)) +- return PTR_ERR(rphy->grf); +- +- if (of_property_read_u32(np, "reg", ®)) { +- dev_err(dev, "the reg property is not assigned in %s node\n", +- np->name); +- return -EINVAL; +- } +- +- rphy->dev = dev; +- phy_cfgs = match->data; +- rphy->chg_state = USB_CHG_STATE_UNDEFINED; +- rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; +- platform_set_drvdata(pdev, rphy); +- +- ret = rockchip_usb2phy_extcon_register(rphy); +- if (ret) +- return ret; +- +- /* find out a proper config which can be matched with dt. */ +- index = 0; +- while (phy_cfgs[index].reg) { +- if (phy_cfgs[index].reg == reg) { +- rphy->phy_cfg = &phy_cfgs[index]; +- break; +- } +- +- ++index; +- } +- +- if (!rphy->phy_cfg) { +- dev_err(dev, "no phy-config can be matched with %s node\n", +- np->name); +- return -EINVAL; +- } +- +- rphy->clk = of_clk_get_by_name(np, "phyclk"); +- if (!IS_ERR(rphy->clk)) { +- clk_prepare_enable(rphy->clk); +- } else { +- dev_info(&pdev->dev, "no phyclk specified\n"); +- rphy->clk = NULL; +- } +- +- ret = rockchip_usb2phy_clk480m_register(rphy); +- if (ret) { +- dev_err(dev, "failed to register 480m output clock\n"); +- goto disable_clks; +- } +- +- index = 0; +- for_each_available_child_of_node(np, child_np) { +- struct rockchip_usb2phy_port *rport = &rphy->ports[index]; +- struct phy *phy; +- +- /* This driver aims to support both otg-port and host-port */ +- if (of_node_cmp(child_np->name, "host-port") && +- of_node_cmp(child_np->name, "otg-port")) +- goto next_child; +- +- phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create phy\n"); +- ret = PTR_ERR(phy); +- goto put_child; +- } +- +- rport->phy = phy; +- phy_set_drvdata(rport->phy, rport); +- +- /* initialize otg/host port separately */ +- if (!of_node_cmp(child_np->name, "host-port")) { +- ret = rockchip_usb2phy_host_port_init(rphy, rport, +- child_np); +- if (ret) +- goto put_child; +- } else { +- ret = rockchip_usb2phy_otg_port_init(rphy, rport, +- child_np); +- if (ret) +- goto put_child; +- } +- +-next_child: +- /* to prevent out of boundary */ +- if (++index >= rphy->phy_cfg->num_ports) +- break; +- } +- +- provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(provider); +- +-put_child: +- of_node_put(child_np); +-disable_clks: +- if (rphy->clk) { +- clk_disable_unprepare(rphy->clk); +- clk_put(rphy->clk); +- } +- return ret; +-} +- +-static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { +- { +- .reg = 0x100, +- .num_ports = 2, +- .clkout_ctl = { 0x108, 4, 4, 1, 0 }, +- .port_cfgs = { +- [USB2PHY_PORT_OTG] = { +- .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, +- .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, +- .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, +- .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 }, +- .ls_det_en = { 0x0110, 0, 0, 0, 1 }, +- .ls_det_st = { 0x0114, 0, 0, 0, 1 }, +- .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, +- .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, +- .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, +- .utmi_ls = { 0x0120, 5, 4, 0, 1 }, +- }, +- [USB2PHY_PORT_HOST] = { +- .phy_sus = { 0x104, 15, 0, 0, 0x1d1 }, +- .ls_det_en = { 0x110, 1, 1, 0, 1 }, +- .ls_det_st = { 0x114, 1, 1, 0, 1 }, +- .ls_det_clr = { 0x118, 1, 1, 0, 1 }, +- .utmi_ls = { 0x120, 17, 16, 0, 1 }, +- .utmi_hstdet = { 0x120, 19, 19, 0, 1 } +- } +- }, +- .chg_det = { +- .opmode = { 0x0100, 3, 0, 5, 1 }, +- .cp_det = { 0x0120, 24, 24, 0, 1 }, +- .dcp_det = { 0x0120, 23, 23, 0, 1 }, +- .dp_det = { 0x0120, 25, 25, 0, 1 }, +- .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, +- .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, +- .idp_src_en = { 0x0108, 9, 9, 0, 1 }, +- .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, +- .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, +- .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, +- }, +- }, +- { /* sentinel */ } +-}; +- +-static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = { +- { +- .reg = 0x700, +- .num_ports = 2, +- .clkout_ctl = { 0x0724, 15, 15, 1, 0 }, +- .port_cfgs = { +- [USB2PHY_PORT_HOST] = { +- .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, +- .ls_det_en = { 0x0680, 4, 4, 0, 1 }, +- .ls_det_st = { 0x0690, 4, 4, 0, 1 }, +- .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, +- .utmi_ls = { 0x049c, 14, 13, 0, 1 }, +- .utmi_hstdet = { 0x049c, 12, 12, 0, 1 } +- } +- }, +- }, +- { /* sentinel */ } +-}; +- +-static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { +- { +- .reg = 0xe450, +- .num_ports = 2, +- .clkout_ctl = { 0xe450, 4, 4, 1, 0 }, +- .port_cfgs = { +- [USB2PHY_PORT_OTG] = { +- .phy_sus = { 0xe454, 1, 0, 2, 1 }, +- .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, +- .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, +- .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, +- .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, +- .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, +- }, +- [USB2PHY_PORT_HOST] = { +- .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, +- .ls_det_en = { 0xe3c0, 6, 6, 0, 1 }, +- .ls_det_st = { 0xe3e0, 6, 6, 0, 1 }, +- .ls_det_clr = { 0xe3d0, 6, 6, 0, 1 }, +- .utmi_ls = { 0xe2ac, 22, 21, 0, 1 }, +- .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 } +- } +- }, +- .chg_det = { +- .opmode = { 0xe454, 3, 0, 5, 1 }, +- .cp_det = { 0xe2ac, 2, 2, 0, 1 }, +- .dcp_det = { 0xe2ac, 1, 1, 0, 1 }, +- .dp_det = { 0xe2ac, 0, 0, 0, 1 }, +- .idm_sink_en = { 0xe450, 8, 8, 0, 1 }, +- .idp_sink_en = { 0xe450, 7, 7, 0, 1 }, +- .idp_src_en = { 0xe450, 9, 9, 0, 1 }, +- .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 }, +- .vdm_src_en = { 0xe450, 12, 12, 0, 1 }, +- .vdp_src_en = { 0xe450, 11, 11, 0, 1 }, +- }, +- }, +- { +- .reg = 0xe460, +- .num_ports = 2, +- .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, +- .port_cfgs = { +- [USB2PHY_PORT_OTG] = { +- .phy_sus = { 0xe464, 1, 0, 2, 1 }, +- .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, +- .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, +- .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, +- .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, +- .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, +- }, +- [USB2PHY_PORT_HOST] = { +- .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, +- .ls_det_en = { 0xe3c0, 11, 11, 0, 1 }, +- .ls_det_st = { 0xe3e0, 11, 11, 0, 1 }, +- .ls_det_clr = { 0xe3d0, 11, 11, 0, 1 }, +- .utmi_ls = { 0xe2ac, 26, 25, 0, 1 }, +- .utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 } +- } +- }, +- }, +- { /* sentinel */ } +-}; +- +-static const struct of_device_id rockchip_usb2phy_dt_match[] = { +- { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, +- { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, +- { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, +- {} +-}; +-MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); +- +-static struct platform_driver rockchip_usb2phy_driver = { +- .probe = rockchip_usb2phy_probe, +- .driver = { +- .name = "rockchip-usb2phy", +- .of_match_table = rockchip_usb2phy_dt_match, +- }, +-}; +-module_platform_driver(rockchip_usb2phy_driver); +- +-MODULE_AUTHOR("Frank Wang "); +-MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-rockchip-pcie.c b/drivers/phy/phy-rockchip-pcie.c +deleted file mode 100644 +index 6904633cad68..000000000000 +--- a/drivers/phy/phy-rockchip-pcie.c ++++ /dev/null +@@ -1,346 +0,0 @@ +-/* +- * Rockchip PCIe PHY driver +- * +- * Copyright (C) 2016 Shawn Lin +- * Copyright (C) 2016 ROCKCHIP, Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* +- * The higher 16-bit of this register is used for write protection +- * only if BIT(x + 16) set to 1 the BIT(x) can be written. +- */ +-#define HIWORD_UPDATE(val, mask, shift) \ +- ((val) << (shift) | (mask) << ((shift) + 16)) +- +-#define PHY_MAX_LANE_NUM 4 +-#define PHY_CFG_DATA_SHIFT 7 +-#define PHY_CFG_ADDR_SHIFT 1 +-#define PHY_CFG_DATA_MASK 0xf +-#define PHY_CFG_ADDR_MASK 0x3f +-#define PHY_CFG_RD_MASK 0x3ff +-#define PHY_CFG_WR_ENABLE 1 +-#define PHY_CFG_WR_DISABLE 1 +-#define PHY_CFG_WR_SHIFT 0 +-#define PHY_CFG_WR_MASK 1 +-#define PHY_CFG_PLL_LOCK 0x10 +-#define PHY_CFG_CLK_TEST 0x10 +-#define PHY_CFG_CLK_SCC 0x12 +-#define PHY_CFG_SEPE_RATE BIT(3) +-#define PHY_CFG_PLL_100M BIT(3) +-#define PHY_PLL_LOCKED BIT(9) +-#define PHY_PLL_OUTPUT BIT(10) +-#define PHY_LANE_A_STATUS 0x30 +-#define PHY_LANE_B_STATUS 0x31 +-#define PHY_LANE_C_STATUS 0x32 +-#define PHY_LANE_D_STATUS 0x33 +-#define PHY_LANE_RX_DET_SHIFT 11 +-#define PHY_LANE_RX_DET_TH 0x1 +-#define PHY_LANE_IDLE_OFF 0x1 +-#define PHY_LANE_IDLE_MASK 0x1 +-#define PHY_LANE_IDLE_A_SHIFT 3 +-#define PHY_LANE_IDLE_B_SHIFT 4 +-#define PHY_LANE_IDLE_C_SHIFT 5 +-#define PHY_LANE_IDLE_D_SHIFT 6 +- +-struct rockchip_pcie_data { +- unsigned int pcie_conf; +- unsigned int pcie_status; +- unsigned int pcie_laneoff; +-}; +- +-struct rockchip_pcie_phy { +- struct rockchip_pcie_data *phy_data; +- struct regmap *reg_base; +- struct reset_control *phy_rst; +- struct clk *clk_pciephy_ref; +-}; +- +-static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy, +- u32 addr, u32 data) +-{ +- regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, +- HIWORD_UPDATE(data, +- PHY_CFG_DATA_MASK, +- PHY_CFG_DATA_SHIFT) | +- HIWORD_UPDATE(addr, +- PHY_CFG_ADDR_MASK, +- PHY_CFG_ADDR_SHIFT)); +- udelay(1); +- regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, +- HIWORD_UPDATE(PHY_CFG_WR_ENABLE, +- PHY_CFG_WR_MASK, +- PHY_CFG_WR_SHIFT)); +- udelay(1); +- regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, +- HIWORD_UPDATE(PHY_CFG_WR_DISABLE, +- PHY_CFG_WR_MASK, +- PHY_CFG_WR_SHIFT)); +-} +- +-static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy, +- u32 addr) +-{ +- u32 val; +- +- regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, +- HIWORD_UPDATE(addr, +- PHY_CFG_RD_MASK, +- PHY_CFG_ADDR_SHIFT)); +- regmap_read(rk_phy->reg_base, +- rk_phy->phy_data->pcie_status, +- &val); +- return val; +-} +- +-static int rockchip_pcie_phy_power_off(struct phy *phy) +-{ +- struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); +- int err = 0; +- +- err = reset_control_assert(rk_phy->phy_rst); +- if (err) { +- dev_err(&phy->dev, "assert phy_rst err %d\n", err); +- return err; +- } +- +- return 0; +-} +- +-static int rockchip_pcie_phy_power_on(struct phy *phy) +-{ +- struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); +- int err = 0; +- u32 status; +- unsigned long timeout; +- +- err = reset_control_deassert(rk_phy->phy_rst); +- if (err) { +- dev_err(&phy->dev, "deassert phy_rst err %d\n", err); +- return err; +- } +- +- regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, +- HIWORD_UPDATE(PHY_CFG_PLL_LOCK, +- PHY_CFG_ADDR_MASK, +- PHY_CFG_ADDR_SHIFT)); +- +- /* +- * No documented timeout value for phy operation below, +- * so we make it large enough here. And we use loop-break +- * method which should not be harmful. +- */ +- timeout = jiffies + msecs_to_jiffies(1000); +- +- err = -EINVAL; +- while (time_before(jiffies, timeout)) { +- regmap_read(rk_phy->reg_base, +- rk_phy->phy_data->pcie_status, +- &status); +- if (status & PHY_PLL_LOCKED) { +- dev_dbg(&phy->dev, "pll locked!\n"); +- err = 0; +- break; +- } +- msleep(20); +- } +- +- if (err) { +- dev_err(&phy->dev, "pll lock timeout!\n"); +- goto err_pll_lock; +- } +- +- phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE); +- phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M); +- +- err = -ETIMEDOUT; +- while (time_before(jiffies, timeout)) { +- regmap_read(rk_phy->reg_base, +- rk_phy->phy_data->pcie_status, +- &status); +- if (!(status & PHY_PLL_OUTPUT)) { +- dev_dbg(&phy->dev, "pll output enable done!\n"); +- err = 0; +- break; +- } +- msleep(20); +- } +- +- if (err) { +- dev_err(&phy->dev, "pll output enable timeout!\n"); +- goto err_pll_lock; +- } +- +- regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, +- HIWORD_UPDATE(PHY_CFG_PLL_LOCK, +- PHY_CFG_ADDR_MASK, +- PHY_CFG_ADDR_SHIFT)); +- err = -EINVAL; +- while (time_before(jiffies, timeout)) { +- regmap_read(rk_phy->reg_base, +- rk_phy->phy_data->pcie_status, +- &status); +- if (status & PHY_PLL_LOCKED) { +- dev_dbg(&phy->dev, "pll relocked!\n"); +- err = 0; +- break; +- } +- msleep(20); +- } +- +- if (err) { +- dev_err(&phy->dev, "pll relock timeout!\n"); +- goto err_pll_lock; +- } +- +- return 0; +- +-err_pll_lock: +- reset_control_assert(rk_phy->phy_rst); +- return err; +-} +- +-static int rockchip_pcie_phy_init(struct phy *phy) +-{ +- struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); +- int err = 0; +- +- err = clk_prepare_enable(rk_phy->clk_pciephy_ref); +- if (err) { +- dev_err(&phy->dev, "Fail to enable pcie ref clock.\n"); +- goto err_refclk; +- } +- +- err = reset_control_assert(rk_phy->phy_rst); +- if (err) { +- dev_err(&phy->dev, "assert phy_rst err %d\n", err); +- goto err_reset; +- } +- +- return err; +- +-err_reset: +- clk_disable_unprepare(rk_phy->clk_pciephy_ref); +-err_refclk: +- return err; +-} +- +-static int rockchip_pcie_phy_exit(struct phy *phy) +-{ +- struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); +- +- clk_disable_unprepare(rk_phy->clk_pciephy_ref); +- +- return 0; +-} +- +-static const struct phy_ops ops = { +- .init = rockchip_pcie_phy_init, +- .exit = rockchip_pcie_phy_exit, +- .power_on = rockchip_pcie_phy_power_on, +- .power_off = rockchip_pcie_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static const struct rockchip_pcie_data rk3399_pcie_data = { +- .pcie_conf = 0xe220, +- .pcie_status = 0xe2a4, +- .pcie_laneoff = 0xe214, +-}; +- +-static const struct of_device_id rockchip_pcie_phy_dt_ids[] = { +- { +- .compatible = "rockchip,rk3399-pcie-phy", +- .data = &rk3399_pcie_data, +- }, +- {} +-}; +- +-MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids); +- +-static int rockchip_pcie_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct rockchip_pcie_phy *rk_phy; +- struct phy *generic_phy; +- struct phy_provider *phy_provider; +- struct regmap *grf; +- const struct of_device_id *of_id; +- +- grf = syscon_node_to_regmap(dev->parent->of_node); +- if (IS_ERR(grf)) { +- dev_err(dev, "Cannot find GRF syscon\n"); +- return PTR_ERR(grf); +- } +- +- rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); +- if (!rk_phy) +- return -ENOMEM; +- +- of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev); +- if (!of_id) +- return -EINVAL; +- +- rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data; +- rk_phy->reg_base = grf; +- +- rk_phy->phy_rst = devm_reset_control_get(dev, "phy"); +- if (IS_ERR(rk_phy->phy_rst)) { +- if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER) +- dev_err(dev, +- "missing phy property for reset controller\n"); +- return PTR_ERR(rk_phy->phy_rst); +- } +- +- rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk"); +- if (IS_ERR(rk_phy->clk_pciephy_ref)) { +- dev_err(dev, "refclk not found.\n"); +- return PTR_ERR(rk_phy->clk_pciephy_ref); +- } +- +- generic_phy = devm_phy_create(dev, dev->of_node, &ops); +- if (IS_ERR(generic_phy)) { +- dev_err(dev, "failed to create PHY\n"); +- return PTR_ERR(generic_phy); +- } +- +- phy_set_drvdata(generic_phy, rk_phy); +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static struct platform_driver rockchip_pcie_driver = { +- .probe = rockchip_pcie_phy_probe, +- .driver = { +- .name = "rockchip-pcie-phy", +- .of_match_table = rockchip_pcie_phy_dt_ids, +- }, +-}; +- +-module_platform_driver(rockchip_pcie_driver); +- +-MODULE_AUTHOR("Shawn Lin "); +-MODULE_DESCRIPTION("Rockchip PCIe PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c +deleted file mode 100644 +index 7cfb0f8995de..000000000000 +--- a/drivers/phy/phy-rockchip-typec.c ++++ /dev/null +@@ -1,1023 +0,0 @@ +-/* +- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd +- * Author: Chris Zhong +- * Kever Yang +- * +- * This software is licensed under the terms of the GNU General Public +- * License version 2, as published by the Free Software Foundation, and +- * may be copied, distributed, and modified under those terms. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * The ROCKCHIP Type-C PHY has two PLL clocks. The first PLL clock +- * is used for USB3, the second PLL clock is used for DP. This Type-C PHY has +- * 3 working modes: USB3 only mode, DP only mode, and USB3+DP mode. +- * At USB3 only mode, both PLL clocks need to be initialized, this allows the +- * PHY to switch mode between USB3 and USB3+DP, without disconnecting the USB +- * device. +- * In The DP only mode, only the DP PLL needs to be powered on, and the 4 lanes +- * are all used for DP. +- * +- * This driver gets extcon cable state and property, then decides which mode to +- * select: +- * +- * 1. USB3 only mode: +- * EXTCON_USB or EXTCON_USB_HOST state is true, and +- * EXTCON_PROP_USB_SS property is true. +- * EXTCON_DISP_DP state is false. +- * +- * 2. DP only mode: +- * EXTCON_DISP_DP state is true, and +- * EXTCON_PROP_USB_SS property is false. +- * If EXTCON_USB_HOST state is true, it is DP + USB2 mode, since the USB2 phy +- * is a separate phy, so this case is still DP only mode. +- * +- * 3. USB3+DP mode: +- * EXTCON_USB_HOST and EXTCON_DISP_DP are both true, and +- * EXTCON_PROP_USB_SS property is true. +- * +- * This Type-C PHY driver supports normal and flip orientation. The orientation +- * is reported by the EXTCON_PROP_USB_TYPEC_POLARITY property: true is flip +- * orientation, false is normal orientation. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +- +-#define CMN_SSM_BANDGAP (0x21 << 2) +-#define CMN_SSM_BIAS (0x22 << 2) +-#define CMN_PLLSM0_PLLEN (0x29 << 2) +-#define CMN_PLLSM0_PLLPRE (0x2a << 2) +-#define CMN_PLLSM0_PLLVREF (0x2b << 2) +-#define CMN_PLLSM0_PLLLOCK (0x2c << 2) +-#define CMN_PLLSM1_PLLEN (0x31 << 2) +-#define CMN_PLLSM1_PLLPRE (0x32 << 2) +-#define CMN_PLLSM1_PLLVREF (0x33 << 2) +-#define CMN_PLLSM1_PLLLOCK (0x34 << 2) +-#define CMN_PLLSM1_USER_DEF_CTRL (0x37 << 2) +-#define CMN_ICAL_OVRD (0xc1 << 2) +-#define CMN_PLL0_VCOCAL_OVRD (0x83 << 2) +-#define CMN_PLL0_VCOCAL_INIT (0x84 << 2) +-#define CMN_PLL0_VCOCAL_ITER (0x85 << 2) +-#define CMN_PLL0_LOCK_REFCNT_START (0x90 << 2) +-#define CMN_PLL0_LOCK_PLLCNT_START (0x92 << 2) +-#define CMN_PLL0_LOCK_PLLCNT_THR (0x93 << 2) +-#define CMN_PLL0_INTDIV (0x94 << 2) +-#define CMN_PLL0_FRACDIV (0x95 << 2) +-#define CMN_PLL0_HIGH_THR (0x96 << 2) +-#define CMN_PLL0_DSM_DIAG (0x97 << 2) +-#define CMN_PLL0_SS_CTRL1 (0x98 << 2) +-#define CMN_PLL0_SS_CTRL2 (0x99 << 2) +-#define CMN_PLL1_VCOCAL_START (0xa1 << 2) +-#define CMN_PLL1_VCOCAL_OVRD (0xa3 << 2) +-#define CMN_PLL1_VCOCAL_INIT (0xa4 << 2) +-#define CMN_PLL1_VCOCAL_ITER (0xa5 << 2) +-#define CMN_PLL1_LOCK_REFCNT_START (0xb0 << 2) +-#define CMN_PLL1_LOCK_PLLCNT_START (0xb2 << 2) +-#define CMN_PLL1_LOCK_PLLCNT_THR (0xb3 << 2) +-#define CMN_PLL1_INTDIV (0xb4 << 2) +-#define CMN_PLL1_FRACDIV (0xb5 << 2) +-#define CMN_PLL1_HIGH_THR (0xb6 << 2) +-#define CMN_PLL1_DSM_DIAG (0xb7 << 2) +-#define CMN_PLL1_SS_CTRL1 (0xb8 << 2) +-#define CMN_PLL1_SS_CTRL2 (0xb9 << 2) +-#define CMN_RXCAL_OVRD (0xd1 << 2) +-#define CMN_TXPUCAL_CTRL (0xe0 << 2) +-#define CMN_TXPUCAL_OVRD (0xe1 << 2) +-#define CMN_TXPDCAL_OVRD (0xf1 << 2) +-#define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2) +-#define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2) +-#define CMN_DIAG_PLL0_OVRD (0x1c2 << 2) +-#define CMN_DIAG_PLL0_V2I_TUNE (0x1c5 << 2) +-#define CMN_DIAG_PLL0_CP_TUNE (0x1c6 << 2) +-#define CMN_DIAG_PLL0_LF_PROG (0x1c7 << 2) +-#define CMN_DIAG_PLL1_FBH_OVRD (0x1d0 << 2) +-#define CMN_DIAG_PLL1_FBL_OVRD (0x1d1 << 2) +-#define CMN_DIAG_PLL1_OVRD (0x1d2 << 2) +-#define CMN_DIAG_PLL1_V2I_TUNE (0x1d5 << 2) +-#define CMN_DIAG_PLL1_CP_TUNE (0x1d6 << 2) +-#define CMN_DIAG_PLL1_LF_PROG (0x1d7 << 2) +-#define CMN_DIAG_PLL1_PTATIS_TUNE1 (0x1d8 << 2) +-#define CMN_DIAG_PLL1_PTATIS_TUNE2 (0x1d9 << 2) +-#define CMN_DIAG_PLL1_INCLK_CTRL (0x1da << 2) +-#define CMN_DIAG_HSCLK_SEL (0x1e0 << 2) +- +-#define XCVR_PSM_RCTRL(n) ((0x4001 | ((n) << 9)) << 2) +-#define XCVR_PSM_CAL_TMR(n) ((0x4002 | ((n) << 9)) << 2) +-#define XCVR_PSM_A0IN_TMR(n) ((0x4003 | ((n) << 9)) << 2) +-#define TX_TXCC_CAL_SCLR_MULT(n) ((0x4047 | ((n) << 9)) << 2) +-#define TX_TXCC_CPOST_MULT_00(n) ((0x404c | ((n) << 9)) << 2) +-#define TX_TXCC_CPOST_MULT_01(n) ((0x404d | ((n) << 9)) << 2) +-#define TX_TXCC_CPOST_MULT_10(n) ((0x404e | ((n) << 9)) << 2) +-#define TX_TXCC_CPOST_MULT_11(n) ((0x404f | ((n) << 9)) << 2) +-#define TX_TXCC_MGNFS_MULT_000(n) ((0x4050 | ((n) << 9)) << 2) +-#define TX_TXCC_MGNFS_MULT_001(n) ((0x4051 | ((n) << 9)) << 2) +-#define TX_TXCC_MGNFS_MULT_010(n) ((0x4052 | ((n) << 9)) << 2) +-#define TX_TXCC_MGNFS_MULT_011(n) ((0x4053 | ((n) << 9)) << 2) +-#define TX_TXCC_MGNFS_MULT_100(n) ((0x4054 | ((n) << 9)) << 2) +-#define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2) +-#define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2) +-#define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2) +-#define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2) +-#define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2) +-#define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2) +-#define TX_PSC_A0(n) ((0x4100 | ((n) << 9)) << 2) +-#define TX_PSC_A1(n) ((0x4101 | ((n) << 9)) << 2) +-#define TX_PSC_A2(n) ((0x4102 | ((n) << 9)) << 2) +-#define TX_PSC_A3(n) ((0x4103 | ((n) << 9)) << 2) +-#define TX_RCVDET_CTRL(n) ((0x4120 | ((n) << 9)) << 2) +-#define TX_RCVDET_EN_TMR(n) ((0x4122 | ((n) << 9)) << 2) +-#define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2) +-#define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2) +-#define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2) +-#define TX_ANA_CTRL_REG_1 (0x5020 << 2) +-#define TX_ANA_CTRL_REG_2 (0x5021 << 2) +-#define TXDA_COEFF_CALC_CTRL (0x5022 << 2) +-#define TX_DIG_CTRL_REG_2 (0x5024 << 2) +-#define TXDA_CYA_AUXDA_CYA (0x5025 << 2) +-#define TX_ANA_CTRL_REG_3 (0x5026 << 2) +-#define TX_ANA_CTRL_REG_4 (0x5027 << 2) +-#define TX_ANA_CTRL_REG_5 (0x5029 << 2) +- +-#define RX_PSC_A0(n) ((0x8000 | ((n) << 9)) << 2) +-#define RX_PSC_A1(n) ((0x8001 | ((n) << 9)) << 2) +-#define RX_PSC_A2(n) ((0x8002 | ((n) << 9)) << 2) +-#define RX_PSC_A3(n) ((0x8003 | ((n) << 9)) << 2) +-#define RX_PSC_CAL(n) ((0x8006 | ((n) << 9)) << 2) +-#define RX_PSC_RDY(n) ((0x8007 | ((n) << 9)) << 2) +-#define RX_IQPI_ILL_CAL_OVRD (0x8023 << 2) +-#define RX_EPI_ILL_CAL_OVRD (0x8033 << 2) +-#define RX_SDCAL0_OVRD (0x8041 << 2) +-#define RX_SDCAL1_OVRD (0x8049 << 2) +-#define RX_SLC_INIT (0x806d << 2) +-#define RX_SLC_RUN (0x806e << 2) +-#define RX_CDRLF_CNFG2 (0x8081 << 2) +-#define RX_SIGDET_HL_FILT_TMR(n) ((0x8090 | ((n) << 9)) << 2) +-#define RX_SLC_IOP0_OVRD (0x8101 << 2) +-#define RX_SLC_IOP1_OVRD (0x8105 << 2) +-#define RX_SLC_QOP0_OVRD (0x8109 << 2) +-#define RX_SLC_QOP1_OVRD (0x810d << 2) +-#define RX_SLC_EOP0_OVRD (0x8111 << 2) +-#define RX_SLC_EOP1_OVRD (0x8115 << 2) +-#define RX_SLC_ION0_OVRD (0x8119 << 2) +-#define RX_SLC_ION1_OVRD (0x811d << 2) +-#define RX_SLC_QON0_OVRD (0x8121 << 2) +-#define RX_SLC_QON1_OVRD (0x8125 << 2) +-#define RX_SLC_EON0_OVRD (0x8129 << 2) +-#define RX_SLC_EON1_OVRD (0x812d << 2) +-#define RX_SLC_IEP0_OVRD (0x8131 << 2) +-#define RX_SLC_IEP1_OVRD (0x8135 << 2) +-#define RX_SLC_QEP0_OVRD (0x8139 << 2) +-#define RX_SLC_QEP1_OVRD (0x813d << 2) +-#define RX_SLC_EEP0_OVRD (0x8141 << 2) +-#define RX_SLC_EEP1_OVRD (0x8145 << 2) +-#define RX_SLC_IEN0_OVRD (0x8149 << 2) +-#define RX_SLC_IEN1_OVRD (0x814d << 2) +-#define RX_SLC_QEN0_OVRD (0x8151 << 2) +-#define RX_SLC_QEN1_OVRD (0x8155 << 2) +-#define RX_SLC_EEN0_OVRD (0x8159 << 2) +-#define RX_SLC_EEN1_OVRD (0x815d << 2) +-#define RX_REE_CTRL_DATA_MASK(n) ((0x81bb | ((n) << 9)) << 2) +-#define RX_DIAG_SIGDET_TUNE(n) ((0x81dc | ((n) << 9)) << 2) +-#define RX_DIAG_SC2C_DELAY (0x81e1 << 2) +- +-#define PMA_LANE_CFG (0xc000 << 2) +-#define PIPE_CMN_CTRL1 (0xc001 << 2) +-#define PIPE_CMN_CTRL2 (0xc002 << 2) +-#define PIPE_COM_LOCK_CFG1 (0xc003 << 2) +-#define PIPE_COM_LOCK_CFG2 (0xc004 << 2) +-#define PIPE_RCV_DET_INH (0xc005 << 2) +-#define DP_MODE_CTL (0xc008 << 2) +-#define DP_CLK_CTL (0xc009 << 2) +-#define STS (0xc00F << 2) +-#define PHY_ISO_CMN_CTRL (0xc010 << 2) +-#define PHY_DP_TX_CTL (0xc408 << 2) +-#define PMA_CMN_CTRL1 (0xc800 << 2) +-#define PHY_PMA_ISO_CMN_CTRL (0xc810 << 2) +-#define PHY_ISOLATION_CTRL (0xc81f << 2) +-#define PHY_PMA_ISO_XCVR_CTRL(n) ((0xcc11 | ((n) << 6)) << 2) +-#define PHY_PMA_ISO_LINK_MODE(n) ((0xcc12 | ((n) << 6)) << 2) +-#define PHY_PMA_ISO_PWRST_CTRL(n) ((0xcc13 | ((n) << 6)) << 2) +-#define PHY_PMA_ISO_TX_DATA_LO(n) ((0xcc14 | ((n) << 6)) << 2) +-#define PHY_PMA_ISO_TX_DATA_HI(n) ((0xcc15 | ((n) << 6)) << 2) +-#define PHY_PMA_ISO_RX_DATA_LO(n) ((0xcc16 | ((n) << 6)) << 2) +-#define PHY_PMA_ISO_RX_DATA_HI(n) ((0xcc17 | ((n) << 6)) << 2) +-#define TX_BIST_CTRL(n) ((0x4140 | ((n) << 9)) << 2) +-#define TX_BIST_UDDWR(n) ((0x4141 | ((n) << 9)) << 2) +- +-/* +- * Selects which PLL clock will be driven on the analog high speed +- * clock 0: PLL 0 div 1 +- * clock 1: PLL 1 div 2 +- */ +-#define CLK_PLL_CONFIG 0X30 +-#define CLK_PLL_MASK 0x33 +- +-#define CMN_READY BIT(0) +- +-#define DP_PLL_CLOCK_ENABLE BIT(2) +-#define DP_PLL_ENABLE BIT(0) +-#define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) +-#define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) +-#define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) +- +-#define DP_MODE_A0 BIT(4) +-#define DP_MODE_A2 BIT(6) +-#define DP_MODE_ENTER_A0 0xc101 +-#define DP_MODE_ENTER_A2 0xc104 +- +-#define PHY_MODE_SET_TIMEOUT 100000 +- +-#define PIN_ASSIGN_C_E 0x51d9 +-#define PIN_ASSIGN_D_F 0x5100 +- +-#define MODE_DISCONNECT 0 +-#define MODE_UFP_USB BIT(0) +-#define MODE_DFP_USB BIT(1) +-#define MODE_DFP_DP BIT(2) +- +-struct usb3phy_reg { +- u32 offset; +- u32 enable_bit; +- u32 write_enable; +-}; +- +-struct rockchip_usb3phy_port_cfg { +- struct usb3phy_reg typec_conn_dir; +- struct usb3phy_reg usb3tousb2_en; +- struct usb3phy_reg external_psm; +- struct usb3phy_reg pipe_status; +-}; +- +-struct rockchip_typec_phy { +- struct device *dev; +- void __iomem *base; +- struct extcon_dev *extcon; +- struct regmap *grf_regs; +- struct clk *clk_core; +- struct clk *clk_ref; +- struct reset_control *uphy_rst; +- struct reset_control *pipe_rst; +- struct reset_control *tcphy_rst; +- struct rockchip_usb3phy_port_cfg port_cfgs; +- /* mutex to protect access to individual PHYs */ +- struct mutex lock; +- +- bool flip; +- u8 mode; +-}; +- +-struct phy_reg { +- u16 value; +- u32 addr; +-}; +- +-struct phy_reg usb3_pll_cfg[] = { +- { 0xf0, CMN_PLL0_VCOCAL_INIT }, +- { 0x18, CMN_PLL0_VCOCAL_ITER }, +- { 0xd0, CMN_PLL0_INTDIV }, +- { 0x4a4a, CMN_PLL0_FRACDIV }, +- { 0x34, CMN_PLL0_HIGH_THR }, +- { 0x1ee, CMN_PLL0_SS_CTRL1 }, +- { 0x7f03, CMN_PLL0_SS_CTRL2 }, +- { 0x20, CMN_PLL0_DSM_DIAG }, +- { 0, CMN_DIAG_PLL0_OVRD }, +- { 0, CMN_DIAG_PLL0_FBH_OVRD }, +- { 0, CMN_DIAG_PLL0_FBL_OVRD }, +- { 0x7, CMN_DIAG_PLL0_V2I_TUNE }, +- { 0x45, CMN_DIAG_PLL0_CP_TUNE }, +- { 0x8, CMN_DIAG_PLL0_LF_PROG }, +-}; +- +-struct phy_reg dp_pll_cfg[] = { +- { 0xf0, CMN_PLL1_VCOCAL_INIT }, +- { 0x18, CMN_PLL1_VCOCAL_ITER }, +- { 0x30b9, CMN_PLL1_VCOCAL_START }, +- { 0x21c, CMN_PLL1_INTDIV }, +- { 0, CMN_PLL1_FRACDIV }, +- { 0x5, CMN_PLL1_HIGH_THR }, +- { 0x35, CMN_PLL1_SS_CTRL1 }, +- { 0x7f1e, CMN_PLL1_SS_CTRL2 }, +- { 0x20, CMN_PLL1_DSM_DIAG }, +- { 0, CMN_PLLSM1_USER_DEF_CTRL }, +- { 0, CMN_DIAG_PLL1_OVRD }, +- { 0, CMN_DIAG_PLL1_FBH_OVRD }, +- { 0, CMN_DIAG_PLL1_FBL_OVRD }, +- { 0x6, CMN_DIAG_PLL1_V2I_TUNE }, +- { 0x45, CMN_DIAG_PLL1_CP_TUNE }, +- { 0x8, CMN_DIAG_PLL1_LF_PROG }, +- { 0x100, CMN_DIAG_PLL1_PTATIS_TUNE1 }, +- { 0x7, CMN_DIAG_PLL1_PTATIS_TUNE2 }, +- { 0x4, CMN_DIAG_PLL1_INCLK_CTRL }, +-}; +- +-static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy) +-{ +- u32 i, rdata; +- +- /* +- * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent +- * cmn_psm_clk_dig_div = 2, set the clk division to 2 +- */ +- writel(0x830, tcphy->base + PMA_CMN_CTRL1); +- for (i = 0; i < 4; i++) { +- /* +- * The following PHY configuration assumes a 24 MHz reference +- * clock. +- */ +- writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i)); +- writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i)); +- writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i)); +- } +- +- rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL); +- rdata &= ~CLK_PLL_MASK; +- rdata |= CLK_PLL_CONFIG; +- writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL); +-} +- +-static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy) +-{ +- u32 i; +- +- /* load the configuration of PLL0 */ +- for (i = 0; i < ARRAY_SIZE(usb3_pll_cfg); i++) +- writel(usb3_pll_cfg[i].value, +- tcphy->base + usb3_pll_cfg[i].addr); +-} +- +-static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy) +-{ +- u32 i; +- +- /* set the default mode to RBR */ +- writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR, +- tcphy->base + DP_CLK_CTL); +- +- /* load the configuration of PLL1 */ +- for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++) +- writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr); +-} +- +-static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) +-{ +- writel(0x7799, tcphy->base + TX_PSC_A0(lane)); +- writel(0x7798, tcphy->base + TX_PSC_A1(lane)); +- writel(0x5098, tcphy->base + TX_PSC_A2(lane)); +- writel(0x5098, tcphy->base + TX_PSC_A3(lane)); +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane)); +- writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane)); +-} +- +-static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) +-{ +- writel(0xa6fd, tcphy->base + RX_PSC_A0(lane)); +- writel(0xa6fd, tcphy->base + RX_PSC_A1(lane)); +- writel(0xa410, tcphy->base + RX_PSC_A2(lane)); +- writel(0x2410, tcphy->base + RX_PSC_A3(lane)); +- writel(0x23ff, tcphy->base + RX_PSC_CAL(lane)); +- writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane)); +- writel(0x03e7, tcphy->base + RX_REE_CTRL_DATA_MASK(lane)); +- writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane)); +- writel(0x2010, tcphy->base + RX_PSC_RDY(lane)); +- writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane)); +-} +- +-static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) +-{ +- u16 rdata; +- +- writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane)); +- writel(0x6799, tcphy->base + TX_PSC_A0(lane)); +- writel(0x6798, tcphy->base + TX_PSC_A1(lane)); +- writel(0x98, tcphy->base + TX_PSC_A2(lane)); +- writel(0x98, tcphy->base + TX_PSC_A3(lane)); +- +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane)); +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane)); +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane)); +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane)); +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane)); +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane)); +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane)); +- writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane)); +- writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane)); +- writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane)); +- writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane)); +- writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane)); +- +- writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane)); +- writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane)); +- +- rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane)); +- rdata = (rdata & 0x8fff) | 0x6000; +- writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane)); +-} +- +-static inline int property_enable(struct rockchip_typec_phy *tcphy, +- const struct usb3phy_reg *reg, bool en) +-{ +- u32 mask = 1 << reg->write_enable; +- u32 val = en << reg->enable_bit; +- +- return regmap_write(tcphy->grf_regs, reg->offset, val | mask); +-} +- +-static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) +-{ +- u16 rdata, rdata2, val; +- +- /* disable txda_cal_latch_en for rewrite the calibration values */ +- rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); +- val = rdata & 0xdfff; +- writel(val, tcphy->base + TX_ANA_CTRL_REG_1); +- +- /* +- * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and +- * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it +- * works. +- */ +- rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2); +- rdata = rdata & 0xffc0; +- +- rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL); +- rdata2 = rdata2 & 0x3f; +- +- val = rdata | rdata2; +- writel(val, tcphy->base + TX_DIG_CTRL_REG_2); +- usleep_range(1000, 1050); +- +- /* +- * Enable signal for latch that sample and holds calibration values. +- * Activate this signal for 1 clock cycle to sample new calibration +- * values. +- */ +- rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); +- val = rdata | 0x2000; +- writel(val, tcphy->base + TX_ANA_CTRL_REG_1); +- usleep_range(150, 200); +- +- /* set TX Voltage Level and TX Deemphasis to 0 */ +- writel(0, tcphy->base + PHY_DP_TX_CTL); +- /* re-enable decap */ +- writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2); +- writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2); +- writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1); +- writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1); +- +- writel(0, tcphy->base + TX_ANA_CTRL_REG_5); +- +- /* +- * Programs txda_drv_ldo_prog[15:0], Sets driver LDO +- * voltage 16'h1001 for DP-AUX-TX and RX +- */ +- writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4); +- +- /* re-enables Bandgap reference for LDO */ +- writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1); +- writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1); +- +- /* +- * re-enables the transmitter pre-driver, driver data selection MUX, +- * and receiver detect circuits. +- */ +- writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2); +- writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2); +- +- /* +- * BIT 12: Controls auxda_polarity, which selects the polarity of the +- * xcvr: +- * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull +- * down aux_m) +- * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down +- * aux_p) +- */ +- val = 0xa078; +- if (!tcphy->flip) +- val |= BIT(12); +- writel(val, tcphy->base + TX_ANA_CTRL_REG_1); +- +- writel(0, tcphy->base + TX_ANA_CTRL_REG_3); +- writel(0, tcphy->base + TX_ANA_CTRL_REG_4); +- writel(0, tcphy->base + TX_ANA_CTRL_REG_5); +- +- /* +- * Controls low_power_swing_en, set the voltage swing of the driver +- * to 400mv. The values below are peak to peak (differential) values. +- */ +- writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL); +- writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA); +- +- /* Controls tx_high_z_tm_en */ +- val = readl(tcphy->base + TX_DIG_CTRL_REG_2); +- val |= BIT(15); +- writel(val, tcphy->base + TX_DIG_CTRL_REG_2); +-} +- +-static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) +-{ +- struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; +- int ret, i; +- u32 val; +- +- ret = clk_prepare_enable(tcphy->clk_core); +- if (ret) { +- dev_err(tcphy->dev, "Failed to prepare_enable core clock\n"); +- return ret; +- } +- +- ret = clk_prepare_enable(tcphy->clk_ref); +- if (ret) { +- dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n"); +- goto err_clk_core; +- } +- +- reset_control_deassert(tcphy->tcphy_rst); +- +- property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip); +- +- tcphy_cfg_24m(tcphy); +- +- if (mode == MODE_DFP_DP) { +- tcphy_cfg_dp_pll(tcphy); +- for (i = 0; i < 4; i++) +- tcphy_dp_cfg_lane(tcphy, i); +- +- writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG); +- } else { +- tcphy_cfg_usb3_pll(tcphy); +- tcphy_cfg_dp_pll(tcphy); +- if (tcphy->flip) { +- tcphy_tx_usb3_cfg_lane(tcphy, 3); +- tcphy_rx_usb3_cfg_lane(tcphy, 2); +- tcphy_dp_cfg_lane(tcphy, 0); +- tcphy_dp_cfg_lane(tcphy, 1); +- } else { +- tcphy_tx_usb3_cfg_lane(tcphy, 0); +- tcphy_rx_usb3_cfg_lane(tcphy, 1); +- tcphy_dp_cfg_lane(tcphy, 2); +- tcphy_dp_cfg_lane(tcphy, 3); +- } +- +- writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG); +- } +- +- writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); +- +- reset_control_deassert(tcphy->uphy_rst); +- +- ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1, +- val, val & CMN_READY, 10, +- PHY_MODE_SET_TIMEOUT); +- if (ret < 0) { +- dev_err(tcphy->dev, "wait pma ready timeout\n"); +- ret = -ETIMEDOUT; +- goto err_wait_pma; +- } +- +- reset_control_deassert(tcphy->pipe_rst); +- +- return 0; +- +-err_wait_pma: +- reset_control_assert(tcphy->uphy_rst); +- reset_control_assert(tcphy->tcphy_rst); +- clk_disable_unprepare(tcphy->clk_ref); +-err_clk_core: +- clk_disable_unprepare(tcphy->clk_core); +- return ret; +-} +- +-static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy) +-{ +- reset_control_assert(tcphy->tcphy_rst); +- reset_control_assert(tcphy->uphy_rst); +- reset_control_assert(tcphy->pipe_rst); +- clk_disable_unprepare(tcphy->clk_core); +- clk_disable_unprepare(tcphy->clk_ref); +-} +- +-static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) +-{ +- struct extcon_dev *edev = tcphy->extcon; +- union extcon_property_value property; +- unsigned int id; +- bool dfp, ufp, dp; +- u8 mode; +- int ret; +- +- ufp = extcon_get_state(edev, EXTCON_USB); +- dfp = extcon_get_state(edev, EXTCON_USB_HOST); +- dp = extcon_get_state(edev, EXTCON_DISP_DP); +- +- mode = MODE_DFP_USB; +- id = EXTCON_USB_HOST; +- +- if (ufp) { +- mode = MODE_UFP_USB; +- id = EXTCON_USB; +- } else if (dp) { +- mode = MODE_DFP_DP; +- id = EXTCON_DISP_DP; +- +- ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS, +- &property); +- if (ret) { +- dev_err(tcphy->dev, "get superspeed property failed\n"); +- return ret; +- } +- +- if (property.intval) +- mode |= MODE_DFP_USB; +- } +- +- ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY, +- &property); +- if (ret) { +- dev_err(tcphy->dev, "get polarity property failed\n"); +- return ret; +- } +- +- tcphy->flip = property.intval ? 1 : 0; +- +- return mode; +-} +- +-static int rockchip_usb3_phy_power_on(struct phy *phy) +-{ +- struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); +- struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; +- const struct usb3phy_reg *reg = &cfg->pipe_status; +- int timeout, new_mode, ret = 0; +- u32 val; +- +- mutex_lock(&tcphy->lock); +- +- new_mode = tcphy_get_mode(tcphy); +- if (new_mode < 0) { +- ret = new_mode; +- goto unlock_ret; +- } +- +- /* DP-only mode; fall back to USB2 */ +- if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) +- goto unlock_ret; +- +- if (tcphy->mode == new_mode) +- goto unlock_ret; +- +- if (tcphy->mode == MODE_DISCONNECT) +- tcphy_phy_init(tcphy, new_mode); +- +- /* wait TCPHY for pipe ready */ +- for (timeout = 0; timeout < 100; timeout++) { +- regmap_read(tcphy->grf_regs, reg->offset, &val); +- if (!(val & BIT(reg->enable_bit))) { +- tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB); +- goto unlock_ret; +- } +- usleep_range(10, 20); +- } +- +- if (tcphy->mode == MODE_DISCONNECT) +- tcphy_phy_deinit(tcphy); +- +- ret = -ETIMEDOUT; +- +-unlock_ret: +- mutex_unlock(&tcphy->lock); +- return ret; +-} +- +-static int rockchip_usb3_phy_power_off(struct phy *phy) +-{ +- struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); +- +- mutex_lock(&tcphy->lock); +- +- if (tcphy->mode == MODE_DISCONNECT) +- goto unlock; +- +- tcphy->mode &= ~(MODE_UFP_USB | MODE_DFP_USB); +- if (tcphy->mode == MODE_DISCONNECT) +- tcphy_phy_deinit(tcphy); +- +-unlock: +- mutex_unlock(&tcphy->lock); +- return 0; +-} +- +-static const struct phy_ops rockchip_usb3_phy_ops = { +- .power_on = rockchip_usb3_phy_power_on, +- .power_off = rockchip_usb3_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int rockchip_dp_phy_power_on(struct phy *phy) +-{ +- struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); +- int new_mode, ret = 0; +- u32 val; +- +- mutex_lock(&tcphy->lock); +- +- new_mode = tcphy_get_mode(tcphy); +- if (new_mode < 0) { +- ret = new_mode; +- goto unlock_ret; +- } +- +- if (!(new_mode & MODE_DFP_DP)) { +- ret = -ENODEV; +- goto unlock_ret; +- } +- +- if (tcphy->mode == new_mode) +- goto unlock_ret; +- +- /* +- * If the PHY has been power on, but the mode is not DP only mode, +- * re-init the PHY for setting all of 4 lanes to DP. +- */ +- if (new_mode == MODE_DFP_DP && tcphy->mode != MODE_DISCONNECT) { +- tcphy_phy_deinit(tcphy); +- tcphy_phy_init(tcphy, new_mode); +- } else if (tcphy->mode == MODE_DISCONNECT) { +- tcphy_phy_init(tcphy, new_mode); +- } +- +- ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, +- val, val & DP_MODE_A2, 1000, +- PHY_MODE_SET_TIMEOUT); +- if (ret < 0) { +- dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n"); +- goto power_on_finish; +- } +- +- tcphy_dp_aux_calibration(tcphy); +- +- writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL); +- +- ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, +- val, val & DP_MODE_A0, 1000, +- PHY_MODE_SET_TIMEOUT); +- if (ret < 0) { +- writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); +- dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n"); +- goto power_on_finish; +- } +- +- tcphy->mode |= MODE_DFP_DP; +- +-power_on_finish: +- if (tcphy->mode == MODE_DISCONNECT) +- tcphy_phy_deinit(tcphy); +-unlock_ret: +- mutex_unlock(&tcphy->lock); +- return ret; +-} +- +-static int rockchip_dp_phy_power_off(struct phy *phy) +-{ +- struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); +- +- mutex_lock(&tcphy->lock); +- +- if (tcphy->mode == MODE_DISCONNECT) +- goto unlock; +- +- tcphy->mode &= ~MODE_DFP_DP; +- +- writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); +- +- if (tcphy->mode == MODE_DISCONNECT) +- tcphy_phy_deinit(tcphy); +- +-unlock: +- mutex_unlock(&tcphy->lock); +- return 0; +-} +- +-static const struct phy_ops rockchip_dp_phy_ops = { +- .power_on = rockchip_dp_phy_power_on, +- .power_off = rockchip_dp_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int tcphy_get_param(struct device *dev, +- struct usb3phy_reg *reg, +- const char *name) +-{ +- u32 buffer[3]; +- int ret; +- +- ret = of_property_read_u32_array(dev->of_node, name, buffer, 3); +- if (ret) { +- dev_err(dev, "Can not parse %s\n", name); +- return ret; +- } +- +- reg->offset = buffer[0]; +- reg->enable_bit = buffer[1]; +- reg->write_enable = buffer[2]; +- return 0; +-} +- +-static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy, +- struct device *dev) +-{ +- struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; +- int ret; +- +- ret = tcphy_get_param(dev, &cfg->typec_conn_dir, +- "rockchip,typec-conn-dir"); +- if (ret) +- return ret; +- +- ret = tcphy_get_param(dev, &cfg->usb3tousb2_en, +- "rockchip,usb3tousb2-en"); +- if (ret) +- return ret; +- +- ret = tcphy_get_param(dev, &cfg->external_psm, +- "rockchip,external-psm"); +- if (ret) +- return ret; +- +- ret = tcphy_get_param(dev, &cfg->pipe_status, +- "rockchip,pipe-status"); +- if (ret) +- return ret; +- +- tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node, +- "rockchip,grf"); +- if (IS_ERR(tcphy->grf_regs)) { +- dev_err(dev, "could not find grf dt node\n"); +- return PTR_ERR(tcphy->grf_regs); +- } +- +- tcphy->clk_core = devm_clk_get(dev, "tcpdcore"); +- if (IS_ERR(tcphy->clk_core)) { +- dev_err(dev, "could not get uphy core clock\n"); +- return PTR_ERR(tcphy->clk_core); +- } +- +- tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref"); +- if (IS_ERR(tcphy->clk_ref)) { +- dev_err(dev, "could not get uphy ref clock\n"); +- return PTR_ERR(tcphy->clk_ref); +- } +- +- tcphy->uphy_rst = devm_reset_control_get(dev, "uphy"); +- if (IS_ERR(tcphy->uphy_rst)) { +- dev_err(dev, "no uphy_rst reset control found\n"); +- return PTR_ERR(tcphy->uphy_rst); +- } +- +- tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe"); +- if (IS_ERR(tcphy->pipe_rst)) { +- dev_err(dev, "no pipe_rst reset control found\n"); +- return PTR_ERR(tcphy->pipe_rst); +- } +- +- tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy"); +- if (IS_ERR(tcphy->tcphy_rst)) { +- dev_err(dev, "no tcphy_rst reset control found\n"); +- return PTR_ERR(tcphy->tcphy_rst); +- } +- +- return 0; +-} +- +-static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) +-{ +- struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; +- +- reset_control_assert(tcphy->tcphy_rst); +- reset_control_assert(tcphy->uphy_rst); +- reset_control_assert(tcphy->pipe_rst); +- +- /* select external psm clock */ +- property_enable(tcphy, &cfg->external_psm, 1); +- property_enable(tcphy, &cfg->usb3tousb2_en, 0); +- +- tcphy->mode = MODE_DISCONNECT; +-} +- +-static int rockchip_typec_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct device_node *np = dev->of_node; +- struct device_node *child_np; +- struct rockchip_typec_phy *tcphy; +- struct phy_provider *phy_provider; +- struct resource *res; +- int ret; +- +- tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL); +- if (!tcphy) +- return -ENOMEM; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- tcphy->base = devm_ioremap_resource(dev, res); +- if (IS_ERR(tcphy->base)) +- return PTR_ERR(tcphy->base); +- +- ret = tcphy_parse_dt(tcphy, dev); +- if (ret) +- return ret; +- +- tcphy->dev = dev; +- platform_set_drvdata(pdev, tcphy); +- mutex_init(&tcphy->lock); +- +- typec_phy_pre_init(tcphy); +- +- tcphy->extcon = extcon_get_edev_by_phandle(dev, 0); +- if (IS_ERR(tcphy->extcon)) { +- if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER) +- dev_err(dev, "Invalid or missing extcon\n"); +- return PTR_ERR(tcphy->extcon); +- } +- +- pm_runtime_enable(dev); +- +- for_each_available_child_of_node(np, child_np) { +- struct phy *phy; +- +- if (!of_node_cmp(child_np->name, "dp-port")) +- phy = devm_phy_create(dev, child_np, +- &rockchip_dp_phy_ops); +- else if (!of_node_cmp(child_np->name, "usb3-port")) +- phy = devm_phy_create(dev, child_np, +- &rockchip_usb3_phy_ops); +- else +- continue; +- +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create phy: %s\n", +- child_np->name); +- return PTR_ERR(phy); +- } +- +- phy_set_drvdata(phy, tcphy); +- } +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) { +- dev_err(dev, "Failed to register phy provider\n"); +- return PTR_ERR(phy_provider); +- } +- +- return 0; +-} +- +-static int rockchip_typec_phy_remove(struct platform_device *pdev) +-{ +- pm_runtime_disable(&pdev->dev); +- +- return 0; +-} +- +-static const struct of_device_id rockchip_typec_phy_dt_ids[] = { +- { .compatible = "rockchip,rk3399-typec-phy" }, +- {} +-}; +- +-MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids); +- +-static struct platform_driver rockchip_typec_phy_driver = { +- .probe = rockchip_typec_phy_probe, +- .remove = rockchip_typec_phy_remove, +- .driver = { +- .name = "rockchip-typec-phy", +- .of_match_table = rockchip_typec_phy_dt_ids, +- }, +-}; +- +-module_platform_driver(rockchip_typec_phy_driver); +- +-MODULE_AUTHOR("Chris Zhong "); +-MODULE_AUTHOR("Kever Yang "); +-MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c +deleted file mode 100644 +index 3378eeb7a562..000000000000 +--- a/drivers/phy/phy-rockchip-usb.c ++++ /dev/null +@@ -1,540 +0,0 @@ +-/* +- * Rockchip usb PHY driver +- * +- * Copyright (C) 2014 Yunzhi Li +- * Copyright (C) 2014 ROCKCHIP, Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-static int enable_usb_uart; +- +-#define HIWORD_UPDATE(val, mask) \ +- ((val) | (mask) << 16) +- +-#define UOC_CON0_SIDDQ BIT(13) +- +-struct rockchip_usb_phys { +- int reg; +- const char *pll_name; +-}; +- +-struct rockchip_usb_phy_base; +-struct rockchip_usb_phy_pdata { +- struct rockchip_usb_phys *phys; +- int (*init_usb_uart)(struct regmap *grf); +- int usb_uart_phy; +-}; +- +-struct rockchip_usb_phy_base { +- struct device *dev; +- struct regmap *reg_base; +- const struct rockchip_usb_phy_pdata *pdata; +-}; +- +-struct rockchip_usb_phy { +- struct rockchip_usb_phy_base *base; +- struct device_node *np; +- unsigned int reg_offset; +- struct clk *clk; +- struct clk *clk480m; +- struct clk_hw clk480m_hw; +- struct phy *phy; +- bool uart_enabled; +- struct reset_control *reset; +- struct regulator *vbus; +-}; +- +-static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, +- bool siddq) +-{ +- u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ); +- +- return regmap_write(phy->base->reg_base, phy->reg_offset, val); +-} +- +-static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, +- unsigned long parent_rate) +-{ +- return 480000000; +-} +- +-static void rockchip_usb_phy480m_disable(struct clk_hw *hw) +-{ +- struct rockchip_usb_phy *phy = container_of(hw, +- struct rockchip_usb_phy, +- clk480m_hw); +- +- if (phy->vbus) +- regulator_disable(phy->vbus); +- +- /* Power down usb phy analog blocks by set siddq 1 */ +- rockchip_usb_phy_power(phy, 1); +-} +- +-static int rockchip_usb_phy480m_enable(struct clk_hw *hw) +-{ +- struct rockchip_usb_phy *phy = container_of(hw, +- struct rockchip_usb_phy, +- clk480m_hw); +- +- /* Power up usb phy analog blocks by set siddq 0 */ +- return rockchip_usb_phy_power(phy, 0); +-} +- +-static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw) +-{ +- struct rockchip_usb_phy *phy = container_of(hw, +- struct rockchip_usb_phy, +- clk480m_hw); +- int ret; +- u32 val; +- +- ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val); +- if (ret < 0) +- return ret; +- +- return (val & UOC_CON0_SIDDQ) ? 0 : 1; +-} +- +-static const struct clk_ops rockchip_usb_phy480m_ops = { +- .enable = rockchip_usb_phy480m_enable, +- .disable = rockchip_usb_phy480m_disable, +- .is_enabled = rockchip_usb_phy480m_is_enabled, +- .recalc_rate = rockchip_usb_phy480m_recalc_rate, +-}; +- +-static int rockchip_usb_phy_power_off(struct phy *_phy) +-{ +- struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); +- +- if (phy->uart_enabled) +- return -EBUSY; +- +- clk_disable_unprepare(phy->clk480m); +- +- return 0; +-} +- +-static int rockchip_usb_phy_power_on(struct phy *_phy) +-{ +- struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); +- +- if (phy->uart_enabled) +- return -EBUSY; +- +- if (phy->vbus) { +- int ret; +- +- ret = regulator_enable(phy->vbus); +- if (ret) +- return ret; +- } +- +- return clk_prepare_enable(phy->clk480m); +-} +- +-static int rockchip_usb_phy_reset(struct phy *_phy) +-{ +- struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); +- +- if (phy->reset) { +- reset_control_assert(phy->reset); +- udelay(10); +- reset_control_deassert(phy->reset); +- } +- +- return 0; +-} +- +-static const struct phy_ops ops = { +- .power_on = rockchip_usb_phy_power_on, +- .power_off = rockchip_usb_phy_power_off, +- .reset = rockchip_usb_phy_reset, +- .owner = THIS_MODULE, +-}; +- +-static void rockchip_usb_phy_action(void *data) +-{ +- struct rockchip_usb_phy *rk_phy = data; +- +- if (!rk_phy->uart_enabled) { +- of_clk_del_provider(rk_phy->np); +- clk_unregister(rk_phy->clk480m); +- } +- +- if (rk_phy->clk) +- clk_put(rk_phy->clk); +-} +- +-static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, +- struct device_node *child) +-{ +- struct rockchip_usb_phy *rk_phy; +- unsigned int reg_offset; +- const char *clk_name; +- struct clk_init_data init; +- int err, i; +- +- rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL); +- if (!rk_phy) +- return -ENOMEM; +- +- rk_phy->base = base; +- rk_phy->np = child; +- +- if (of_property_read_u32(child, "reg", ®_offset)) { +- dev_err(base->dev, "missing reg property in node %s\n", +- child->name); +- return -EINVAL; +- } +- +- rk_phy->reset = of_reset_control_get(child, "phy-reset"); +- if (IS_ERR(rk_phy->reset)) +- rk_phy->reset = NULL; +- +- rk_phy->reg_offset = reg_offset; +- +- rk_phy->clk = of_clk_get_by_name(child, "phyclk"); +- if (IS_ERR(rk_phy->clk)) +- rk_phy->clk = NULL; +- +- i = 0; +- init.name = NULL; +- while (base->pdata->phys[i].reg) { +- if (base->pdata->phys[i].reg == reg_offset) { +- init.name = base->pdata->phys[i].pll_name; +- break; +- } +- i++; +- } +- +- if (!init.name) { +- dev_err(base->dev, "phy data not found\n"); +- return -EINVAL; +- } +- +- if (enable_usb_uart && base->pdata->usb_uart_phy == i) { +- dev_dbg(base->dev, "phy%d used as uart output\n", i); +- rk_phy->uart_enabled = true; +- } else { +- if (rk_phy->clk) { +- clk_name = __clk_get_name(rk_phy->clk); +- init.flags = 0; +- init.parent_names = &clk_name; +- init.num_parents = 1; +- } else { +- init.flags = 0; +- init.parent_names = NULL; +- init.num_parents = 0; +- } +- +- init.ops = &rockchip_usb_phy480m_ops; +- rk_phy->clk480m_hw.init = &init; +- +- rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); +- if (IS_ERR(rk_phy->clk480m)) { +- err = PTR_ERR(rk_phy->clk480m); +- goto err_clk; +- } +- +- err = of_clk_add_provider(child, of_clk_src_simple_get, +- rk_phy->clk480m); +- if (err < 0) +- goto err_clk_prov; +- } +- +- err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action, +- rk_phy); +- if (err) +- return err; +- +- rk_phy->phy = devm_phy_create(base->dev, child, &ops); +- if (IS_ERR(rk_phy->phy)) { +- dev_err(base->dev, "failed to create PHY\n"); +- return PTR_ERR(rk_phy->phy); +- } +- phy_set_drvdata(rk_phy->phy, rk_phy); +- +- rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus"); +- if (IS_ERR(rk_phy->vbus)) { +- if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER) +- return PTR_ERR(rk_phy->vbus); +- rk_phy->vbus = NULL; +- } +- +- /* +- * When acting as uart-pipe, just keep clock on otherwise +- * only power up usb phy when it use, so disable it when init +- */ +- if (rk_phy->uart_enabled) +- return clk_prepare_enable(rk_phy->clk); +- else +- return rockchip_usb_phy_power(rk_phy, 1); +- +-err_clk_prov: +- if (!rk_phy->uart_enabled) +- clk_unregister(rk_phy->clk480m); +-err_clk: +- if (rk_phy->clk) +- clk_put(rk_phy->clk); +- return err; +-} +- +-static const struct rockchip_usb_phy_pdata rk3066a_pdata = { +- .phys = (struct rockchip_usb_phys[]){ +- { .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" }, +- { .reg = 0x188, .pll_name = "sclk_otgphy1_480m" }, +- { /* sentinel */ } +- }, +-}; +- +-static const struct rockchip_usb_phy_pdata rk3188_pdata = { +- .phys = (struct rockchip_usb_phys[]){ +- { .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" }, +- { .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" }, +- { /* sentinel */ } +- }, +-}; +- +-#define RK3288_UOC0_CON0 0x320 +-#define RK3288_UOC0_CON0_COMMON_ON_N BIT(0) +-#define RK3288_UOC0_CON0_DISABLE BIT(4) +- +-#define RK3288_UOC0_CON2 0x328 +-#define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2) +- +-#define RK3288_UOC0_CON3 0x32c +-#define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0) +-#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1) +-#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1) +-#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3) +-#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3) +-#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5) +-#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6) +-#define RK3288_UOC0_CON3_BYPASSSEL BIT(7) +- +-/* +- * Enable the bypass of uart2 data through the otg usb phy. +- * Original description in the TRM. +- * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1. +- * 2. Disable the pull-up resistance on the D+ line by setting +- * OPMODE0[1:0] to 2’b01. +- * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend +- * mode, set COMMONONN to 1’b1. +- * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0. +- * 5. Set BYPASSSEL0 to 1’b1. +- * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0. +- * To receive data, monitor FSVPLUS0. +- * +- * The actual code in the vendor kernel does some things differently. +- */ +-static int __init rk3288_init_usb_uart(struct regmap *grf) +-{ +- u32 val; +- int ret; +- +- /* +- * COMMON_ON and DISABLE settings are described in the TRM, +- * but were not present in the original code. +- * Also disable the analog phy components to save power. +- */ +- val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N +- | RK3288_UOC0_CON0_DISABLE +- | UOC_CON0_SIDDQ, +- RK3288_UOC0_CON0_COMMON_ON_N +- | RK3288_UOC0_CON0_DISABLE +- | UOC_CON0_SIDDQ); +- ret = regmap_write(grf, RK3288_UOC0_CON0, val); +- if (ret) +- return ret; +- +- val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL, +- RK3288_UOC0_CON2_SOFT_CON_SEL); +- ret = regmap_write(grf, RK3288_UOC0_CON2, val); +- if (ret) +- return ret; +- +- val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING +- | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC +- | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED, +- RK3288_UOC0_CON3_UTMI_SUSPENDN +- | RK3288_UOC0_CON3_UTMI_OPMODE_MASK +- | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK +- | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED); +- ret = regmap_write(grf, RK3288_UOC0_CON3, val); +- if (ret) +- return ret; +- +- val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL +- | RK3288_UOC0_CON3_BYPASSDMEN, +- RK3288_UOC0_CON3_BYPASSSEL +- | RK3288_UOC0_CON3_BYPASSDMEN); +- ret = regmap_write(grf, RK3288_UOC0_CON3, val); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static const struct rockchip_usb_phy_pdata rk3288_pdata = { +- .phys = (struct rockchip_usb_phys[]){ +- { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, +- { .reg = 0x334, .pll_name = "sclk_otgphy1_480m" }, +- { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, +- { /* sentinel */ } +- }, +- .init_usb_uart = rk3288_init_usb_uart, +- .usb_uart_phy = 0, +-}; +- +-static int rockchip_usb_phy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct rockchip_usb_phy_base *phy_base; +- struct phy_provider *phy_provider; +- const struct of_device_id *match; +- struct device_node *child; +- int err; +- +- phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL); +- if (!phy_base) +- return -ENOMEM; +- +- match = of_match_device(dev->driver->of_match_table, dev); +- if (!match || !match->data) { +- dev_err(dev, "missing phy data\n"); +- return -EINVAL; +- } +- +- phy_base->pdata = match->data; +- +- phy_base->dev = dev; +- phy_base->reg_base = ERR_PTR(-ENODEV); +- if (dev->parent && dev->parent->of_node) +- phy_base->reg_base = syscon_node_to_regmap( +- dev->parent->of_node); +- if (IS_ERR(phy_base->reg_base)) +- phy_base->reg_base = syscon_regmap_lookup_by_phandle( +- dev->of_node, "rockchip,grf"); +- if (IS_ERR(phy_base->reg_base)) { +- dev_err(&pdev->dev, "Missing rockchip,grf property\n"); +- return PTR_ERR(phy_base->reg_base); +- } +- +- for_each_available_child_of_node(dev->of_node, child) { +- err = rockchip_usb_phy_init(phy_base, child); +- if (err) { +- of_node_put(child); +- return err; +- } +- } +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id rockchip_usb_phy_dt_ids[] = { +- { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata }, +- { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata }, +- { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata }, +- {} +-}; +- +-MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids); +- +-static struct platform_driver rockchip_usb_driver = { +- .probe = rockchip_usb_phy_probe, +- .driver = { +- .name = "rockchip-usb-phy", +- .of_match_table = rockchip_usb_phy_dt_ids, +- }, +-}; +- +-module_platform_driver(rockchip_usb_driver); +- +-#ifndef MODULE +-static int __init rockchip_init_usb_uart(void) +-{ +- const struct of_device_id *match; +- const struct rockchip_usb_phy_pdata *data; +- struct device_node *np; +- struct regmap *grf; +- int ret; +- +- if (!enable_usb_uart) +- return 0; +- +- np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids, +- &match); +- if (!np) { +- pr_err("%s: failed to find usbphy node\n", __func__); +- return -ENOTSUPP; +- } +- +- pr_debug("%s: using settings for %s\n", __func__, match->compatible); +- data = match->data; +- +- if (!data->init_usb_uart) { +- pr_err("%s: usb-uart not available on %s\n", +- __func__, match->compatible); +- return -ENOTSUPP; +- } +- +- grf = ERR_PTR(-ENODEV); +- if (np->parent) +- grf = syscon_node_to_regmap(np->parent); +- if (IS_ERR(grf)) +- grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); +- if (IS_ERR(grf)) { +- pr_err("%s: Missing rockchip,grf property, %lu\n", +- __func__, PTR_ERR(grf)); +- return PTR_ERR(grf); +- } +- +- ret = data->init_usb_uart(grf); +- if (ret) { +- pr_err("%s: could not init usb_uart, %d\n", __func__, ret); +- enable_usb_uart = 0; +- return ret; +- } +- +- return 0; +-} +-early_initcall(rockchip_init_usb_uart); +- +-static int __init rockchip_usb_uart(char *buf) +-{ +- enable_usb_uart = true; +- return 0; +-} +-early_param("rockchip.usb_uart", rockchip_usb_uart); +-#endif +- +-MODULE_AUTHOR("Yunzhi Li "); +-MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-s5pv210-usb2.c b/drivers/phy/phy-s5pv210-usb2.c +deleted file mode 100644 +index f6f72339bbc3..000000000000 +--- a/drivers/phy/phy-s5pv210-usb2.c ++++ /dev/null +@@ -1,187 +0,0 @@ +-/* +- * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support +- * +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Authors: Kamil Debski +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include "phy-samsung-usb2.h" +- +-/* Exynos USB PHY registers */ +- +-/* PHY power control */ +-#define S5PV210_UPHYPWR 0x0 +- +-#define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0) +-#define S5PV210_UPHYPWR_PHY0_PWR BIT(3) +-#define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4) +-#define S5PV210_UPHYPWR_PHY0 ( \ +- S5PV210_UPHYPWR_PHY0_SUSPEND | \ +- S5PV210_UPHYPWR_PHY0_PWR | \ +- S5PV210_UPHYPWR_PHY0_OTG_PWR) +- +-#define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6) +-#define S5PV210_UPHYPWR_PHY1_PWR BIT(7) +-#define S5PV210_UPHYPWR_PHY1 ( \ +- S5PV210_UPHYPWR_PHY1_SUSPEND | \ +- S5PV210_UPHYPWR_PHY1_PWR) +- +-/* PHY clock control */ +-#define S5PV210_UPHYCLK 0x4 +- +-#define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) +-#define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) +-#define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) +-#define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) +- +-#define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2) +-#define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4) +-#define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7) +- +-/* PHY reset control */ +-#define S5PV210_UPHYRST 0x8 +- +-#define S5PV210_URSTCON_PHY0 BIT(0) +-#define S5PV210_URSTCON_OTG_HLINK BIT(1) +-#define S5PV210_URSTCON_OTG_PHYLINK BIT(2) +-#define S5PV210_URSTCON_PHY1_ALL BIT(3) +-#define S5PV210_URSTCON_HOST_LINK_ALL BIT(4) +- +-/* Isolation, configured in the power management unit */ +-#define S5PV210_USB_ISOL_OFFSET 0x680c +-#define S5PV210_USB_ISOL_DEVICE BIT(0) +-#define S5PV210_USB_ISOL_HOST BIT(1) +- +- +-enum s5pv210_phy_id { +- S5PV210_DEVICE, +- S5PV210_HOST, +- S5PV210_NUM_PHYS, +-}; +- +-/* +- * s5pv210_rate_to_clk() converts the supplied clock rate to the value that +- * can be written to the phy register. +- */ +-static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg) +-{ +- switch (rate) { +- case 12 * MHZ: +- *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ; +- break; +- case 24 * MHZ: +- *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ; +- break; +- case 48 * MHZ: +- *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 mask; +- +- switch (inst->cfg->id) { +- case S5PV210_DEVICE: +- mask = S5PV210_USB_ISOL_DEVICE; +- break; +- case S5PV210_HOST: +- mask = S5PV210_USB_ISOL_HOST; +- break; +- default: +- return; +- } +- +- regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, +- mask, on ? 0 : mask); +-} +- +-static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) +-{ +- struct samsung_usb2_phy_driver *drv = inst->drv; +- u32 rstbits = 0; +- u32 phypwr = 0; +- u32 rst; +- u32 pwr; +- +- switch (inst->cfg->id) { +- case S5PV210_DEVICE: +- phypwr = S5PV210_UPHYPWR_PHY0; +- rstbits = S5PV210_URSTCON_PHY0; +- break; +- case S5PV210_HOST: +- phypwr = S5PV210_UPHYPWR_PHY1; +- rstbits = S5PV210_URSTCON_PHY1_ALL | +- S5PV210_URSTCON_HOST_LINK_ALL; +- break; +- } +- +- if (on) { +- writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); +- +- pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); +- pwr &= ~phypwr; +- writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); +- +- rst = readl(drv->reg_phy + S5PV210_UPHYRST); +- rst |= rstbits; +- writel(rst, drv->reg_phy + S5PV210_UPHYRST); +- udelay(10); +- rst &= ~rstbits; +- writel(rst, drv->reg_phy + S5PV210_UPHYRST); +- } else { +- pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); +- pwr |= phypwr; +- writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); +- } +-} +- +-static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst) +-{ +- s5pv210_isol(inst, 0); +- s5pv210_phy_pwr(inst, 1); +- +- return 0; +-} +- +-static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst) +-{ +- s5pv210_phy_pwr(inst, 0); +- s5pv210_isol(inst, 1); +- +- return 0; +-} +- +-static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = { +- [S5PV210_DEVICE] = { +- .label = "device", +- .id = S5PV210_DEVICE, +- .power_on = s5pv210_power_on, +- .power_off = s5pv210_power_off, +- }, +- [S5PV210_HOST] = { +- .label = "host", +- .id = S5PV210_HOST, +- .power_on = s5pv210_power_on, +- .power_off = s5pv210_power_off, +- }, +-}; +- +-const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = { +- .num_phys = ARRAY_SIZE(s5pv210_phys), +- .phys = s5pv210_phys, +- .rate_to_clk = s5pv210_rate_to_clk, +-}; +diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c +deleted file mode 100644 +index 1d22d93b552d..000000000000 +--- a/drivers/phy/phy-samsung-usb2.c ++++ /dev/null +@@ -1,267 +0,0 @@ +-/* +- * Samsung SoC USB 1.1/2.0 PHY driver +- * +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Author: Kamil Debski +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "phy-samsung-usb2.h" +- +-static int samsung_usb2_phy_power_on(struct phy *phy) +-{ +- struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); +- struct samsung_usb2_phy_driver *drv = inst->drv; +- int ret; +- +- dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n", +- inst->cfg->label); +- +- if (drv->vbus) { +- ret = regulator_enable(drv->vbus); +- if (ret) +- goto err_regulator; +- } +- +- ret = clk_prepare_enable(drv->clk); +- if (ret) +- goto err_main_clk; +- ret = clk_prepare_enable(drv->ref_clk); +- if (ret) +- goto err_instance_clk; +- if (inst->cfg->power_on) { +- spin_lock(&drv->lock); +- ret = inst->cfg->power_on(inst); +- spin_unlock(&drv->lock); +- if (ret) +- goto err_power_on; +- } +- +- return 0; +- +-err_power_on: +- clk_disable_unprepare(drv->ref_clk); +-err_instance_clk: +- clk_disable_unprepare(drv->clk); +-err_main_clk: +- if (drv->vbus) +- regulator_disable(drv->vbus); +-err_regulator: +- return ret; +-} +- +-static int samsung_usb2_phy_power_off(struct phy *phy) +-{ +- struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); +- struct samsung_usb2_phy_driver *drv = inst->drv; +- int ret = 0; +- +- dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n", +- inst->cfg->label); +- if (inst->cfg->power_off) { +- spin_lock(&drv->lock); +- ret = inst->cfg->power_off(inst); +- spin_unlock(&drv->lock); +- if (ret) +- return ret; +- } +- clk_disable_unprepare(drv->ref_clk); +- clk_disable_unprepare(drv->clk); +- if (drv->vbus) +- ret = regulator_disable(drv->vbus); +- +- return ret; +-} +- +-static const struct phy_ops samsung_usb2_phy_ops = { +- .power_on = samsung_usb2_phy_power_on, +- .power_off = samsung_usb2_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static struct phy *samsung_usb2_phy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct samsung_usb2_phy_driver *drv; +- +- drv = dev_get_drvdata(dev); +- if (!drv) +- return ERR_PTR(-EINVAL); +- +- if (WARN_ON(args->args[0] >= drv->cfg->num_phys)) +- return ERR_PTR(-ENODEV); +- +- return drv->instances[args->args[0]].phy; +-} +- +-static const struct of_device_id samsung_usb2_phy_of_match[] = { +-#ifdef CONFIG_PHY_EXYNOS4X12_USB2 +- { +- .compatible = "samsung,exynos3250-usb2-phy", +- .data = &exynos3250_usb2_phy_config, +- }, +-#endif +-#ifdef CONFIG_PHY_EXYNOS4210_USB2 +- { +- .compatible = "samsung,exynos4210-usb2-phy", +- .data = &exynos4210_usb2_phy_config, +- }, +-#endif +-#ifdef CONFIG_PHY_EXYNOS4X12_USB2 +- { +- .compatible = "samsung,exynos4x12-usb2-phy", +- .data = &exynos4x12_usb2_phy_config, +- }, +-#endif +-#ifdef CONFIG_PHY_EXYNOS5250_USB2 +- { +- .compatible = "samsung,exynos5250-usb2-phy", +- .data = &exynos5250_usb2_phy_config, +- }, +-#endif +-#ifdef CONFIG_PHY_S5PV210_USB2 +- { +- .compatible = "samsung,s5pv210-usb2-phy", +- .data = &s5pv210_usb2_phy_config, +- }, +-#endif +- { }, +-}; +-MODULE_DEVICE_TABLE(of, samsung_usb2_phy_of_match); +- +-static int samsung_usb2_phy_probe(struct platform_device *pdev) +-{ +- const struct of_device_id *match; +- const struct samsung_usb2_phy_config *cfg; +- struct device *dev = &pdev->dev; +- struct phy_provider *phy_provider; +- struct resource *mem; +- struct samsung_usb2_phy_driver *drv; +- int i, ret; +- +- if (!pdev->dev.of_node) { +- dev_err(dev, "This driver is required to be instantiated from device tree\n"); +- return -EINVAL; +- } +- +- match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node); +- if (!match) { +- dev_err(dev, "of_match_node() failed\n"); +- return -EINVAL; +- } +- cfg = match->data; +- +- drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) + +- cfg->num_phys * sizeof(struct samsung_usb2_phy_instance), +- GFP_KERNEL); +- if (!drv) +- return -ENOMEM; +- +- dev_set_drvdata(dev, drv); +- spin_lock_init(&drv->lock); +- +- drv->cfg = cfg; +- drv->dev = dev; +- +- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- drv->reg_phy = devm_ioremap_resource(dev, mem); +- if (IS_ERR(drv->reg_phy)) { +- dev_err(dev, "Failed to map register memory (phy)\n"); +- return PTR_ERR(drv->reg_phy); +- } +- +- drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, +- "samsung,pmureg-phandle"); +- if (IS_ERR(drv->reg_pmu)) { +- dev_err(dev, "Failed to map PMU registers (via syscon)\n"); +- return PTR_ERR(drv->reg_pmu); +- } +- +- if (drv->cfg->has_mode_switch) { +- drv->reg_sys = syscon_regmap_lookup_by_phandle( +- pdev->dev.of_node, "samsung,sysreg-phandle"); +- if (IS_ERR(drv->reg_sys)) { +- dev_err(dev, "Failed to map system registers (via syscon)\n"); +- return PTR_ERR(drv->reg_sys); +- } +- } +- +- drv->clk = devm_clk_get(dev, "phy"); +- if (IS_ERR(drv->clk)) { +- dev_err(dev, "Failed to get clock of phy controller\n"); +- return PTR_ERR(drv->clk); +- } +- +- drv->ref_clk = devm_clk_get(dev, "ref"); +- if (IS_ERR(drv->ref_clk)) { +- dev_err(dev, "Failed to get reference clock for the phy controller\n"); +- return PTR_ERR(drv->ref_clk); +- } +- +- drv->ref_rate = clk_get_rate(drv->ref_clk); +- if (drv->cfg->rate_to_clk) { +- ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val); +- if (ret) +- return ret; +- } +- +- drv->vbus = devm_regulator_get(dev, "vbus"); +- if (IS_ERR(drv->vbus)) { +- ret = PTR_ERR(drv->vbus); +- if (ret == -EPROBE_DEFER) +- return ret; +- drv->vbus = NULL; +- } +- +- for (i = 0; i < drv->cfg->num_phys; i++) { +- char *label = drv->cfg->phys[i].label; +- struct samsung_usb2_phy_instance *p = &drv->instances[i]; +- +- dev_dbg(dev, "Creating phy \"%s\"\n", label); +- p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops); +- if (IS_ERR(p->phy)) { +- dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", +- label); +- return PTR_ERR(p->phy); +- } +- +- p->cfg = &drv->cfg->phys[i]; +- p->drv = drv; +- phy_set_bus_width(p->phy, 8); +- phy_set_drvdata(p->phy, p); +- } +- +- phy_provider = devm_of_phy_provider_register(dev, +- samsung_usb2_phy_xlate); +- if (IS_ERR(phy_provider)) { +- dev_err(drv->dev, "Failed to register phy provider\n"); +- return PTR_ERR(phy_provider); +- } +- +- return 0; +-} +- +-static struct platform_driver samsung_usb2_phy_driver = { +- .probe = samsung_usb2_phy_probe, +- .driver = { +- .of_match_table = samsung_usb2_phy_of_match, +- .name = "samsung-usb2-phy", +- } +-}; +- +-module_platform_driver(samsung_usb2_phy_driver); +-MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver"); +-MODULE_AUTHOR("Kamil Debski "); +-MODULE_LICENSE("GPL v2"); +-MODULE_ALIAS("platform:samsung-usb2-phy"); +diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h +deleted file mode 100644 +index 6563e7ca0ac4..000000000000 +--- a/drivers/phy/phy-samsung-usb2.h ++++ /dev/null +@@ -1,73 +0,0 @@ +-/* +- * Samsung SoC USB 1.1/2.0 PHY driver +- * +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Author: Kamil Debski +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +- +-#ifndef _PHY_EXYNOS_USB2_H +-#define _PHY_EXYNOS_USB2_H +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define KHZ 1000 +-#define MHZ (KHZ * KHZ) +- +-struct samsung_usb2_phy_driver; +-struct samsung_usb2_phy_instance; +-struct samsung_usb2_phy_config; +- +-struct samsung_usb2_phy_instance { +- const struct samsung_usb2_common_phy *cfg; +- struct phy *phy; +- struct samsung_usb2_phy_driver *drv; +- int int_cnt; +- int ext_cnt; +-}; +- +-struct samsung_usb2_phy_driver { +- const struct samsung_usb2_phy_config *cfg; +- struct clk *clk; +- struct clk *ref_clk; +- struct regulator *vbus; +- unsigned long ref_rate; +- u32 ref_reg_val; +- struct device *dev; +- void __iomem *reg_phy; +- struct regmap *reg_pmu; +- struct regmap *reg_sys; +- spinlock_t lock; +- struct samsung_usb2_phy_instance instances[0]; +-}; +- +-struct samsung_usb2_common_phy { +- int (*power_on)(struct samsung_usb2_phy_instance *); +- int (*power_off)(struct samsung_usb2_phy_instance *); +- unsigned int id; +- char *label; +-}; +- +- +-struct samsung_usb2_phy_config { +- const struct samsung_usb2_common_phy *phys; +- int (*rate_to_clk)(unsigned long, u32 *); +- unsigned int num_phys; +- bool has_mode_switch; +- bool has_refclk_sel; +-}; +- +-extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config; +-extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; +-extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; +-extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config; +-extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config; +-#endif +diff --git a/drivers/phy/phy-spear1310-miphy.c b/drivers/phy/phy-spear1310-miphy.c +deleted file mode 100644 +index ed67e98e54ca..000000000000 +--- a/drivers/phy/phy-spear1310-miphy.c ++++ /dev/null +@@ -1,261 +0,0 @@ +-/* +- * ST SPEAr1310-miphy driver +- * +- * Copyright (C) 2014 ST Microelectronics +- * Pratyush Anand +- * Mohit Kumar +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* SPEAr1310 Registers */ +-#define SPEAR1310_PCIE_SATA_CFG 0x3A4 +- #define SPEAR1310_PCIE_SATA2_SEL_PCIE (0 << 31) +- #define SPEAR1310_PCIE_SATA1_SEL_PCIE (0 << 30) +- #define SPEAR1310_PCIE_SATA0_SEL_PCIE (0 << 29) +- #define SPEAR1310_PCIE_SATA2_SEL_SATA BIT(31) +- #define SPEAR1310_PCIE_SATA1_SEL_SATA BIT(30) +- #define SPEAR1310_PCIE_SATA0_SEL_SATA BIT(29) +- #define SPEAR1310_SATA2_CFG_TX_CLK_EN BIT(27) +- #define SPEAR1310_SATA2_CFG_RX_CLK_EN BIT(26) +- #define SPEAR1310_SATA2_CFG_POWERUP_RESET BIT(25) +- #define SPEAR1310_SATA2_CFG_PM_CLK_EN BIT(24) +- #define SPEAR1310_SATA1_CFG_TX_CLK_EN BIT(23) +- #define SPEAR1310_SATA1_CFG_RX_CLK_EN BIT(22) +- #define SPEAR1310_SATA1_CFG_POWERUP_RESET BIT(21) +- #define SPEAR1310_SATA1_CFG_PM_CLK_EN BIT(20) +- #define SPEAR1310_SATA0_CFG_TX_CLK_EN BIT(19) +- #define SPEAR1310_SATA0_CFG_RX_CLK_EN BIT(18) +- #define SPEAR1310_SATA0_CFG_POWERUP_RESET BIT(17) +- #define SPEAR1310_SATA0_CFG_PM_CLK_EN BIT(16) +- #define SPEAR1310_PCIE2_CFG_DEVICE_PRESENT BIT(11) +- #define SPEAR1310_PCIE2_CFG_POWERUP_RESET BIT(10) +- #define SPEAR1310_PCIE2_CFG_CORE_CLK_EN BIT(9) +- #define SPEAR1310_PCIE2_CFG_AUX_CLK_EN BIT(8) +- #define SPEAR1310_PCIE1_CFG_DEVICE_PRESENT BIT(7) +- #define SPEAR1310_PCIE1_CFG_POWERUP_RESET BIT(6) +- #define SPEAR1310_PCIE1_CFG_CORE_CLK_EN BIT(5) +- #define SPEAR1310_PCIE1_CFG_AUX_CLK_EN BIT(4) +- #define SPEAR1310_PCIE0_CFG_DEVICE_PRESENT BIT(3) +- #define SPEAR1310_PCIE0_CFG_POWERUP_RESET BIT(2) +- #define SPEAR1310_PCIE0_CFG_CORE_CLK_EN BIT(1) +- #define SPEAR1310_PCIE0_CFG_AUX_CLK_EN BIT(0) +- +- #define SPEAR1310_PCIE_CFG_MASK(x) ((0xF << (x * 4)) | BIT((x + 29))) +- #define SPEAR1310_SATA_CFG_MASK(x) ((0xF << (x * 4 + 16)) | \ +- BIT((x + 29))) +- #define SPEAR1310_PCIE_CFG_VAL(x) \ +- (SPEAR1310_PCIE_SATA##x##_SEL_PCIE | \ +- SPEAR1310_PCIE##x##_CFG_AUX_CLK_EN | \ +- SPEAR1310_PCIE##x##_CFG_CORE_CLK_EN | \ +- SPEAR1310_PCIE##x##_CFG_POWERUP_RESET | \ +- SPEAR1310_PCIE##x##_CFG_DEVICE_PRESENT) +- #define SPEAR1310_SATA_CFG_VAL(x) \ +- (SPEAR1310_PCIE_SATA##x##_SEL_SATA | \ +- SPEAR1310_SATA##x##_CFG_PM_CLK_EN | \ +- SPEAR1310_SATA##x##_CFG_POWERUP_RESET | \ +- SPEAR1310_SATA##x##_CFG_RX_CLK_EN | \ +- SPEAR1310_SATA##x##_CFG_TX_CLK_EN) +- +-#define SPEAR1310_PCIE_MIPHY_CFG_1 0x3A8 +- #define SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT BIT(31) +- #define SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 BIT(28) +- #define SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(x) (x << 16) +- #define SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT BIT(15) +- #define SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 BIT(12) +- #define SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(x) (x << 0) +- #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_MASK (0xFFFF) +- #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK (0xFFFF << 16) +- #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA \ +- (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ +- SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 | \ +- SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(60) | \ +- SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ +- SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 | \ +- SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(60)) +- #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ +- (SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(120)) +- #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE \ +- (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ +- SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(25) | \ +- SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ +- SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(25)) +- +-#define SPEAR1310_PCIE_MIPHY_CFG_2 0x3AC +- +-enum spear1310_miphy_mode { +- SATA, +- PCIE, +-}; +- +-struct spear1310_miphy_priv { +- /* instance id of this phy */ +- u32 id; +- /* phy mode: 0 for SATA 1 for PCIe */ +- enum spear1310_miphy_mode mode; +- /* regmap for any soc specific misc registers */ +- struct regmap *misc; +- /* phy struct pointer */ +- struct phy *phy; +-}; +- +-static int spear1310_miphy_pcie_init(struct spear1310_miphy_priv *priv) +-{ +- u32 val; +- +- regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, +- SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, +- SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE); +- +- switch (priv->id) { +- case 0: +- val = SPEAR1310_PCIE_CFG_VAL(0); +- break; +- case 1: +- val = SPEAR1310_PCIE_CFG_VAL(1); +- break; +- case 2: +- val = SPEAR1310_PCIE_CFG_VAL(2); +- break; +- default: +- return -EINVAL; +- } +- +- regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, +- SPEAR1310_PCIE_CFG_MASK(priv->id), val); +- +- return 0; +-} +- +-static int spear1310_miphy_pcie_exit(struct spear1310_miphy_priv *priv) +-{ +- regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, +- SPEAR1310_PCIE_CFG_MASK(priv->id), 0); +- +- regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, +- SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, 0); +- +- return 0; +-} +- +-static int spear1310_miphy_init(struct phy *phy) +-{ +- struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); +- int ret = 0; +- +- if (priv->mode == PCIE) +- ret = spear1310_miphy_pcie_init(priv); +- +- return ret; +-} +- +-static int spear1310_miphy_exit(struct phy *phy) +-{ +- struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); +- int ret = 0; +- +- if (priv->mode == PCIE) +- ret = spear1310_miphy_pcie_exit(priv); +- +- return ret; +-} +- +-static const struct of_device_id spear1310_miphy_of_match[] = { +- { .compatible = "st,spear1310-miphy" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match); +- +-static const struct phy_ops spear1310_miphy_ops = { +- .init = spear1310_miphy_init, +- .exit = spear1310_miphy_exit, +- .owner = THIS_MODULE, +-}; +- +-static struct phy *spear1310_miphy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct spear1310_miphy_priv *priv = dev_get_drvdata(dev); +- +- if (args->args_count < 1) { +- dev_err(dev, "DT did not pass correct no of args\n"); +- return ERR_PTR(-ENODEV); +- } +- +- priv->mode = args->args[0]; +- +- if (priv->mode != SATA && priv->mode != PCIE) { +- dev_err(dev, "DT did not pass correct phy mode\n"); +- return ERR_PTR(-ENODEV); +- } +- +- return priv->phy; +-} +- +-static int spear1310_miphy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct spear1310_miphy_priv *priv; +- struct phy_provider *phy_provider; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->misc = +- syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); +- if (IS_ERR(priv->misc)) { +- dev_err(dev, "failed to find misc regmap\n"); +- return PTR_ERR(priv->misc); +- } +- +- if (of_property_read_u32(dev->of_node, "phy-id", &priv->id)) { +- dev_err(dev, "failed to find phy id\n"); +- return -EINVAL; +- } +- +- priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops); +- if (IS_ERR(priv->phy)) { +- dev_err(dev, "failed to create SATA PCIe PHY\n"); +- return PTR_ERR(priv->phy); +- } +- +- dev_set_drvdata(dev, priv); +- phy_set_drvdata(priv->phy, priv); +- +- phy_provider = +- devm_of_phy_provider_register(dev, spear1310_miphy_xlate); +- if (IS_ERR(phy_provider)) { +- dev_err(dev, "failed to register phy provider\n"); +- return PTR_ERR(phy_provider); +- } +- +- return 0; +-} +- +-static struct platform_driver spear1310_miphy_driver = { +- .probe = spear1310_miphy_probe, +- .driver = { +- .name = "spear1310-miphy", +- .of_match_table = of_match_ptr(spear1310_miphy_of_match), +- }, +-}; +- +-module_platform_driver(spear1310_miphy_driver); +- +-MODULE_DESCRIPTION("ST SPEAR1310-MIPHY driver"); +-MODULE_AUTHOR("Pratyush Anand "); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-spear1340-miphy.c b/drivers/phy/phy-spear1340-miphy.c +deleted file mode 100644 +index 97280c0cf612..000000000000 +--- a/drivers/phy/phy-spear1340-miphy.c ++++ /dev/null +@@ -1,294 +0,0 @@ +-/* +- * ST spear1340-miphy driver +- * +- * Copyright (C) 2014 ST Microelectronics +- * Pratyush Anand +- * Mohit Kumar +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* SPEAr1340 Registers */ +-/* Power Management Registers */ +-#define SPEAR1340_PCM_CFG 0x100 +- #define SPEAR1340_PCM_CFG_SATA_POWER_EN BIT(11) +-#define SPEAR1340_PCM_WKUP_CFG 0x104 +-#define SPEAR1340_SWITCH_CTR 0x108 +- +-#define SPEAR1340_PERIP1_SW_RST 0x318 +- #define SPEAR1340_PERIP1_SW_RSATA BIT(12) +-#define SPEAR1340_PERIP2_SW_RST 0x31C +-#define SPEAR1340_PERIP3_SW_RST 0x320 +- +-/* PCIE - SATA configuration registers */ +-#define SPEAR1340_PCIE_SATA_CFG 0x424 +- /* PCIE CFG MASks */ +- #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT BIT(11) +- #define SPEAR1340_PCIE_CFG_POWERUP_RESET BIT(10) +- #define SPEAR1340_PCIE_CFG_CORE_CLK_EN BIT(9) +- #define SPEAR1340_PCIE_CFG_AUX_CLK_EN BIT(8) +- #define SPEAR1340_SATA_CFG_TX_CLK_EN BIT(4) +- #define SPEAR1340_SATA_CFG_RX_CLK_EN BIT(3) +- #define SPEAR1340_SATA_CFG_POWERUP_RESET BIT(2) +- #define SPEAR1340_SATA_CFG_PM_CLK_EN BIT(1) +- #define SPEAR1340_PCIE_SATA_SEL_PCIE (0) +- #define SPEAR1340_PCIE_SATA_SEL_SATA (1) +- #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F +- #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \ +- SPEAR1340_PCIE_CFG_AUX_CLK_EN | \ +- SPEAR1340_PCIE_CFG_CORE_CLK_EN | \ +- SPEAR1340_PCIE_CFG_POWERUP_RESET | \ +- SPEAR1340_PCIE_CFG_DEVICE_PRESENT) +- #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \ +- SPEAR1340_SATA_CFG_PM_CLK_EN | \ +- SPEAR1340_SATA_CFG_POWERUP_RESET | \ +- SPEAR1340_SATA_CFG_RX_CLK_EN | \ +- SPEAR1340_SATA_CFG_TX_CLK_EN) +- +-#define SPEAR1340_PCIE_MIPHY_CFG 0x428 +- #define SPEAR1340_MIPHY_OSC_BYPASS_EXT BIT(31) +- #define SPEAR1340_MIPHY_CLK_REF_DIV2 BIT(27) +- #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27) +- #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27) +- #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0) +- #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF +- #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \ +- (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ +- SPEAR1340_MIPHY_CLK_REF_DIV2 | \ +- SPEAR1340_MIPHY_PLL_RATIO_TOP(60)) +- #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ +- (SPEAR1340_MIPHY_PLL_RATIO_TOP(120)) +- #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \ +- (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ +- SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) +- +-enum spear1340_miphy_mode { +- SATA, +- PCIE, +-}; +- +-struct spear1340_miphy_priv { +- /* phy mode: 0 for SATA 1 for PCIe */ +- enum spear1340_miphy_mode mode; +- /* regmap for any soc specific misc registers */ +- struct regmap *misc; +- /* phy struct pointer */ +- struct phy *phy; +-}; +- +-static int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv) +-{ +- regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, +- SPEAR1340_PCIE_SATA_CFG_MASK, +- SPEAR1340_SATA_CFG_VAL); +- regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, +- SPEAR1340_PCIE_MIPHY_CFG_MASK, +- SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK); +- /* Switch on sata power domain */ +- regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, +- SPEAR1340_PCM_CFG_SATA_POWER_EN, +- SPEAR1340_PCM_CFG_SATA_POWER_EN); +- /* Wait for SATA power domain on */ +- msleep(20); +- +- /* Disable PCIE SATA Controller reset */ +- regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, +- SPEAR1340_PERIP1_SW_RSATA, 0); +- /* Wait for SATA reset de-assert completion */ +- msleep(20); +- +- return 0; +-} +- +-static int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv) +-{ +- regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, +- SPEAR1340_PCIE_SATA_CFG_MASK, 0); +- regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, +- SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); +- +- /* Enable PCIE SATA Controller reset */ +- regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, +- SPEAR1340_PERIP1_SW_RSATA, +- SPEAR1340_PERIP1_SW_RSATA); +- /* Wait for SATA power domain off */ +- msleep(20); +- /* Switch off sata power domain */ +- regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, +- SPEAR1340_PCM_CFG_SATA_POWER_EN, 0); +- /* Wait for SATA reset assert completion */ +- msleep(20); +- +- return 0; +-} +- +-static int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv) +-{ +- regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, +- SPEAR1340_PCIE_MIPHY_CFG_MASK, +- SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE); +- regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, +- SPEAR1340_PCIE_SATA_CFG_MASK, +- SPEAR1340_PCIE_CFG_VAL); +- +- return 0; +-} +- +-static int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv) +-{ +- regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, +- SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); +- regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, +- SPEAR1340_PCIE_SATA_CFG_MASK, 0); +- +- return 0; +-} +- +-static int spear1340_miphy_init(struct phy *phy) +-{ +- struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); +- int ret = 0; +- +- if (priv->mode == SATA) +- ret = spear1340_miphy_sata_init(priv); +- else if (priv->mode == PCIE) +- ret = spear1340_miphy_pcie_init(priv); +- +- return ret; +-} +- +-static int spear1340_miphy_exit(struct phy *phy) +-{ +- struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); +- int ret = 0; +- +- if (priv->mode == SATA) +- ret = spear1340_miphy_sata_exit(priv); +- else if (priv->mode == PCIE) +- ret = spear1340_miphy_pcie_exit(priv); +- +- return ret; +-} +- +-static const struct of_device_id spear1340_miphy_of_match[] = { +- { .compatible = "st,spear1340-miphy" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match); +- +-static const struct phy_ops spear1340_miphy_ops = { +- .init = spear1340_miphy_init, +- .exit = spear1340_miphy_exit, +- .owner = THIS_MODULE, +-}; +- +-#ifdef CONFIG_PM_SLEEP +-static int spear1340_miphy_suspend(struct device *dev) +-{ +- struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); +- int ret = 0; +- +- if (priv->mode == SATA) +- ret = spear1340_miphy_sata_exit(priv); +- +- return ret; +-} +- +-static int spear1340_miphy_resume(struct device *dev) +-{ +- struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); +- int ret = 0; +- +- if (priv->mode == SATA) +- ret = spear1340_miphy_sata_init(priv); +- +- return ret; +-} +-#endif +- +-static SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend, +- spear1340_miphy_resume); +- +-static struct phy *spear1340_miphy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); +- +- if (args->args_count < 1) { +- dev_err(dev, "DT did not pass correct no of args\n"); +- return ERR_PTR(-ENODEV); +- } +- +- priv->mode = args->args[0]; +- +- if (priv->mode != SATA && priv->mode != PCIE) { +- dev_err(dev, "DT did not pass correct phy mode\n"); +- return ERR_PTR(-ENODEV); +- } +- +- return priv->phy; +-} +- +-static int spear1340_miphy_probe(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct spear1340_miphy_priv *priv; +- struct phy_provider *phy_provider; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->misc = +- syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); +- if (IS_ERR(priv->misc)) { +- dev_err(dev, "failed to find misc regmap\n"); +- return PTR_ERR(priv->misc); +- } +- +- priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops); +- if (IS_ERR(priv->phy)) { +- dev_err(dev, "failed to create SATA PCIe PHY\n"); +- return PTR_ERR(priv->phy); +- } +- +- dev_set_drvdata(dev, priv); +- phy_set_drvdata(priv->phy, priv); +- +- phy_provider = +- devm_of_phy_provider_register(dev, spear1340_miphy_xlate); +- if (IS_ERR(phy_provider)) { +- dev_err(dev, "failed to register phy provider\n"); +- return PTR_ERR(phy_provider); +- } +- +- return 0; +-} +- +-static struct platform_driver spear1340_miphy_driver = { +- .probe = spear1340_miphy_probe, +- .driver = { +- .name = "spear1340-miphy", +- .pm = &spear1340_miphy_pm_ops, +- .of_match_table = of_match_ptr(spear1340_miphy_of_match), +- }, +-}; +- +-module_platform_driver(spear1340_miphy_driver); +- +-MODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver"); +-MODULE_AUTHOR("Pratyush Anand "); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c +deleted file mode 100644 +index b1f44ab669fb..000000000000 +--- a/drivers/phy/phy-stih407-usb.c ++++ /dev/null +@@ -1,180 +0,0 @@ +-/* +- * Copyright (C) 2014 STMicroelectronics +- * +- * STMicroelectronics Generic PHY driver for STiH407 USB2. +- * +- * Author: Giuseppe Cavallaro +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2, as +- * published by the Free Software Foundation. +- * +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define PHYPARAM_REG 1 +-#define PHYCTRL_REG 2 +- +-/* Default PHY_SEL and REFCLKSEL configuration */ +-#define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 +-#define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f +- +-/* ports parameters overriding */ +-#define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc +-#define STIH407_USB_PICOPHY_PARAM_MASK 0xffffffff +- +-struct stih407_usb2_picophy { +- struct phy *phy; +- struct regmap *regmap; +- struct device *dev; +- struct reset_control *rstc; +- struct reset_control *rstport; +- int ctrl; +- int param; +-}; +- +-static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev) +-{ +- reset_control_deassert(phy_dev->rstc); +- +- return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl, +- STIH407_USB_PICOPHY_CTRL_PORT_MASK, +- STIH407_USB_PICOPHY_CTRL_PORT_CONF); +-} +- +-static int stih407_usb2_init_port(struct phy *phy) +-{ +- int ret; +- struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); +- +- stih407_usb2_pico_ctrl(phy_dev); +- +- ret = regmap_update_bits(phy_dev->regmap, +- phy_dev->param, +- STIH407_USB_PICOPHY_PARAM_MASK, +- STIH407_USB_PICOPHY_PARAM_DEF); +- if (ret) +- return ret; +- +- return reset_control_deassert(phy_dev->rstport); +-} +- +-static int stih407_usb2_exit_port(struct phy *phy) +-{ +- struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); +- +- /* +- * Only port reset is asserted, phy global reset is kept untouched +- * as other ports may still be active. When all ports are in reset +- * state, assumption is made that power will be cut off on the phy, in +- * case of suspend for instance. Theoretically, asserting individual +- * reset (like here) or global reset should be equivalent. +- */ +- return reset_control_assert(phy_dev->rstport); +-} +- +-static const struct phy_ops stih407_usb2_picophy_data = { +- .init = stih407_usb2_init_port, +- .exit = stih407_usb2_exit_port, +- .owner = THIS_MODULE, +-}; +- +-static int stih407_usb2_picophy_probe(struct platform_device *pdev) +-{ +- struct stih407_usb2_picophy *phy_dev; +- struct device *dev = &pdev->dev; +- struct device_node *np = dev->of_node; +- struct phy_provider *phy_provider; +- struct phy *phy; +- int ret; +- +- phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); +- if (!phy_dev) +- return -ENOMEM; +- +- phy_dev->dev = dev; +- dev_set_drvdata(dev, phy_dev); +- +- phy_dev->rstc = devm_reset_control_get_shared(dev, "global"); +- if (IS_ERR(phy_dev->rstc)) { +- dev_err(dev, "failed to ctrl picoPHY reset\n"); +- return PTR_ERR(phy_dev->rstc); +- } +- +- phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port"); +- if (IS_ERR(phy_dev->rstport)) { +- dev_err(dev, "failed to ctrl picoPHY reset\n"); +- return PTR_ERR(phy_dev->rstport); +- } +- +- /* 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"); +- 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 = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); +- if (IS_ERR(phy)) { +- dev_err(dev, "failed to create Display Port PHY\n"); +- return PTR_ERR(phy); +- } +- +- phy_dev->phy = phy; +- phy_set_drvdata(phy, phy_dev); +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) +- return PTR_ERR(phy_provider); +- +- dev_info(dev, "STiH407 USB Generic picoPHY driver probed!"); +- +- return 0; +-} +- +-static const struct of_device_id stih407_usb2_picophy_of_match[] = { +- { .compatible = "st,stih407-usb2-phy" }, +- { /*sentinel */ }, +-}; +- +-MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match); +- +-static struct platform_driver stih407_usb2_picophy_driver = { +- .probe = stih407_usb2_picophy_probe, +- .driver = { +- .name = "stih407-usb-genphy", +- .of_match_table = stih407_usb2_picophy_of_match, +- } +-}; +- +-module_platform_driver(stih407_usb2_picophy_driver); +- +-MODULE_AUTHOR("Giuseppe Cavallaro "); +-MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c +deleted file mode 100644 +index bbf06cfe5898..000000000000 +--- a/drivers/phy/phy-sun4i-usb.c ++++ /dev/null +@@ -1,891 +0,0 @@ +-/* +- * Allwinner sun4i USB phy driver +- * +- * Copyright (C) 2014-2015 Hans de Goede +- * +- * Based on code from +- * Allwinner Technology Co., Ltd. +- * +- * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver +- * Copyright (C) 2013 Samsung Electronics Co., Ltd. +- * Author: Sylwester Nawrocki +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define REG_ISCR 0x00 +-#define REG_PHYCTL_A10 0x04 +-#define REG_PHYBIST 0x08 +-#define REG_PHYTUNE 0x0c +-#define REG_PHYCTL_A33 0x10 +-#define REG_PHY_OTGCTL 0x20 +- +-#define REG_PMU_UNK1 0x10 +- +-#define PHYCTL_DATA BIT(7) +- +-#define OTGCTL_ROUTE_MUSB BIT(0) +- +-#define SUNXI_AHB_ICHR8_EN BIT(10) +-#define SUNXI_AHB_INCR4_BURST_EN BIT(9) +-#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) +-#define SUNXI_ULPI_BYPASS_EN BIT(0) +- +-/* ISCR, Interface Status and Control bits */ +-#define ISCR_ID_PULLUP_EN (1 << 17) +-#define ISCR_DPDM_PULLUP_EN (1 << 16) +-/* sunxi has the phy id/vbus pins not connected, so we use the force bits */ +-#define ISCR_FORCE_ID_MASK (3 << 14) +-#define ISCR_FORCE_ID_LOW (2 << 14) +-#define ISCR_FORCE_ID_HIGH (3 << 14) +-#define ISCR_FORCE_VBUS_MASK (3 << 12) +-#define ISCR_FORCE_VBUS_LOW (2 << 12) +-#define ISCR_FORCE_VBUS_HIGH (3 << 12) +- +-/* Common Control Bits for Both PHYs */ +-#define PHY_PLL_BW 0x03 +-#define PHY_RES45_CAL_EN 0x0c +- +-/* Private Control Bits for Each PHY */ +-#define PHY_TX_AMPLITUDE_TUNE 0x20 +-#define PHY_TX_SLEWRATE_TUNE 0x22 +-#define PHY_VBUSVALID_TH_SEL 0x25 +-#define PHY_PULLUP_RES_SEL 0x27 +-#define PHY_OTG_FUNC_EN 0x28 +-#define PHY_VBUS_DET_EN 0x29 +-#define PHY_DISCON_TH_SEL 0x2a +-#define PHY_SQUELCH_DETECT 0x3c +- +-#define MAX_PHYS 4 +- +-/* +- * Note do not raise the debounce time, we must report Vusb high within 100ms +- * otherwise we get Vbus errors +- */ +-#define DEBOUNCE_TIME msecs_to_jiffies(50) +-#define POLL_TIME msecs_to_jiffies(250) +- +-enum sun4i_usb_phy_type { +- sun4i_a10_phy, +- sun6i_a31_phy, +- sun8i_a33_phy, +- sun8i_h3_phy, +- sun8i_v3s_phy, +- sun50i_a64_phy, +-}; +- +-struct sun4i_usb_phy_cfg { +- int num_phys; +- enum sun4i_usb_phy_type type; +- u32 disc_thresh; +- u8 phyctl_offset; +- bool dedicated_clocks; +- bool enable_pmu_unk1; +- bool phy0_dual_route; +-}; +- +-struct sun4i_usb_phy_data { +- void __iomem *base; +- const struct sun4i_usb_phy_cfg *cfg; +- enum usb_dr_mode dr_mode; +- spinlock_t reg_lock; /* guard access to phyctl reg */ +- struct sun4i_usb_phy { +- struct phy *phy; +- void __iomem *pmu; +- struct regulator *vbus; +- struct reset_control *reset; +- struct clk *clk; +- bool regulator_on; +- int index; +- } phys[MAX_PHYS]; +- /* phy0 / otg related variables */ +- struct extcon_dev *extcon; +- bool phy0_init; +- struct gpio_desc *id_det_gpio; +- struct gpio_desc *vbus_det_gpio; +- struct power_supply *vbus_power_supply; +- struct notifier_block vbus_power_nb; +- bool vbus_power_nb_registered; +- bool force_session_end; +- int id_det_irq; +- int vbus_det_irq; +- int id_det; +- int vbus_det; +- struct delayed_work detect; +-}; +- +-#define to_sun4i_usb_phy_data(phy) \ +- container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index]) +- +-static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set) +-{ +- struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); +- struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); +- u32 iscr; +- +- iscr = readl(data->base + REG_ISCR); +- iscr &= ~clr; +- iscr |= set; +- writel(iscr, data->base + REG_ISCR); +-} +- +-static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val) +-{ +- if (val) +- val = ISCR_FORCE_ID_HIGH; +- else +- val = ISCR_FORCE_ID_LOW; +- +- sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val); +-} +- +-static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val) +-{ +- if (val) +- val = ISCR_FORCE_VBUS_HIGH; +- else +- val = ISCR_FORCE_VBUS_LOW; +- +- sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val); +-} +- +-static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, +- int len) +-{ +- struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); +- u32 temp, usbc_bit = BIT(phy->index * 2); +- void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; +- unsigned long flags; +- int i; +- +- spin_lock_irqsave(&phy_data->reg_lock, flags); +- +- if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { +- /* SoCs newer than A33 need us to set phyctl to 0 explicitly */ +- writel(0, phyctl); +- } +- +- for (i = 0; i < len; i++) { +- temp = readl(phyctl); +- +- /* clear the address portion */ +- temp &= ~(0xff << 8); +- +- /* set the address */ +- temp |= ((addr + i) << 8); +- writel(temp, phyctl); +- +- /* set the data bit and clear usbc bit*/ +- temp = readb(phyctl); +- if (data & 0x1) +- temp |= PHYCTL_DATA; +- else +- temp &= ~PHYCTL_DATA; +- temp &= ~usbc_bit; +- writeb(temp, phyctl); +- +- /* pulse usbc_bit */ +- temp = readb(phyctl); +- temp |= usbc_bit; +- writeb(temp, phyctl); +- +- temp = readb(phyctl); +- temp &= ~usbc_bit; +- writeb(temp, phyctl); +- +- data >>= 1; +- } +- +- spin_unlock_irqrestore(&phy_data->reg_lock, flags); +-} +- +-static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable) +-{ +- u32 bits, reg_value; +- +- if (!phy->pmu) +- return; +- +- bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | +- SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; +- +- reg_value = readl(phy->pmu); +- +- if (enable) +- reg_value |= bits; +- else +- reg_value &= ~bits; +- +- writel(reg_value, phy->pmu); +-} +- +-static int sun4i_usb_phy_init(struct phy *_phy) +-{ +- struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); +- struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); +- int ret; +- u32 val; +- +- ret = clk_prepare_enable(phy->clk); +- if (ret) +- return ret; +- +- ret = reset_control_deassert(phy->reset); +- if (ret) { +- clk_disable_unprepare(phy->clk); +- return ret; +- } +- +- if (phy->pmu && data->cfg->enable_pmu_unk1) { +- val = readl(phy->pmu + REG_PMU_UNK1); +- writel(val & ~2, phy->pmu + REG_PMU_UNK1); +- } +- +- /* Enable USB 45 Ohm resistor calibration */ +- if (phy->index == 0) +- sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); +- +- /* Adjust PHY's magnitude and rate */ +- sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); +- +- /* Disconnect threshold adjustment */ +- sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, +- data->cfg->disc_thresh, 2); +- +- sun4i_usb_phy_passby(phy, 1); +- +- if (phy->index == 0) { +- data->phy0_init = true; +- +- /* Enable pull-ups */ +- sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); +- sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); +- +- /* Force ISCR and cable state updates */ +- data->id_det = -1; +- data->vbus_det = -1; +- queue_delayed_work(system_wq, &data->detect, 0); +- } +- +- return 0; +-} +- +-static int sun4i_usb_phy_exit(struct phy *_phy) +-{ +- struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); +- struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); +- +- if (phy->index == 0) { +- /* Disable pull-ups */ +- sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0); +- sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0); +- data->phy0_init = false; +- } +- +- sun4i_usb_phy_passby(phy, 0); +- reset_control_assert(phy->reset); +- clk_disable_unprepare(phy->clk); +- +- return 0; +-} +- +-static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) +-{ +- switch (data->dr_mode) { +- case USB_DR_MODE_OTG: +- if (data->id_det_gpio) +- return gpiod_get_value_cansleep(data->id_det_gpio); +- else +- return 1; /* Fallback to peripheral mode */ +- case USB_DR_MODE_HOST: +- return 0; +- case USB_DR_MODE_PERIPHERAL: +- default: +- return 1; +- } +-} +- +-static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) +-{ +- if (data->vbus_det_gpio) +- return gpiod_get_value_cansleep(data->vbus_det_gpio); +- +- if (data->vbus_power_supply) { +- union power_supply_propval val; +- int r; +- +- r = power_supply_get_property(data->vbus_power_supply, +- POWER_SUPPLY_PROP_PRESENT, &val); +- if (r == 0) +- return val.intval; +- } +- +- /* Fallback: report vbus as high */ +- return 1; +-} +- +-static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) +-{ +- return data->vbus_det_gpio || data->vbus_power_supply; +-} +- +-static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) +-{ +- if ((data->id_det_gpio && data->id_det_irq <= 0) || +- (data->vbus_det_gpio && data->vbus_det_irq <= 0)) +- return true; +- +- /* +- * The A31 companion pmic (axp221) does not generate vbus change +- * interrupts when the board is driving vbus, so we must poll +- * when using the pmic for vbus-det _and_ we're driving vbus. +- */ +- if (data->cfg->type == sun6i_a31_phy && +- data->vbus_power_supply && data->phys[0].regulator_on) +- return true; +- +- return false; +-} +- +-static int sun4i_usb_phy_power_on(struct phy *_phy) +-{ +- struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); +- struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); +- int ret; +- +- if (!phy->vbus || phy->regulator_on) +- return 0; +- +- /* For phy0 only turn on Vbus if we don't have an ext. Vbus */ +- if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) && +- data->vbus_det) { +- dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n"); +- return 0; +- } +- +- ret = regulator_enable(phy->vbus); +- if (ret) +- return ret; +- +- phy->regulator_on = true; +- +- /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ +- if (phy->index == 0 && sun4i_usb_phy0_poll(data)) +- mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); +- +- return 0; +-} +- +-static int sun4i_usb_phy_power_off(struct phy *_phy) +-{ +- struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); +- struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); +- +- if (!phy->vbus || !phy->regulator_on) +- return 0; +- +- regulator_disable(phy->vbus); +- phy->regulator_on = false; +- +- /* +- * phy0 vbus typically slowly discharges, sometimes this causes the +- * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. +- */ +- if (phy->index == 0 && !sun4i_usb_phy0_poll(data)) +- mod_delayed_work(system_wq, &data->detect, POLL_TIME); +- +- return 0; +-} +- +-static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode) +-{ +- struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); +- struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); +- int new_mode; +- +- if (phy->index != 0) +- return -EINVAL; +- +- switch (mode) { +- case PHY_MODE_USB_HOST: +- new_mode = USB_DR_MODE_HOST; +- break; +- case PHY_MODE_USB_DEVICE: +- new_mode = USB_DR_MODE_PERIPHERAL; +- break; +- case PHY_MODE_USB_OTG: +- new_mode = USB_DR_MODE_OTG; +- break; +- default: +- return -EINVAL; +- } +- +- if (new_mode != data->dr_mode) { +- dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); +- data->dr_mode = new_mode; +- } +- +- data->id_det = -1; /* Force reprocessing of id */ +- data->force_session_end = true; +- queue_delayed_work(system_wq, &data->detect, 0); +- +- return 0; +-} +- +-void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) +-{ +- struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); +- +- sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); +-} +-EXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect); +- +-static const struct phy_ops sun4i_usb_phy_ops = { +- .init = sun4i_usb_phy_init, +- .exit = sun4i_usb_phy_exit, +- .power_on = sun4i_usb_phy_power_on, +- .power_off = sun4i_usb_phy_power_off, +- .set_mode = sun4i_usb_phy_set_mode, +- .owner = THIS_MODULE, +-}; +- +-static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, int id_det) +-{ +- u32 regval; +- +- regval = readl(data->base + REG_PHY_OTGCTL); +- if (id_det == 0) { +- /* Host mode. Route phy0 to EHCI/OHCI */ +- regval &= ~OTGCTL_ROUTE_MUSB; +- } else { +- /* Peripheral mode. Route phy0 to MUSB */ +- regval |= OTGCTL_ROUTE_MUSB; +- } +- writel(regval, data->base + REG_PHY_OTGCTL); +-} +- +-static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) +-{ +- struct sun4i_usb_phy_data *data = +- container_of(work, struct sun4i_usb_phy_data, detect.work); +- struct phy *phy0 = data->phys[0].phy; +- bool force_session_end, id_notify = false, vbus_notify = false; +- int id_det, vbus_det; +- +- if (phy0 == NULL) +- return; +- +- id_det = sun4i_usb_phy0_get_id_det(data); +- vbus_det = sun4i_usb_phy0_get_vbus_det(data); +- +- mutex_lock(&phy0->mutex); +- +- if (!data->phy0_init) { +- mutex_unlock(&phy0->mutex); +- return; +- } +- +- force_session_end = data->force_session_end; +- data->force_session_end = false; +- +- if (id_det != data->id_det) { +- /* id-change, force session end if we've no vbus detection */ +- if (data->dr_mode == USB_DR_MODE_OTG && +- !sun4i_usb_phy0_have_vbus_det(data)) +- force_session_end = true; +- +- /* When entering host mode (id = 0) force end the session now */ +- if (force_session_end && id_det == 0) { +- sun4i_usb_phy0_set_vbus_detect(phy0, 0); +- msleep(200); +- sun4i_usb_phy0_set_vbus_detect(phy0, 1); +- } +- sun4i_usb_phy0_set_id_detect(phy0, id_det); +- data->id_det = id_det; +- id_notify = true; +- } +- +- if (vbus_det != data->vbus_det) { +- sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det); +- data->vbus_det = vbus_det; +- vbus_notify = true; +- } +- +- mutex_unlock(&phy0->mutex); +- +- if (id_notify) { +- extcon_set_state_sync(data->extcon, EXTCON_USB_HOST, +- !id_det); +- /* When leaving host mode force end the session here */ +- if (force_session_end && id_det == 1) { +- mutex_lock(&phy0->mutex); +- sun4i_usb_phy0_set_vbus_detect(phy0, 0); +- msleep(1000); +- sun4i_usb_phy0_set_vbus_detect(phy0, 1); +- mutex_unlock(&phy0->mutex); +- } +- +- /* Re-route PHY0 if necessary */ +- if (data->cfg->phy0_dual_route) +- sun4i_usb_phy0_reroute(data, id_det); +- } +- +- if (vbus_notify) +- extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det); +- +- if (sun4i_usb_phy0_poll(data)) +- queue_delayed_work(system_wq, &data->detect, POLL_TIME); +-} +- +-static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id) +-{ +- struct sun4i_usb_phy_data *data = dev_id; +- +- /* vbus or id changed, let the pins settle and then scan them */ +- mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); +- +- return IRQ_HANDLED; +-} +- +-static int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb, +- unsigned long val, void *v) +-{ +- struct sun4i_usb_phy_data *data = +- container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb); +- struct power_supply *psy = v; +- +- /* Properties on the vbus_power_supply changed, scan vbus_det */ +- if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) +- mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); +- +- return NOTIFY_OK; +-} +- +-static struct phy *sun4i_usb_phy_xlate(struct device *dev, +- struct of_phandle_args *args) +-{ +- struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); +- +- if (args->args[0] >= data->cfg->num_phys) +- return ERR_PTR(-ENODEV); +- +- return data->phys[args->args[0]].phy; +-} +- +-static int sun4i_usb_phy_remove(struct platform_device *pdev) +-{ +- struct device *dev = &pdev->dev; +- struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); +- +- if (data->vbus_power_nb_registered) +- power_supply_unreg_notifier(&data->vbus_power_nb); +- if (data->id_det_irq > 0) +- devm_free_irq(dev, data->id_det_irq, data); +- if (data->vbus_det_irq > 0) +- devm_free_irq(dev, data->vbus_det_irq, data); +- +- cancel_delayed_work_sync(&data->detect); +- +- return 0; +-} +- +-static const unsigned int sun4i_usb_phy0_cable[] = { +- EXTCON_USB, +- EXTCON_USB_HOST, +- EXTCON_NONE, +-}; +- +-static int sun4i_usb_phy_probe(struct platform_device *pdev) +-{ +- struct sun4i_usb_phy_data *data; +- struct device *dev = &pdev->dev; +- struct device_node *np = dev->of_node; +- struct phy_provider *phy_provider; +- struct resource *res; +- int i, ret; +- +- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- spin_lock_init(&data->reg_lock); +- INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); +- dev_set_drvdata(dev, data); +- data->cfg = of_device_get_match_data(dev); +- if (!data->cfg) +- return -EINVAL; +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); +- data->base = devm_ioremap_resource(dev, res); +- if (IS_ERR(data->base)) +- return PTR_ERR(data->base); +- +- data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", +- GPIOD_IN); +- if (IS_ERR(data->id_det_gpio)) +- return PTR_ERR(data->id_det_gpio); +- +- data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", +- GPIOD_IN); +- if (IS_ERR(data->vbus_det_gpio)) +- return PTR_ERR(data->vbus_det_gpio); +- +- if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { +- data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, +- "usb0_vbus_power-supply"); +- if (IS_ERR(data->vbus_power_supply)) +- return PTR_ERR(data->vbus_power_supply); +- +- if (!data->vbus_power_supply) +- return -EPROBE_DEFER; +- } +- +- data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); +- +- data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable); +- if (IS_ERR(data->extcon)) +- return PTR_ERR(data->extcon); +- +- ret = devm_extcon_dev_register(dev, data->extcon); +- if (ret) { +- dev_err(dev, "failed to register extcon: %d\n", ret); +- return ret; +- } +- +- for (i = 0; i < data->cfg->num_phys; i++) { +- struct sun4i_usb_phy *phy = data->phys + i; +- char name[16]; +- +- snprintf(name, sizeof(name), "usb%d_vbus", i); +- phy->vbus = devm_regulator_get_optional(dev, name); +- if (IS_ERR(phy->vbus)) { +- if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) +- return -EPROBE_DEFER; +- phy->vbus = NULL; +- } +- +- if (data->cfg->dedicated_clocks) +- snprintf(name, sizeof(name), "usb%d_phy", i); +- else +- strlcpy(name, "usb_phy", sizeof(name)); +- +- phy->clk = devm_clk_get(dev, name); +- if (IS_ERR(phy->clk)) { +- dev_err(dev, "failed to get clock %s\n", name); +- return PTR_ERR(phy->clk); +- } +- +- snprintf(name, sizeof(name), "usb%d_reset", i); +- phy->reset = devm_reset_control_get(dev, name); +- if (IS_ERR(phy->reset)) { +- dev_err(dev, "failed to get reset %s\n", name); +- return PTR_ERR(phy->reset); +- } +- +- if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ +- snprintf(name, sizeof(name), "pmu%d", i); +- res = platform_get_resource_byname(pdev, +- IORESOURCE_MEM, name); +- phy->pmu = devm_ioremap_resource(dev, res); +- if (IS_ERR(phy->pmu)) +- return PTR_ERR(phy->pmu); +- } +- +- phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); +- if (IS_ERR(phy->phy)) { +- dev_err(dev, "failed to create PHY %d\n", i); +- return PTR_ERR(phy->phy); +- } +- +- phy->index = i; +- phy_set_drvdata(phy->phy, &data->phys[i]); +- } +- +- data->id_det_irq = gpiod_to_irq(data->id_det_gpio); +- if (data->id_det_irq > 0) { +- ret = devm_request_irq(dev, data->id_det_irq, +- sun4i_usb_phy0_id_vbus_det_irq, +- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +- "usb0-id-det", data); +- if (ret) { +- dev_err(dev, "Err requesting id-det-irq: %d\n", ret); +- return ret; +- } +- } +- +- data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); +- if (data->vbus_det_irq > 0) { +- ret = devm_request_irq(dev, data->vbus_det_irq, +- sun4i_usb_phy0_id_vbus_det_irq, +- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +- "usb0-vbus-det", data); +- if (ret) { +- dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret); +- data->vbus_det_irq = -1; +- sun4i_usb_phy_remove(pdev); /* Stop detect work */ +- return ret; +- } +- } +- +- if (data->vbus_power_supply) { +- data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; +- data->vbus_power_nb.priority = 0; +- ret = power_supply_reg_notifier(&data->vbus_power_nb); +- if (ret) { +- sun4i_usb_phy_remove(pdev); /* Stop detect work */ +- return ret; +- } +- data->vbus_power_nb_registered = true; +- } +- +- phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); +- if (IS_ERR(phy_provider)) { +- sun4i_usb_phy_remove(pdev); /* Stop detect work */ +- return PTR_ERR(phy_provider); +- } +- +- return 0; +-} +- +-static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { +- .num_phys = 3, +- .type = sun4i_a10_phy, +- .disc_thresh = 3, +- .phyctl_offset = REG_PHYCTL_A10, +- .dedicated_clocks = false, +- .enable_pmu_unk1 = false, +-}; +- +-static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { +- .num_phys = 2, +- .type = sun4i_a10_phy, +- .disc_thresh = 2, +- .phyctl_offset = REG_PHYCTL_A10, +- .dedicated_clocks = false, +- .enable_pmu_unk1 = false, +-}; +- +-static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { +- .num_phys = 3, +- .type = sun6i_a31_phy, +- .disc_thresh = 3, +- .phyctl_offset = REG_PHYCTL_A10, +- .dedicated_clocks = true, +- .enable_pmu_unk1 = false, +-}; +- +-static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { +- .num_phys = 3, +- .type = sun4i_a10_phy, +- .disc_thresh = 2, +- .phyctl_offset = REG_PHYCTL_A10, +- .dedicated_clocks = false, +- .enable_pmu_unk1 = false, +-}; +- +-static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { +- .num_phys = 2, +- .type = sun4i_a10_phy, +- .disc_thresh = 3, +- .phyctl_offset = REG_PHYCTL_A10, +- .dedicated_clocks = true, +- .enable_pmu_unk1 = false, +-}; +- +-static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { +- .num_phys = 2, +- .type = sun8i_a33_phy, +- .disc_thresh = 3, +- .phyctl_offset = REG_PHYCTL_A33, +- .dedicated_clocks = true, +- .enable_pmu_unk1 = false, +-}; +- +-static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { +- .num_phys = 4, +- .type = sun8i_h3_phy, +- .disc_thresh = 3, +- .phyctl_offset = REG_PHYCTL_A33, +- .dedicated_clocks = true, +- .enable_pmu_unk1 = true, +- .phy0_dual_route = true, +-}; +- +-static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { +- .num_phys = 1, +- .type = sun8i_v3s_phy, +- .disc_thresh = 3, +- .phyctl_offset = REG_PHYCTL_A33, +- .dedicated_clocks = true, +- .enable_pmu_unk1 = true, +-}; +- +-static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { +- .num_phys = 2, +- .type = sun50i_a64_phy, +- .disc_thresh = 3, +- .phyctl_offset = REG_PHYCTL_A33, +- .dedicated_clocks = true, +- .enable_pmu_unk1 = true, +- .phy0_dual_route = true, +-}; +- +-static const struct of_device_id sun4i_usb_phy_of_match[] = { +- { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg }, +- { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg }, +- { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg }, +- { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg }, +- { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg }, +- { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, +- { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, +- { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, +- { .compatible = "allwinner,sun50i-a64-usb-phy", +- .data = &sun50i_a64_cfg}, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); +- +-static struct platform_driver sun4i_usb_phy_driver = { +- .probe = sun4i_usb_phy_probe, +- .remove = sun4i_usb_phy_remove, +- .driver = { +- .of_match_table = sun4i_usb_phy_of_match, +- .name = "sun4i-usb-phy", +- } +-}; +-module_platform_driver(sun4i_usb_phy_driver); +- +-MODULE_DESCRIPTION("Allwinner sun4i USB phy driver"); +-MODULE_AUTHOR("Hans de Goede "); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-sun9i-usb.c b/drivers/phy/phy-sun9i-usb.c +deleted file mode 100644 +index 28fce4bce638..000000000000 +--- a/drivers/phy/phy-sun9i-usb.c ++++ /dev/null +@@ -1,202 +0,0 @@ +-/* +- * Allwinner sun9i USB phy driver +- * +- * Copyright (C) 2014-2015 Chen-Yu Tsai +- * +- * Based on phy-sun4i-usb.c from +- * Hans de Goede +- * +- * and code from +- * Allwinner Technology Co., Ltd. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define SUNXI_AHB_INCR16_BURST_EN BIT(11) +-#define SUNXI_AHB_INCR8_BURST_EN BIT(10) +-#define SUNXI_AHB_INCR4_BURST_EN BIT(9) +-#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) +-#define SUNXI_ULPI_BYPASS_EN BIT(0) +- +-/* usb1 HSIC specific bits */ +-#define SUNXI_EHCI_HS_FORCE BIT(20) +-#define SUNXI_HSIC_CONNECT_DET BIT(17) +-#define SUNXI_HSIC_CONNECT_INT BIT(16) +-#define SUNXI_HSIC BIT(1) +- +-struct sun9i_usb_phy { +- struct phy *phy; +- void __iomem *pmu; +- struct reset_control *reset; +- struct clk *clk; +- struct clk *hsic_clk; +- enum usb_phy_interface type; +-}; +- +-static void sun9i_usb_phy_passby(struct sun9i_usb_phy *phy, int enable) +-{ +- u32 bits, reg_value; +- +- bits = SUNXI_AHB_INCR16_BURST_EN | SUNXI_AHB_INCR8_BURST_EN | +- SUNXI_AHB_INCR4_BURST_EN | SUNXI_AHB_INCRX_ALIGN_EN | +- SUNXI_ULPI_BYPASS_EN; +- +- if (phy->type == USBPHY_INTERFACE_MODE_HSIC) +- bits |= SUNXI_HSIC | SUNXI_EHCI_HS_FORCE | +- SUNXI_HSIC_CONNECT_DET | SUNXI_HSIC_CONNECT_INT; +- +- reg_value = readl(phy->pmu); +- +- if (enable) +- reg_value |= bits; +- else +- reg_value &= ~bits; +- +- writel(reg_value, phy->pmu); +-} +- +-static int sun9i_usb_phy_init(struct phy *_phy) +-{ +- struct sun9i_usb_phy *phy = phy_get_drvdata(_phy); +- int ret; +- +- ret = clk_prepare_enable(phy->clk); +- if (ret) +- goto err_clk; +- +- ret = clk_prepare_enable(phy->hsic_clk); +- if (ret) +- goto err_hsic_clk; +- +- ret = reset_control_deassert(phy->reset); +- if (ret) +- goto err_reset; +- +- sun9i_usb_phy_passby(phy, 1); +- return 0; +- +-err_reset: +- clk_disable_unprepare(phy->hsic_clk); +- +-err_hsic_clk: +- clk_disable_unprepare(phy->clk); +- +-err_clk: +- return ret; +-} +- +-static int sun9i_usb_phy_exit(struct phy *_phy) +-{ +- struct sun9i_usb_phy *phy = phy_get_drvdata(_phy); +- +- sun9i_usb_phy_passby(phy, 0); +- reset_control_assert(phy->reset); +- clk_disable_unprepare(phy->hsic_clk); +- clk_disable_unprepare(phy->clk); +- +- return 0; +-} +- +-static const struct phy_ops sun9i_usb_phy_ops = { +- .init = sun9i_usb_phy_init, +- .exit = sun9i_usb_phy_exit, +- .owner = THIS_MODULE, +-}; +- +-static int sun9i_usb_phy_probe(struct platform_device *pdev) +-{ +- struct sun9i_usb_phy *phy; +- struct device *dev = &pdev->dev; +- struct device_node *np = dev->of_node; +- struct phy_provider *phy_provider; +- struct resource *res; +- +- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) +- return -ENOMEM; +- +- phy->type = of_usb_get_phy_mode(np); +- if (phy->type == USBPHY_INTERFACE_MODE_HSIC) { +- phy->clk = devm_clk_get(dev, "hsic_480M"); +- if (IS_ERR(phy->clk)) { +- dev_err(dev, "failed to get hsic_480M clock\n"); +- return PTR_ERR(phy->clk); +- } +- +- phy->hsic_clk = devm_clk_get(dev, "hsic_12M"); +- if (IS_ERR(phy->hsic_clk)) { +- dev_err(dev, "failed to get hsic_12M clock\n"); +- return PTR_ERR(phy->hsic_clk); +- } +- +- phy->reset = devm_reset_control_get(dev, "hsic"); +- if (IS_ERR(phy->reset)) { +- dev_err(dev, "failed to get reset control\n"); +- return PTR_ERR(phy->reset); +- } +- } else { +- phy->clk = devm_clk_get(dev, "phy"); +- if (IS_ERR(phy->clk)) { +- dev_err(dev, "failed to get phy clock\n"); +- return PTR_ERR(phy->clk); +- } +- +- phy->reset = devm_reset_control_get(dev, "phy"); +- if (IS_ERR(phy->reset)) { +- dev_err(dev, "failed to get reset control\n"); +- return PTR_ERR(phy->reset); +- } +- } +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- phy->pmu = devm_ioremap_resource(dev, res); +- if (IS_ERR(phy->pmu)) +- return PTR_ERR(phy->pmu); +- +- phy->phy = devm_phy_create(dev, NULL, &sun9i_usb_phy_ops); +- if (IS_ERR(phy->phy)) { +- dev_err(dev, "failed to create PHY\n"); +- return PTR_ERR(phy->phy); +- } +- +- phy_set_drvdata(phy->phy, phy); +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static const struct of_device_id sun9i_usb_phy_of_match[] = { +- { .compatible = "allwinner,sun9i-a80-usb-phy" }, +- { }, +-}; +-MODULE_DEVICE_TABLE(of, sun9i_usb_phy_of_match); +- +-static struct platform_driver sun9i_usb_phy_driver = { +- .probe = sun9i_usb_phy_probe, +- .driver = { +- .of_match_table = sun9i_usb_phy_of_match, +- .name = "sun9i-usb-phy", +- } +-}; +-module_platform_driver(sun9i_usb_phy_driver); +- +-MODULE_DESCRIPTION("Allwinner sun9i USB phy driver"); +-MODULE_AUTHOR("Chen-Yu Tsai "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c +deleted file mode 100644 +index 9c84d32c6f60..000000000000 +--- a/drivers/phy/phy-ti-pipe3.c ++++ /dev/null +@@ -1,697 +0,0 @@ +-/* +- * phy-ti-pipe3 - PIPE3 PHY driver. +- * +- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * Author: Kishon Vijay Abraham I +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#define PLL_STATUS 0x00000004 +-#define PLL_GO 0x00000008 +-#define PLL_CONFIGURATION1 0x0000000C +-#define PLL_CONFIGURATION2 0x00000010 +-#define PLL_CONFIGURATION3 0x00000014 +-#define PLL_CONFIGURATION4 0x00000020 +- +-#define PLL_REGM_MASK 0x001FFE00 +-#define PLL_REGM_SHIFT 0x9 +-#define PLL_REGM_F_MASK 0x0003FFFF +-#define PLL_REGM_F_SHIFT 0x0 +-#define PLL_REGN_MASK 0x000001FE +-#define PLL_REGN_SHIFT 0x1 +-#define PLL_SELFREQDCO_MASK 0x0000000E +-#define PLL_SELFREQDCO_SHIFT 0x1 +-#define PLL_SD_MASK 0x0003FC00 +-#define PLL_SD_SHIFT 10 +-#define SET_PLL_GO 0x1 +-#define PLL_LDOPWDN BIT(15) +-#define PLL_TICOPWDN BIT(16) +-#define PLL_LOCK 0x2 +-#define PLL_IDLE 0x1 +- +-#define SATA_PLL_SOFT_RESET BIT(18) +- +-#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 +-#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14 +- +-#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 +-#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22 +- +-#define PIPE3_PHY_TX_RX_POWERON 0x3 +-#define PIPE3_PHY_TX_RX_POWEROFF 0x0 +- +-#define PCIE_PCS_MASK 0xFF0000 +-#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10 +- +-/* +- * This is an Empirical value that works, need to confirm the actual +- * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status +- * to be correctly reflected in the PIPE3PHY_PLL_STATUS register. +- */ +-#define PLL_IDLE_TIME 100 /* in milliseconds */ +-#define PLL_LOCK_TIME 100 /* in milliseconds */ +- +-struct pipe3_dpll_params { +- u16 m; +- u8 n; +- u8 freq:3; +- u8 sd; +- u32 mf; +-}; +- +-struct pipe3_dpll_map { +- unsigned long rate; +- struct pipe3_dpll_params params; +-}; +- +-struct ti_pipe3 { +- void __iomem *pll_ctrl_base; +- struct device *dev; +- struct device *control_dev; +- struct clk *wkupclk; +- struct clk *sys_clk; +- struct clk *refclk; +- struct clk *div_clk; +- struct pipe3_dpll_map *dpll_map; +- struct regmap *phy_power_syscon; /* ctrl. reg. acces */ +- struct regmap *pcs_syscon; /* ctrl. reg. acces */ +- struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */ +- unsigned int dpll_reset_reg; /* reg. index within syscon */ +- unsigned int power_reg; /* power reg. index within syscon */ +- unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */ +- bool sata_refclk_enabled; +-}; +- +-static struct pipe3_dpll_map dpll_map_usb[] = { +- {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ +- {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ +- {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ +- {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ +- {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ +- {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ +- { }, /* Terminator */ +-}; +- +-static struct pipe3_dpll_map dpll_map_sata[] = { +- {12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */ +- {16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */ +- {19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */ +- {20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */ +- {26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */ +- {38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */ +- { }, /* Terminator */ +-}; +- +-static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset) +-{ +- return __raw_readl(addr + offset); +-} +- +-static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset, +- u32 data) +-{ +- __raw_writel(data, addr + offset); +-} +- +-static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy) +-{ +- unsigned long rate; +- struct pipe3_dpll_map *dpll_map = phy->dpll_map; +- +- rate = clk_get_rate(phy->sys_clk); +- +- for (; dpll_map->rate; dpll_map++) { +- if (rate == dpll_map->rate) +- return &dpll_map->params; +- } +- +- dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate); +- +- return NULL; +-} +- +-static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy); +-static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy); +- +-static int ti_pipe3_power_off(struct phy *x) +-{ +- u32 val; +- int ret; +- struct ti_pipe3 *phy = phy_get_drvdata(x); +- +- if (!phy->phy_power_syscon) { +- omap_control_phy_power(phy->control_dev, 0); +- return 0; +- } +- +- val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; +- +- ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, +- PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val); +- return ret; +-} +- +-static int ti_pipe3_power_on(struct phy *x) +-{ +- u32 val; +- u32 mask; +- int ret; +- unsigned long rate; +- struct ti_pipe3 *phy = phy_get_drvdata(x); +- +- if (!phy->phy_power_syscon) { +- omap_control_phy_power(phy->control_dev, 1); +- return 0; +- } +- +- rate = clk_get_rate(phy->sys_clk); +- if (!rate) { +- dev_err(phy->dev, "Invalid clock rate\n"); +- return -EINVAL; +- } +- rate = rate / 1000000; +- mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | +- OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; +- val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; +- val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; +- +- ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, +- mask, val); +- return ret; +-} +- +-static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy) +-{ +- u32 val; +- unsigned long timeout; +- +- timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME); +- do { +- cpu_relax(); +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); +- if (val & PLL_LOCK) +- return 0; +- } while (!time_after(jiffies, timeout)); +- +- dev_err(phy->dev, "DPLL failed to lock\n"); +- return -EBUSY; +-} +- +-static int ti_pipe3_dpll_program(struct ti_pipe3 *phy) +-{ +- u32 val; +- struct pipe3_dpll_params *dpll_params; +- +- dpll_params = ti_pipe3_get_dpll_params(phy); +- if (!dpll_params) +- return -EINVAL; +- +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); +- val &= ~PLL_REGN_MASK; +- val |= dpll_params->n << PLL_REGN_SHIFT; +- ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); +- +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); +- val &= ~PLL_SELFREQDCO_MASK; +- val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; +- ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); +- +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); +- val &= ~PLL_REGM_MASK; +- val |= dpll_params->m << PLL_REGM_SHIFT; +- ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); +- +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); +- val &= ~PLL_REGM_F_MASK; +- val |= dpll_params->mf << PLL_REGM_F_SHIFT; +- ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); +- +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); +- val &= ~PLL_SD_MASK; +- val |= dpll_params->sd << PLL_SD_SHIFT; +- ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); +- +- ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); +- +- return ti_pipe3_dpll_wait_lock(phy); +-} +- +-static int ti_pipe3_init(struct phy *x) +-{ +- struct ti_pipe3 *phy = phy_get_drvdata(x); +- u32 val; +- int ret = 0; +- +- ti_pipe3_enable_clocks(phy); +- /* +- * Set pcie_pcs register to 0x96 for proper functioning of phy +- * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table +- * 18-1804. +- */ +- if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { +- if (!phy->pcs_syscon) { +- omap_control_pcie_pcs(phy->control_dev, 0x96); +- return 0; +- } +- +- val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT; +- ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg, +- PCIE_PCS_MASK, val); +- return ret; +- } +- +- /* Bring it out of IDLE if it is IDLE */ +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); +- if (val & PLL_IDLE) { +- val &= ~PLL_IDLE; +- ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); +- ret = ti_pipe3_dpll_wait_lock(phy); +- } +- +- /* SATA has issues if re-programmed when locked */ +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); +- if ((val & PLL_LOCK) && of_device_is_compatible(phy->dev->of_node, +- "ti,phy-pipe3-sata")) +- return ret; +- +- /* Program the DPLL */ +- ret = ti_pipe3_dpll_program(phy); +- if (ret) { +- ti_pipe3_disable_clocks(phy); +- return -EINVAL; +- } +- +- return ret; +-} +- +-static int ti_pipe3_exit(struct phy *x) +-{ +- struct ti_pipe3 *phy = phy_get_drvdata(x); +- u32 val; +- unsigned long timeout; +- +- /* If dpll_reset_syscon is not present we wont power down SATA DPLL +- * due to Errata i783 +- */ +- if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") && +- !phy->dpll_reset_syscon) +- return 0; +- +- /* PCIe doesn't have internal DPLL */ +- if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { +- /* Put DPLL in IDLE mode */ +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); +- val |= PLL_IDLE; +- ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); +- +- /* wait for LDO and Oscillator to power down */ +- timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME); +- do { +- cpu_relax(); +- val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); +- if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) +- break; +- } while (!time_after(jiffies, timeout)); +- +- if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { +- dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n", +- val); +- return -EBUSY; +- } +- } +- +- /* i783: SATA needs control bit toggle after PLL unlock */ +- if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) { +- regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, +- SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET); +- regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, +- SATA_PLL_SOFT_RESET, 0); +- } +- +- ti_pipe3_disable_clocks(phy); +- +- return 0; +-} +-static const struct phy_ops ops = { +- .init = ti_pipe3_init, +- .exit = ti_pipe3_exit, +- .power_on = ti_pipe3_power_on, +- .power_off = ti_pipe3_power_off, +- .owner = THIS_MODULE, +-}; +- +-static const struct of_device_id ti_pipe3_id_table[]; +- +-static int ti_pipe3_get_clk(struct ti_pipe3 *phy) +-{ +- struct clk *clk; +- struct device *dev = phy->dev; +- struct device_node *node = dev->of_node; +- +- phy->refclk = devm_clk_get(dev, "refclk"); +- if (IS_ERR(phy->refclk)) { +- dev_err(dev, "unable to get refclk\n"); +- /* older DTBs have missing refclk in SATA PHY +- * so don't bail out in case of SATA PHY. +- */ +- if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) +- return PTR_ERR(phy->refclk); +- } +- +- if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { +- phy->wkupclk = devm_clk_get(dev, "wkupclk"); +- if (IS_ERR(phy->wkupclk)) { +- dev_err(dev, "unable to get wkupclk\n"); +- return PTR_ERR(phy->wkupclk); +- } +- } else { +- phy->wkupclk = ERR_PTR(-ENODEV); +- } +- +- if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") || +- phy->phy_power_syscon) { +- phy->sys_clk = devm_clk_get(dev, "sysclk"); +- if (IS_ERR(phy->sys_clk)) { +- dev_err(dev, "unable to get sysclk\n"); +- return -EINVAL; +- } +- } +- +- if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { +- clk = devm_clk_get(dev, "dpll_ref"); +- if (IS_ERR(clk)) { +- dev_err(dev, "unable to get dpll ref clk\n"); +- return PTR_ERR(clk); +- } +- clk_set_rate(clk, 1500000000); +- +- clk = devm_clk_get(dev, "dpll_ref_m2"); +- if (IS_ERR(clk)) { +- dev_err(dev, "unable to get dpll ref m2 clk\n"); +- return PTR_ERR(clk); +- } +- clk_set_rate(clk, 100000000); +- +- clk = devm_clk_get(dev, "phy-div"); +- if (IS_ERR(clk)) { +- dev_err(dev, "unable to get phy-div clk\n"); +- return PTR_ERR(clk); +- } +- clk_set_rate(clk, 100000000); +- +- phy->div_clk = devm_clk_get(dev, "div-clk"); +- if (IS_ERR(phy->div_clk)) { +- dev_err(dev, "unable to get div-clk\n"); +- return PTR_ERR(phy->div_clk); +- } +- } else { +- phy->div_clk = ERR_PTR(-ENODEV); +- } +- +- return 0; +-} +- +-static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) +-{ +- struct device *dev = phy->dev; +- struct device_node *node = dev->of_node; +- struct device_node *control_node; +- struct platform_device *control_pdev; +- +- phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node, +- "syscon-phy-power"); +- if (IS_ERR(phy->phy_power_syscon)) { +- dev_dbg(dev, +- "can't get syscon-phy-power, using control device\n"); +- phy->phy_power_syscon = NULL; +- } else { +- if (of_property_read_u32_index(node, +- "syscon-phy-power", 1, +- &phy->power_reg)) { +- dev_err(dev, "couldn't get power reg. offset\n"); +- return -EINVAL; +- } +- } +- +- if (!phy->phy_power_syscon) { +- control_node = of_parse_phandle(node, "ctrl-module", 0); +- if (!control_node) { +- dev_err(dev, "Failed to get control device phandle\n"); +- return -EINVAL; +- } +- +- control_pdev = of_find_device_by_node(control_node); +- if (!control_pdev) { +- dev_err(dev, "Failed to get control device\n"); +- return -EINVAL; +- } +- +- phy->control_dev = &control_pdev->dev; +- } +- +- if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { +- phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node, +- "syscon-pcs"); +- if (IS_ERR(phy->pcs_syscon)) { +- dev_dbg(dev, +- "can't get syscon-pcs, using omap control\n"); +- phy->pcs_syscon = NULL; +- } else { +- if (of_property_read_u32_index(node, +- "syscon-pcs", 1, +- &phy->pcie_pcs_reg)) { +- dev_err(dev, +- "couldn't get pcie pcs reg. offset\n"); +- return -EINVAL; +- } +- } +- } +- +- if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { +- phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node, +- "syscon-pllreset"); +- if (IS_ERR(phy->dpll_reset_syscon)) { +- dev_info(dev, +- "can't get syscon-pllreset, sata dpll won't idle\n"); +- phy->dpll_reset_syscon = NULL; +- } else { +- if (of_property_read_u32_index(node, +- "syscon-pllreset", 1, +- &phy->dpll_reset_reg)) { +- dev_err(dev, +- "couldn't get pllreset reg. offset\n"); +- return -EINVAL; +- } +- } +- } +- +- return 0; +-} +- +-static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) +-{ +- struct resource *res; +- const struct of_device_id *match; +- struct device *dev = phy->dev; +- struct device_node *node = dev->of_node; +- struct platform_device *pdev = to_platform_device(dev); +- +- if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) +- return 0; +- +- match = of_match_device(ti_pipe3_id_table, dev); +- if (!match) +- return -EINVAL; +- +- phy->dpll_map = (struct pipe3_dpll_map *)match->data; +- if (!phy->dpll_map) { +- dev_err(dev, "no DPLL data\n"); +- return -EINVAL; +- } +- +- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, +- "pll_ctrl"); +- phy->pll_ctrl_base = devm_ioremap_resource(dev, res); +- return PTR_ERR_OR_ZERO(phy->pll_ctrl_base); +-} +- +-static int ti_pipe3_probe(struct platform_device *pdev) +-{ +- struct ti_pipe3 *phy; +- struct phy *generic_phy; +- struct phy_provider *phy_provider; +- struct device_node *node = pdev->dev.of_node; +- struct device *dev = &pdev->dev; +- int ret; +- +- phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); +- if (!phy) +- return -ENOMEM; +- +- phy->dev = dev; +- +- ret = ti_pipe3_get_pll_base(phy); +- if (ret) +- return ret; +- +- ret = ti_pipe3_get_sysctrl(phy); +- if (ret) +- return ret; +- +- ret = ti_pipe3_get_clk(phy); +- if (ret) +- return ret; +- +- platform_set_drvdata(pdev, phy); +- pm_runtime_enable(dev); +- +- /* +- * Prevent auto-disable of refclk for SATA PHY due to Errata i783 +- */ +- if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { +- if (!IS_ERR(phy->refclk)) { +- clk_prepare_enable(phy->refclk); +- phy->sata_refclk_enabled = true; +- } +- } +- +- generic_phy = devm_phy_create(dev, NULL, &ops); +- if (IS_ERR(generic_phy)) +- return PTR_ERR(generic_phy); +- +- phy_set_drvdata(generic_phy, phy); +- +- ti_pipe3_power_off(generic_phy); +- +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); +- return PTR_ERR_OR_ZERO(phy_provider); +-} +- +-static int ti_pipe3_remove(struct platform_device *pdev) +-{ +- pm_runtime_disable(&pdev->dev); +- +- return 0; +-} +- +-static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy) +-{ +- int ret = 0; +- +- if (!IS_ERR(phy->refclk)) { +- ret = clk_prepare_enable(phy->refclk); +- if (ret) { +- dev_err(phy->dev, "Failed to enable refclk %d\n", ret); +- return ret; +- } +- } +- +- if (!IS_ERR(phy->wkupclk)) { +- ret = clk_prepare_enable(phy->wkupclk); +- if (ret) { +- dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); +- goto disable_refclk; +- } +- } +- +- if (!IS_ERR(phy->div_clk)) { +- ret = clk_prepare_enable(phy->div_clk); +- if (ret) { +- dev_err(phy->dev, "Failed to enable div_clk %d\n", ret); +- goto disable_wkupclk; +- } +- } +- +- return 0; +- +-disable_wkupclk: +- if (!IS_ERR(phy->wkupclk)) +- clk_disable_unprepare(phy->wkupclk); +- +-disable_refclk: +- if (!IS_ERR(phy->refclk)) +- clk_disable_unprepare(phy->refclk); +- +- return ret; +-} +- +-static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) +-{ +- if (!IS_ERR(phy->wkupclk)) +- clk_disable_unprepare(phy->wkupclk); +- if (!IS_ERR(phy->refclk)) { +- clk_disable_unprepare(phy->refclk); +- /* +- * SATA refclk needs an additional disable as we left it +- * on in probe to avoid Errata i783 +- */ +- if (phy->sata_refclk_enabled) { +- clk_disable_unprepare(phy->refclk); +- phy->sata_refclk_enabled = false; +- } +- } +- +- if (!IS_ERR(phy->div_clk)) +- clk_disable_unprepare(phy->div_clk); +-} +- +-static const struct of_device_id ti_pipe3_id_table[] = { +- { +- .compatible = "ti,phy-usb3", +- .data = dpll_map_usb, +- }, +- { +- .compatible = "ti,omap-usb3", +- .data = dpll_map_usb, +- }, +- { +- .compatible = "ti,phy-pipe3-sata", +- .data = dpll_map_sata, +- }, +- { +- .compatible = "ti,phy-pipe3-pcie", +- }, +- {} +-}; +-MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); +- +-static struct platform_driver ti_pipe3_driver = { +- .probe = ti_pipe3_probe, +- .remove = ti_pipe3_remove, +- .driver = { +- .name = "ti-pipe3", +- .of_match_table = ti_pipe3_id_table, +- }, +-}; +- +-module_platform_driver(ti_pipe3_driver); +- +-MODULE_ALIAS("platform:ti_pipe3"); +-MODULE_AUTHOR("Texas Instruments Inc."); +-MODULE_DESCRIPTION("TI PIPE3 phy driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/phy-tusb1210.c b/drivers/phy/phy-tusb1210.c +deleted file mode 100644 +index bb3fb031c478..000000000000 +--- a/drivers/phy/phy-tusb1210.c ++++ /dev/null +@@ -1,146 +0,0 @@ +-/** +- * tusb1210.c - TUSB1210 USB ULPI PHY driver +- * +- * Copyright (C) 2015 Intel Corporation +- * +- * Author: Heikki Krogerus +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +-#include +-#include +-#include +-#include +- +-#define TUSB1210_VENDOR_SPECIFIC2 0x80 +-#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 +-#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 +-#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 +- +-struct tusb1210 { +- struct ulpi *ulpi; +- struct phy *phy; +- struct gpio_desc *gpio_reset; +- struct gpio_desc *gpio_cs; +- u8 vendor_specific2; +-}; +- +-static int tusb1210_power_on(struct phy *phy) +-{ +- struct tusb1210 *tusb = phy_get_drvdata(phy); +- +- gpiod_set_value_cansleep(tusb->gpio_reset, 1); +- gpiod_set_value_cansleep(tusb->gpio_cs, 1); +- +- /* Restore the optional eye diagram optimization value */ +- if (tusb->vendor_specific2) +- ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, +- tusb->vendor_specific2); +- +- return 0; +-} +- +-static int tusb1210_power_off(struct phy *phy) +-{ +- struct tusb1210 *tusb = phy_get_drvdata(phy); +- +- gpiod_set_value_cansleep(tusb->gpio_reset, 0); +- gpiod_set_value_cansleep(tusb->gpio_cs, 0); +- +- return 0; +-} +- +-static const struct phy_ops phy_ops = { +- .power_on = tusb1210_power_on, +- .power_off = tusb1210_power_off, +- .owner = THIS_MODULE, +-}; +- +-static int tusb1210_probe(struct ulpi *ulpi) +-{ +- struct tusb1210 *tusb; +- u8 val, reg; +- +- tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); +- if (!tusb) +- return -ENOMEM; +- +- tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", +- GPIOD_OUT_LOW); +- if (IS_ERR(tusb->gpio_reset)) +- return PTR_ERR(tusb->gpio_reset); +- +- gpiod_set_value_cansleep(tusb->gpio_reset, 1); +- +- tusb->gpio_cs = devm_gpiod_get_optional(&ulpi->dev, "cs", +- GPIOD_OUT_LOW); +- if (IS_ERR(tusb->gpio_cs)) +- return PTR_ERR(tusb->gpio_cs); +- +- gpiod_set_value_cansleep(tusb->gpio_cs, 1); +- +- /* +- * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye +- * diagram optimization and DP/DM swap. +- */ +- +- /* High speed output drive strength configuration */ +- device_property_read_u8(&ulpi->dev, "ihstx", &val); +- reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; +- +- /* High speed output impedance configuration */ +- device_property_read_u8(&ulpi->dev, "zhsdrv", &val); +- reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; +- +- /* DP/DM swap control */ +- device_property_read_u8(&ulpi->dev, "datapolarity", &val); +- reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; +- +- if (reg) { +- ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); +- tusb->vendor_specific2 = reg; +- } +- +- tusb->phy = ulpi_phy_create(ulpi, &phy_ops); +- if (IS_ERR(tusb->phy)) +- return PTR_ERR(tusb->phy); +- +- tusb->ulpi = ulpi; +- +- phy_set_drvdata(tusb->phy, tusb); +- ulpi_set_drvdata(ulpi, tusb); +- return 0; +-} +- +-static void tusb1210_remove(struct ulpi *ulpi) +-{ +- struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); +- +- ulpi_phy_destroy(ulpi, tusb->phy); +-} +- +-#define TI_VENDOR_ID 0x0451 +- +-static const struct ulpi_device_id tusb1210_ulpi_id[] = { +- { TI_VENDOR_ID, 0x1507, }, +- { }, +-}; +-MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); +- +-static struct ulpi_driver tusb1210_driver = { +- .id_table = tusb1210_ulpi_id, +- .probe = tusb1210_probe, +- .remove = tusb1210_remove, +- .driver = { +- .name = "tusb1210", +- .owner = THIS_MODULE, +- }, +-}; +- +-module_ulpi_driver(tusb1210_driver); +- +-MODULE_AUTHOR("Intel Corporation"); +-MODULE_LICENSE("GPL v2"); +-MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); +diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c +deleted file mode 100644 +index 2990b3965460..000000000000 +--- a/drivers/phy/phy-twl4030-usb.c ++++ /dev/null +@@ -1,839 +0,0 @@ +-/* +- * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller +- * +- * Copyright (C) 2004-2007 Texas Instruments +- * Copyright (C) 2008 Nokia Corporation +- * Contact: Felipe Balbi +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * Current status: +- * - HS USB ULPI mode works. +- * - 3-pin mode support may be added in future. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* Register defines */ +- +-#define MCPC_CTRL 0x30 +-#define MCPC_CTRL_RTSOL (1 << 7) +-#define MCPC_CTRL_EXTSWR (1 << 6) +-#define MCPC_CTRL_EXTSWC (1 << 5) +-#define MCPC_CTRL_VOICESW (1 << 4) +-#define MCPC_CTRL_OUT64K (1 << 3) +-#define MCPC_CTRL_RTSCTSSW (1 << 2) +-#define MCPC_CTRL_HS_UART (1 << 0) +- +-#define MCPC_IO_CTRL 0x33 +-#define MCPC_IO_CTRL_MICBIASEN (1 << 5) +-#define MCPC_IO_CTRL_CTS_NPU (1 << 4) +-#define MCPC_IO_CTRL_RXD_PU (1 << 3) +-#define MCPC_IO_CTRL_TXDTYP (1 << 2) +-#define MCPC_IO_CTRL_CTSTYP (1 << 1) +-#define MCPC_IO_CTRL_RTSTYP (1 << 0) +- +-#define MCPC_CTRL2 0x36 +-#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) +- +-#define OTHER_FUNC_CTRL 0x80 +-#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) +-#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) +- +-#define OTHER_IFC_CTRL 0x83 +-#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) +-#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) +-#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) +-#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) +-#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) +-#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) +- +-#define OTHER_INT_EN_RISE 0x86 +-#define OTHER_INT_EN_FALL 0x89 +-#define OTHER_INT_STS 0x8C +-#define OTHER_INT_LATCH 0x8D +-#define OTHER_INT_VB_SESS_VLD (1 << 7) +-#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ +-#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ +-#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ +-#define OTHER_INT_MANU (1 << 1) +-#define OTHER_INT_ABNORMAL_STRESS (1 << 0) +- +-#define ID_STATUS 0x96 +-#define ID_RES_FLOAT (1 << 4) +-#define ID_RES_440K (1 << 3) +-#define ID_RES_200K (1 << 2) +-#define ID_RES_102K (1 << 1) +-#define ID_RES_GND (1 << 0) +- +-#define POWER_CTRL 0xAC +-#define POWER_CTRL_OTG_ENAB (1 << 5) +- +-#define OTHER_IFC_CTRL2 0xAF +-#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) +-#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) +-#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) +-#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ +-#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) +-#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) +- +-#define REG_CTRL_EN 0xB2 +-#define REG_CTRL_ERROR 0xB5 +-#define ULPI_I2C_CONFLICT_INTEN (1 << 0) +- +-#define OTHER_FUNC_CTRL2 0xB8 +-#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) +- +-/* following registers do not have separate _clr and _set registers */ +-#define VBUS_DEBOUNCE 0xC0 +-#define ID_DEBOUNCE 0xC1 +-#define VBAT_TIMER 0xD3 +-#define PHY_PWR_CTRL 0xFD +-#define PHY_PWR_PHYPWD (1 << 0) +-#define PHY_CLK_CTRL 0xFE +-#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) +-#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) +-#define REQ_PHY_DPLL_CLK (1 << 0) +-#define PHY_CLK_CTRL_STS 0xFF +-#define PHY_DPLL_CLK (1 << 0) +- +-/* In module TWL_MODULE_PM_MASTER */ +-#define STS_HW_CONDITIONS 0x0F +- +-/* In module TWL_MODULE_PM_RECEIVER */ +-#define VUSB_DEDICATED1 0x7D +-#define VUSB_DEDICATED2 0x7E +-#define VUSB1V5_DEV_GRP 0x71 +-#define VUSB1V5_TYPE 0x72 +-#define VUSB1V5_REMAP 0x73 +-#define VUSB1V8_DEV_GRP 0x74 +-#define VUSB1V8_TYPE 0x75 +-#define VUSB1V8_REMAP 0x76 +-#define VUSB3V1_DEV_GRP 0x77 +-#define VUSB3V1_TYPE 0x78 +-#define VUSB3V1_REMAP 0x79 +- +-/* In module TWL4030_MODULE_INTBR */ +-#define PMBR1 0x0D +-#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) +- +-/* +- * If VBUS is valid or ID is ground, then we know a +- * cable is present and we need to be runtime-enabled +- */ +-static inline bool cable_present(enum musb_vbus_id_status stat) +-{ +- return stat == MUSB_VBUS_VALID || +- stat == MUSB_ID_GROUND; +-} +- +-struct twl4030_usb { +- struct usb_phy phy; +- struct device *dev; +- +- /* TWL4030 internal USB regulator supplies */ +- struct regulator *usb1v5; +- struct regulator *usb1v8; +- struct regulator *usb3v1; +- +- /* for vbus reporting with irqs disabled */ +- struct mutex lock; +- +- /* pin configuration */ +- enum twl4030_usb_mode usb_mode; +- +- int irq; +- enum musb_vbus_id_status linkstat; +- bool vbus_supplied; +- bool musb_mailbox_pending; +- +- struct delayed_work id_workaround_work; +-}; +- +-/* internal define on top of container_of */ +-#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) +- +-/*-------------------------------------------------------------------------*/ +- +-static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, +- u8 module, u8 data, u8 address) +-{ +- u8 check; +- +- if ((twl_i2c_write_u8(module, data, address) >= 0) && +- (twl_i2c_read_u8(module, &check, address) >= 0) && +- (check == data)) +- return 0; +- dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", +- 1, module, address, check, data); +- +- /* Failed once: Try again */ +- if ((twl_i2c_write_u8(module, data, address) >= 0) && +- (twl_i2c_read_u8(module, &check, address) >= 0) && +- (check == data)) +- return 0; +- dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", +- 2, module, address, check, data); +- +- /* Failed again: Return error */ +- return -EBUSY; +-} +- +-#define twl4030_usb_write_verify(twl, address, data) \ +- twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) +- +-static inline int twl4030_usb_write(struct twl4030_usb *twl, +- u8 address, u8 data) +-{ +- int ret = 0; +- +- ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); +- if (ret < 0) +- dev_dbg(twl->dev, +- "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); +- return ret; +-} +- +-static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) +-{ +- u8 data; +- int ret = 0; +- +- ret = twl_i2c_read_u8(module, &data, address); +- if (ret >= 0) +- ret = data; +- else +- dev_dbg(twl->dev, +- "TWL4030:readb[0x%x,0x%x] Error %d\n", +- module, address, ret); +- +- return ret; +-} +- +-static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) +-{ +- return twl4030_readb(twl, TWL_MODULE_USB, address); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static inline int +-twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +-{ +- return twl4030_usb_write(twl, ULPI_SET(reg), bits); +-} +- +-static inline int +-twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +-{ +- return twl4030_usb_write(twl, ULPI_CLR(reg), bits); +-} +- +-/*-------------------------------------------------------------------------*/ +- +-static bool twl4030_is_driving_vbus(struct twl4030_usb *twl) +-{ +- int ret; +- +- ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS); +- if (ret < 0 || !(ret & PHY_DPLL_CLK)) +- /* +- * if clocks are off, registers are not updated, +- * but we can assume we don't drive VBUS in this case +- */ +- return false; +- +- ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); +- if (ret < 0) +- return false; +- +- return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false; +-} +- +-static enum musb_vbus_id_status +- twl4030_usb_linkstat(struct twl4030_usb *twl) +-{ +- int status; +- enum musb_vbus_id_status linkstat = MUSB_UNKNOWN; +- +- twl->vbus_supplied = false; +- +- /* +- * For ID/VBUS sensing, see manual section 15.4.8 ... +- * except when using only battery backup power, two +- * comparators produce VBUS_PRES and ID_PRES signals, +- * which don't match docs elsewhere. But ... BIT(7) +- * and BIT(2) of STS_HW_CONDITIONS, respectively, do +- * seem to match up. If either is true the USB_PRES +- * signal is active, the OTG module is activated, and +- * its interrupt may be raised (may wake the system). +- */ +- status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); +- if (status < 0) +- dev_err(twl->dev, "USB link status err %d\n", status); +- else if (status & (BIT(7) | BIT(2))) { +- if (status & BIT(7)) { +- if (twl4030_is_driving_vbus(twl)) +- status &= ~BIT(7); +- else +- twl->vbus_supplied = true; +- } +- +- if (status & BIT(2)) +- linkstat = MUSB_ID_GROUND; +- else if (status & BIT(7)) +- linkstat = MUSB_VBUS_VALID; +- else +- linkstat = MUSB_VBUS_OFF; +- } else { +- if (twl->linkstat != MUSB_UNKNOWN) +- linkstat = MUSB_VBUS_OFF; +- } +- +- kobject_uevent(&twl->dev->kobj, linkstat == MUSB_VBUS_VALID +- ? KOBJ_ONLINE : KOBJ_OFFLINE); +- +- dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", +- status, status, linkstat); +- +- /* REVISIT this assumes host and peripheral controllers +- * are registered, and that both are active... +- */ +- +- return linkstat; +-} +- +-static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) +-{ +- twl->usb_mode = mode; +- +- switch (mode) { +- case T2_USB_MODE_ULPI: +- twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, +- ULPI_IFC_CTRL_CARKITMODE); +- twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); +- twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, +- ULPI_FUNC_CTRL_XCVRSEL_MASK | +- ULPI_FUNC_CTRL_OPMODE_MASK); +- break; +- case -1: +- /* FIXME: power on defaults */ +- break; +- default: +- dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", +- mode); +- break; +- } +-} +- +-static void twl4030_i2c_access(struct twl4030_usb *twl, int on) +-{ +- unsigned long timeout; +- int val = twl4030_usb_read(twl, PHY_CLK_CTRL); +- +- if (val >= 0) { +- if (on) { +- /* enable DPLL to access PHY registers over I2C */ +- val |= REQ_PHY_DPLL_CLK; +- WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, +- (u8)val) < 0); +- +- timeout = jiffies + HZ; +- while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & +- PHY_DPLL_CLK) +- && time_before(jiffies, timeout)) +- udelay(10); +- if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & +- PHY_DPLL_CLK)) +- dev_err(twl->dev, "Timeout setting T2 HSUSB " +- "PHY DPLL clock\n"); +- } else { +- /* let ULPI control the DPLL clock */ +- val &= ~REQ_PHY_DPLL_CLK; +- WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, +- (u8)val) < 0); +- } +- } +-} +- +-static void __twl4030_phy_power(struct twl4030_usb *twl, int on) +-{ +- u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); +- +- if (on) +- pwr &= ~PHY_PWR_PHYPWD; +- else +- pwr |= PHY_PWR_PHYPWD; +- +- WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); +-} +- +-static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev) +-{ +- struct twl4030_usb *twl = dev_get_drvdata(dev); +- +- dev_dbg(twl->dev, "%s\n", __func__); +- +- __twl4030_phy_power(twl, 0); +- regulator_disable(twl->usb1v5); +- regulator_disable(twl->usb1v8); +- regulator_disable(twl->usb3v1); +- +- return 0; +-} +- +-static int __maybe_unused twl4030_usb_runtime_resume(struct device *dev) +-{ +- struct twl4030_usb *twl = dev_get_drvdata(dev); +- int res; +- +- dev_dbg(twl->dev, "%s\n", __func__); +- +- res = regulator_enable(twl->usb3v1); +- if (res) +- dev_err(twl->dev, "Failed to enable usb3v1\n"); +- +- res = regulator_enable(twl->usb1v8); +- if (res) +- dev_err(twl->dev, "Failed to enable usb1v8\n"); +- +- /* +- * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP +- * in twl4030) resets the VUSB_DEDICATED2 register. This reset +- * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to +- * SLEEP. We work around this by clearing the bit after usv3v1 +- * is re-activated. This ensures that VUSB3V1 is really active. +- */ +- twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); +- +- res = regulator_enable(twl->usb1v5); +- if (res) +- dev_err(twl->dev, "Failed to enable usb1v5\n"); +- +- __twl4030_phy_power(twl, 1); +- twl4030_usb_write(twl, PHY_CLK_CTRL, +- twl4030_usb_read(twl, PHY_CLK_CTRL) | +- (PHY_CLK_CTRL_CLOCKGATING_EN | +- PHY_CLK_CTRL_CLK32K_EN)); +- +- twl4030_i2c_access(twl, 1); +- twl4030_usb_set_mode(twl, twl->usb_mode); +- if (twl->usb_mode == T2_USB_MODE_ULPI) +- twl4030_i2c_access(twl, 0); +- /* +- * According to the TPS65950 TRM, there has to be at least 50ms +- * delay between setting POWER_CTRL_OTG_ENAB and enabling charging +- * so wait here so that a fully enabled phy can be expected after +- * resume +- */ +- msleep(50); +- return 0; +-} +- +-static int twl4030_phy_power_off(struct phy *phy) +-{ +- struct twl4030_usb *twl = phy_get_drvdata(phy); +- +- dev_dbg(twl->dev, "%s\n", __func__); +- +- return 0; +-} +- +-static int twl4030_phy_power_on(struct phy *phy) +-{ +- struct twl4030_usb *twl = phy_get_drvdata(phy); +- +- dev_dbg(twl->dev, "%s\n", __func__); +- pm_runtime_get_sync(twl->dev); +- schedule_delayed_work(&twl->id_workaround_work, HZ); +- pm_runtime_mark_last_busy(twl->dev); +- pm_runtime_put_autosuspend(twl->dev); +- +- return 0; +-} +- +-static int twl4030_usb_ldo_init(struct twl4030_usb *twl) +-{ +- /* Enable writing to power configuration registers */ +- twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, +- TWL4030_PM_MASTER_PROTECT_KEY); +- +- twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, +- TWL4030_PM_MASTER_PROTECT_KEY); +- +- /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ +- /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ +- +- /* input to VUSB3V1 LDO is from VBAT, not VBUS */ +- twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); +- +- /* Initialize 3.1V regulator */ +- twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); +- +- twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1"); +- if (IS_ERR(twl->usb3v1)) +- return -ENODEV; +- +- twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); +- +- /* Initialize 1.5V regulator */ +- twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); +- +- twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5"); +- if (IS_ERR(twl->usb1v5)) +- return -ENODEV; +- +- twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); +- +- /* Initialize 1.8V regulator */ +- twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); +- +- twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8"); +- if (IS_ERR(twl->usb1v8)) +- return -ENODEV; +- +- twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); +- +- /* disable access to power configuration registers */ +- twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, +- TWL4030_PM_MASTER_PROTECT_KEY); +- +- return 0; +-} +- +-static ssize_t twl4030_usb_vbus_show(struct device *dev, +- struct device_attribute *attr, char *buf) +-{ +- struct twl4030_usb *twl = dev_get_drvdata(dev); +- int ret = -EINVAL; +- +- mutex_lock(&twl->lock); +- ret = sprintf(buf, "%s\n", +- twl->vbus_supplied ? "on" : "off"); +- mutex_unlock(&twl->lock); +- +- return ret; +-} +-static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); +- +-static irqreturn_t twl4030_usb_irq(int irq, void *_twl) +-{ +- struct twl4030_usb *twl = _twl; +- enum musb_vbus_id_status status; +- bool status_changed = false; +- int err; +- +- status = twl4030_usb_linkstat(twl); +- +- mutex_lock(&twl->lock); +- if (status >= 0 && status != twl->linkstat) { +- status_changed = +- cable_present(twl->linkstat) != +- cable_present(status); +- twl->linkstat = status; +- } +- mutex_unlock(&twl->lock); +- +- if (status_changed) { +- /* FIXME add a set_power() method so that B-devices can +- * configure the charger appropriately. It's not always +- * correct to consume VBUS power, and how much current to +- * consume is a function of the USB configuration chosen +- * by the host. +- * +- * REVISIT usb_gadget_vbus_connect(...) as needed, ditto +- * its disconnect() sibling, when changing to/from the +- * USB_LINK_VBUS state. musb_hdrc won't care until it +- * starts to handle softconnect right. +- */ +- if (cable_present(status)) { +- pm_runtime_get_sync(twl->dev); +- } else { +- pm_runtime_mark_last_busy(twl->dev); +- pm_runtime_put_autosuspend(twl->dev); +- } +- twl->musb_mailbox_pending = true; +- } +- if (twl->musb_mailbox_pending) { +- err = musb_mailbox(status); +- if (!err) +- twl->musb_mailbox_pending = false; +- } +- +- /* don't schedule during sleep - irq works right then */ +- if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) { +- cancel_delayed_work(&twl->id_workaround_work); +- schedule_delayed_work(&twl->id_workaround_work, HZ); +- } +- +- if (irq) +- sysfs_notify(&twl->dev->kobj, NULL, "vbus"); +- +- return IRQ_HANDLED; +-} +- +-static void twl4030_id_workaround_work(struct work_struct *work) +-{ +- struct twl4030_usb *twl = container_of(work, struct twl4030_usb, +- id_workaround_work.work); +- +- twl4030_usb_irq(0, twl); +-} +- +-static int twl4030_phy_init(struct phy *phy) +-{ +- struct twl4030_usb *twl = phy_get_drvdata(phy); +- +- pm_runtime_get_sync(twl->dev); +- twl->linkstat = MUSB_UNKNOWN; +- schedule_delayed_work(&twl->id_workaround_work, HZ); +- pm_runtime_mark_last_busy(twl->dev); +- pm_runtime_put_autosuspend(twl->dev); +- +- return 0; +-} +- +-static int twl4030_set_peripheral(struct usb_otg *otg, +- struct usb_gadget *gadget) +-{ +- if (!otg) +- return -ENODEV; +- +- otg->gadget = gadget; +- if (!gadget) +- otg->state = OTG_STATE_UNDEFINED; +- +- return 0; +-} +- +-static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) +-{ +- if (!otg) +- return -ENODEV; +- +- otg->host = host; +- if (!host) +- otg->state = OTG_STATE_UNDEFINED; +- +- return 0; +-} +- +-static const struct phy_ops ops = { +- .init = twl4030_phy_init, +- .power_on = twl4030_phy_power_on, +- .power_off = twl4030_phy_power_off, +- .owner = THIS_MODULE, +-}; +- +-static const struct dev_pm_ops twl4030_usb_pm_ops = { +- SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend, +- twl4030_usb_runtime_resume, NULL) +-}; +- +-static int twl4030_usb_probe(struct platform_device *pdev) +-{ +- struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev); +- struct twl4030_usb *twl; +- struct phy *phy; +- int status, err; +- struct usb_otg *otg; +- struct device_node *np = pdev->dev.of_node; +- struct phy_provider *phy_provider; +- +- twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); +- if (!twl) +- return -ENOMEM; +- +- if (np) +- of_property_read_u32(np, "usb_mode", +- (enum twl4030_usb_mode *)&twl->usb_mode); +- else if (pdata) { +- twl->usb_mode = pdata->usb_mode; +- } else { +- dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); +- return -EINVAL; +- } +- +- otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); +- if (!otg) +- return -ENOMEM; +- +- twl->dev = &pdev->dev; +- twl->irq = platform_get_irq(pdev, 0); +- twl->vbus_supplied = false; +- twl->linkstat = MUSB_UNKNOWN; +- twl->musb_mailbox_pending = false; +- +- twl->phy.dev = twl->dev; +- twl->phy.label = "twl4030"; +- twl->phy.otg = otg; +- twl->phy.type = USB_PHY_TYPE_USB2; +- +- otg->usb_phy = &twl->phy; +- otg->set_host = twl4030_set_host; +- otg->set_peripheral = twl4030_set_peripheral; +- +- phy = devm_phy_create(twl->dev, NULL, &ops); +- if (IS_ERR(phy)) { +- dev_dbg(&pdev->dev, "Failed to create PHY\n"); +- return PTR_ERR(phy); +- } +- +- phy_set_drvdata(phy, twl); +- +- phy_provider = devm_of_phy_provider_register(twl->dev, +- of_phy_simple_xlate); +- if (IS_ERR(phy_provider)) +- return PTR_ERR(phy_provider); +- +- /* init mutex for workqueue */ +- mutex_init(&twl->lock); +- +- INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); +- +- err = twl4030_usb_ldo_init(twl); +- if (err) { +- dev_err(&pdev->dev, "ldo init failed\n"); +- return err; +- } +- usb_add_phy_dev(&twl->phy); +- +- platform_set_drvdata(pdev, twl); +- if (device_create_file(&pdev->dev, &dev_attr_vbus)) +- dev_warn(&pdev->dev, "could not create sysfs file\n"); +- +- ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); +- +- pm_runtime_use_autosuspend(&pdev->dev); +- pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); +- pm_runtime_enable(&pdev->dev); +- pm_runtime_get_sync(&pdev->dev); +- +- /* Our job is to use irqs and status from the power module +- * to keep the transceiver disabled when nothing's connected. +- * +- * FIXME we actually shouldn't start enabling it until the +- * USB controller drivers have said they're ready, by calling +- * set_host() and/or set_peripheral() ... OTG_capable boards +- * need both handles, otherwise just one suffices. +- */ +- status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, +- twl4030_usb_irq, IRQF_TRIGGER_FALLING | +- IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); +- if (status < 0) { +- dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", +- twl->irq, status); +- return status; +- } +- +- if (pdata) +- err = phy_create_lookup(phy, "usb", "musb-hdrc.0"); +- if (err) +- return err; +- +- pm_runtime_mark_last_busy(&pdev->dev); +- pm_runtime_put_autosuspend(twl->dev); +- +- dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); +- return 0; +-} +- +-static int twl4030_usb_remove(struct platform_device *pdev) +-{ +- struct twl4030_usb *twl = platform_get_drvdata(pdev); +- int val; +- +- usb_remove_phy(&twl->phy); +- pm_runtime_get_sync(twl->dev); +- cancel_delayed_work(&twl->id_workaround_work); +- device_remove_file(twl->dev, &dev_attr_vbus); +- +- /* set transceiver mode to power on defaults */ +- twl4030_usb_set_mode(twl, -1); +- +- /* idle ulpi before powering off */ +- if (cable_present(twl->linkstat)) +- pm_runtime_put_noidle(twl->dev); +- pm_runtime_mark_last_busy(twl->dev); +- pm_runtime_dont_use_autosuspend(&pdev->dev); +- pm_runtime_put_sync(twl->dev); +- pm_runtime_disable(twl->dev); +- +- /* autogate 60MHz ULPI clock, +- * clear dpll clock request for i2c access, +- * disable 32KHz +- */ +- val = twl4030_usb_read(twl, PHY_CLK_CTRL); +- if (val >= 0) { +- val |= PHY_CLK_CTRL_CLOCKGATING_EN; +- val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); +- twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); +- } +- +- /* disable complete OTG block */ +- twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); +- +- return 0; +-} +- +-#ifdef CONFIG_OF +-static const struct of_device_id twl4030_usb_id_table[] = { +- { .compatible = "ti,twl4030-usb" }, +- {} +-}; +-MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); +-#endif +- +-static struct platform_driver twl4030_usb_driver = { +- .probe = twl4030_usb_probe, +- .remove = twl4030_usb_remove, +- .driver = { +- .name = "twl4030_usb", +- .pm = &twl4030_usb_pm_ops, +- .of_match_table = of_match_ptr(twl4030_usb_id_table), +- }, +-}; +- +-static int __init twl4030_usb_init(void) +-{ +- return platform_driver_register(&twl4030_usb_driver); +-} +-subsys_initcall(twl4030_usb_init); +- +-static void __exit twl4030_usb_exit(void) +-{ +- platform_driver_unregister(&twl4030_usb_driver); +-} +-module_exit(twl4030_usb_exit); +- +-MODULE_ALIAS("platform:twl4030_usb"); +-MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); +-MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig +new file mode 100644 +index 000000000000..7bfa64baf837 +--- /dev/null ++++ b/drivers/phy/qualcomm/Kconfig +@@ -0,0 +1,58 @@ ++# ++# Phy drivers for Qualcomm platforms ++# ++config PHY_QCOM_APQ8064_SATA ++ tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" ++ depends on ARCH_QCOM ++ depends on HAS_IOMEM ++ depends on OF ++ select GENERIC_PHY ++ ++config PHY_QCOM_IPQ806X_SATA ++ tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" ++ depends on ARCH_QCOM ++ depends on HAS_IOMEM ++ depends on OF ++ select GENERIC_PHY ++ ++config PHY_QCOM_QMP ++ tristate "Qualcomm QMP PHY Driver" ++ depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST) ++ select GENERIC_PHY ++ help ++ Enable this to support the QMP PHY transceiver that is used ++ with controllers such as PCIe, UFS, and USB on Qualcomm chips. ++ ++config PHY_QCOM_QUSB2 ++ tristate "Qualcomm QUSB2 PHY Driver" ++ depends on OF && (ARCH_QCOM || COMPILE_TEST) ++ depends on NVMEM || !NVMEM ++ select GENERIC_PHY ++ help ++ Enable this to support the HighSpeed QUSB2 PHY transceiver for USB ++ controllers on Qualcomm chips. This driver supports the high-speed ++ PHY which is usually paired with either the ChipIdea or Synopsys DWC3 ++ USB IPs on MSM SOCs. ++ ++config PHY_QCOM_UFS ++ tristate "Qualcomm UFS PHY driver" ++ depends on OF && ARCH_QCOM ++ select GENERIC_PHY ++ help ++ Support for UFS PHY on QCOM chipsets. ++ ++config PHY_QCOM_USB_HS ++ tristate "Qualcomm USB HS PHY module" ++ depends on USB_ULPI_BUS ++ depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in ++ select GENERIC_PHY ++ help ++ Support for the USB high-speed ULPI compliant phy on Qualcomm ++ chipsets. ++ ++config PHY_QCOM_USB_HSIC ++ tristate "Qualcomm USB HSIC ULPI PHY module" ++ depends on USB_ULPI_BUS ++ select GENERIC_PHY ++ help ++ Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. +diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile +new file mode 100644 +index 000000000000..2e183d7695fd +--- /dev/null ++++ b/drivers/phy/qualcomm/Makefile +@@ -0,0 +1,9 @@ ++obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o ++obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o ++obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o ++obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o ++obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o ++obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o ++obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o ++obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o ++obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o +diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c +new file mode 100644 +index 000000000000..69ce2afac015 +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c +@@ -0,0 +1,287 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* PHY registers */ ++#define UNIPHY_PLL_REFCLK_CFG 0x000 ++#define UNIPHY_PLL_PWRGEN_CFG 0x014 ++#define UNIPHY_PLL_GLB_CFG 0x020 ++#define UNIPHY_PLL_SDM_CFG0 0x038 ++#define UNIPHY_PLL_SDM_CFG1 0x03C ++#define UNIPHY_PLL_SDM_CFG2 0x040 ++#define UNIPHY_PLL_SDM_CFG3 0x044 ++#define UNIPHY_PLL_SDM_CFG4 0x048 ++#define UNIPHY_PLL_SSC_CFG0 0x04C ++#define UNIPHY_PLL_SSC_CFG1 0x050 ++#define UNIPHY_PLL_SSC_CFG2 0x054 ++#define UNIPHY_PLL_SSC_CFG3 0x058 ++#define UNIPHY_PLL_LKDET_CFG0 0x05C ++#define UNIPHY_PLL_LKDET_CFG1 0x060 ++#define UNIPHY_PLL_LKDET_CFG2 0x064 ++#define UNIPHY_PLL_CAL_CFG0 0x06C ++#define UNIPHY_PLL_CAL_CFG8 0x08C ++#define UNIPHY_PLL_CAL_CFG9 0x090 ++#define UNIPHY_PLL_CAL_CFG10 0x094 ++#define UNIPHY_PLL_CAL_CFG11 0x098 ++#define UNIPHY_PLL_STATUS 0x0C0 ++ ++#define SATA_PHY_SER_CTRL 0x100 ++#define SATA_PHY_TX_DRIV_CTRL0 0x104 ++#define SATA_PHY_TX_DRIV_CTRL1 0x108 ++#define SATA_PHY_TX_IMCAL0 0x11C ++#define SATA_PHY_TX_IMCAL2 0x124 ++#define SATA_PHY_RX_IMCAL0 0x128 ++#define SATA_PHY_EQUAL 0x13C ++#define SATA_PHY_OOB_TERM 0x144 ++#define SATA_PHY_CDR_CTRL0 0x148 ++#define SATA_PHY_CDR_CTRL1 0x14C ++#define SATA_PHY_CDR_CTRL2 0x150 ++#define SATA_PHY_CDR_CTRL3 0x154 ++#define SATA_PHY_PI_CTRL0 0x168 ++#define SATA_PHY_POW_DWN_CTRL0 0x180 ++#define SATA_PHY_POW_DWN_CTRL1 0x184 ++#define SATA_PHY_TX_DATA_CTRL 0x188 ++#define SATA_PHY_ALIGNP 0x1A4 ++#define SATA_PHY_TX_IMCAL_STAT 0x1E4 ++#define SATA_PHY_RX_IMCAL_STAT 0x1E8 ++ ++#define UNIPHY_PLL_LOCK BIT(0) ++#define SATA_PHY_TX_CAL BIT(0) ++#define SATA_PHY_RX_CAL BIT(0) ++ ++/* default timeout set to 1 sec */ ++#define TIMEOUT_MS 10000 ++#define DELAY_INTERVAL_US 100 ++ ++struct qcom_apq8064_sata_phy { ++ void __iomem *mmio; ++ struct clk *cfg_clk; ++ struct device *dev; ++}; ++ ++/* Helper function to do poll and timeout */ ++static int read_poll_timeout(void __iomem *addr, u32 mask) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); ++ ++ do { ++ if (readl_relaxed(addr) & mask) ++ return 0; ++ ++ usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); ++ } while (!time_after(jiffies, timeout)); ++ ++ return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; ++} ++ ++static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) ++{ ++ struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); ++ void __iomem *base = phy->mmio; ++ int ret = 0; ++ ++ /* SATA phy initialization */ ++ writel_relaxed(0x01, base + SATA_PHY_SER_CTRL); ++ writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0); ++ /* Make sure the power down happens before power up */ ++ mb(); ++ usleep_range(10, 60); ++ ++ writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); ++ writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); ++ writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); ++ writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); ++ writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2); ++ ++ /* Write UNIPHYPLL registers to configure PLL */ ++ writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG); ++ writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG); ++ ++ writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0); ++ writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8); ++ writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9); ++ writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10); ++ writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11); ++ ++ writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0); ++ writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1); ++ writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2); ++ writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3); ++ writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4); ++ ++ writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0); ++ writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1); ++ writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2); ++ writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3); ++ ++ writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0); ++ writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1); ++ ++ writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG); ++ /* make sure global config LDO power down happens before power up */ ++ mb(); ++ ++ writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG); ++ writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2); ++ ++ /* PLL Lock wait */ ++ ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); ++ if (ret) { ++ dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n"); ++ return ret; ++ } ++ ++ /* TX Calibration */ ++ ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); ++ if (ret) { ++ dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n"); ++ return ret; ++ } ++ ++ /* RX Calibration */ ++ ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); ++ if (ret) { ++ dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n"); ++ return ret; ++ } ++ ++ /* SATA phy calibrated succesfully, power up to functional mode */ ++ writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); ++ writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); ++ writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); ++ ++ writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1); ++ writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0); ++ writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1); ++ writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2); ++ writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0); ++ writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3); ++ writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); ++ ++ writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL); ++ writel_relaxed(0x43, base + SATA_PHY_ALIGNP); ++ writel_relaxed(0x04, base + SATA_PHY_OOB_TERM); ++ ++ writel_relaxed(0x01, base + SATA_PHY_EQUAL); ++ writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0); ++ writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1); ++ ++ return 0; ++} ++ ++static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy) ++{ ++ struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); ++ void __iomem *base = phy->mmio; ++ ++ /* Power down PHY */ ++ writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0); ++ writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1); ++ ++ /* Power down PLL block */ ++ writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG); ++ ++ return 0; ++} ++ ++static const struct phy_ops qcom_apq8064_sata_phy_ops = { ++ .init = qcom_apq8064_sata_phy_init, ++ .exit = qcom_apq8064_sata_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) ++{ ++ struct qcom_apq8064_sata_phy *phy; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ struct phy_provider *phy_provider; ++ struct phy *generic_phy; ++ int ret; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy->mmio = devm_ioremap_resource(dev, res); ++ if (IS_ERR(phy->mmio)) ++ return PTR_ERR(phy->mmio); ++ ++ generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops); ++ if (IS_ERR(generic_phy)) { ++ dev_err(dev, "%s: failed to create phy\n", __func__); ++ return PTR_ERR(generic_phy); ++ } ++ ++ phy->dev = dev; ++ phy_set_drvdata(generic_phy, phy); ++ platform_set_drvdata(pdev, phy); ++ ++ phy->cfg_clk = devm_clk_get(dev, "cfg"); ++ if (IS_ERR(phy->cfg_clk)) { ++ dev_err(dev, "Failed to get sata cfg clock\n"); ++ return PTR_ERR(phy->cfg_clk); ++ } ++ ++ ret = clk_prepare_enable(phy->cfg_clk); ++ if (ret) ++ return ret; ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ clk_disable_unprepare(phy->cfg_clk); ++ dev_err(dev, "%s: failed to register phy\n", __func__); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev) ++{ ++ struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(phy->cfg_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = { ++ { .compatible = "qcom,apq8064-sata-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match); ++ ++static struct platform_driver qcom_apq8064_sata_phy_driver = { ++ .probe = qcom_apq8064_sata_phy_probe, ++ .remove = qcom_apq8064_sata_phy_remove, ++ .driver = { ++ .name = "qcom-apq8064-sata-phy", ++ .of_match_table = qcom_apq8064_sata_phy_of_match, ++ } ++}; ++module_platform_driver(qcom_apq8064_sata_phy_driver); ++ ++MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c +new file mode 100644 +index 000000000000..0ad127cc9298 +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct qcom_ipq806x_sata_phy { ++ void __iomem *mmio; ++ struct clk *cfg_clk; ++ struct device *dev; ++}; ++ ++#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) ++ ++#define SATA_PHY_P0_PARAM0 0x200 ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) ++#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) ++ ++#define SATA_PHY_P0_PARAM1 0x204 ++#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) ++#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) ++ ++#define SATA_PHY_P0_PARAM2 0x208 ++#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) ++#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) ++ ++#define SATA_PHY_P0_PARAM3 0x20C ++#define SATA_PHY_SSC_EN 0x8 ++#define SATA_PHY_P0_PARAM4 0x210 ++#define SATA_PHY_REF_SSP_EN 0x2 ++#define SATA_PHY_RESET 0x1 ++ ++static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) ++{ ++ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); ++ u32 reg; ++ ++ /* Setting SSC_EN to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); ++ reg = reg | SATA_PHY_SSC_EN; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & ++ ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | ++ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | ++ SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); ++ reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & ++ ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); ++ reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | ++ SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); ++ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & ++ ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; ++ reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); ++ ++ /* Setting PHY_RESET to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ /* Setting REF_SSP_EN to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ /* make sure all changes complete before we let the PHY out of reset */ ++ mb(); ++ ++ /* sleep for max. 50us more to combine processor wakeups */ ++ usleep_range(20, 20 + 50); ++ ++ /* Clearing PHY_RESET to 0 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg & ~SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ return 0; ++} ++ ++static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) ++{ ++ struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); ++ u32 reg; ++ ++ /* Setting PHY_RESET to 1 */ ++ reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); ++ reg = reg | SATA_PHY_RESET; ++ writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); ++ ++ return 0; ++} ++ ++static const struct phy_ops qcom_ipq806x_sata_phy_ops = { ++ .init = qcom_ipq806x_sata_phy_init, ++ .exit = qcom_ipq806x_sata_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) ++{ ++ struct qcom_ipq806x_sata_phy *phy; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ struct phy_provider *phy_provider; ++ struct phy *generic_phy; ++ int ret; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy->mmio = devm_ioremap_resource(dev, res); ++ if (IS_ERR(phy->mmio)) ++ return PTR_ERR(phy->mmio); ++ ++ generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops); ++ if (IS_ERR(generic_phy)) { ++ dev_err(dev, "%s: failed to create phy\n", __func__); ++ return PTR_ERR(generic_phy); ++ } ++ ++ phy->dev = dev; ++ phy_set_drvdata(generic_phy, phy); ++ platform_set_drvdata(pdev, phy); ++ ++ phy->cfg_clk = devm_clk_get(dev, "cfg"); ++ if (IS_ERR(phy->cfg_clk)) { ++ dev_err(dev, "Failed to get sata cfg clock\n"); ++ return PTR_ERR(phy->cfg_clk); ++ } ++ ++ ret = clk_prepare_enable(phy->cfg_clk); ++ if (ret) ++ return ret; ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ clk_disable_unprepare(phy->cfg_clk); ++ dev_err(dev, "%s: failed to register phy\n", __func__); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) ++{ ++ struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(phy->cfg_clk); ++ ++ return 0; ++} ++ ++static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { ++ { .compatible = "qcom,ipq806x-sata-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); ++ ++static struct platform_driver qcom_ipq806x_sata_phy_driver = { ++ .probe = qcom_ipq806x_sata_phy_probe, ++ .remove = qcom_ipq806x_sata_phy_remove, ++ .driver = { ++ .name = "qcom-ipq806x-sata-phy", ++ .of_match_table = qcom_ipq806x_sata_phy_of_match, ++ } ++}; ++module_platform_driver(qcom_ipq806x_sata_phy_driver); ++ ++MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c +new file mode 100644 +index 000000000000..78ca62897784 +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-qmp.c +@@ -0,0 +1,1153 @@ ++/* ++ * Copyright (c) 2017, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* QMP PHY QSERDES COM registers */ ++#define QSERDES_COM_BG_TIMER 0x00c ++#define QSERDES_COM_SSC_EN_CENTER 0x010 ++#define QSERDES_COM_SSC_ADJ_PER1 0x014 ++#define QSERDES_COM_SSC_ADJ_PER2 0x018 ++#define QSERDES_COM_SSC_PER1 0x01c ++#define QSERDES_COM_SSC_PER2 0x020 ++#define QSERDES_COM_SSC_STEP_SIZE1 0x024 ++#define QSERDES_COM_SSC_STEP_SIZE2 0x028 ++#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034 ++#define QSERDES_COM_CLK_ENABLE1 0x038 ++#define QSERDES_COM_SYS_CLK_CTRL 0x03c ++#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040 ++#define QSERDES_COM_PLL_IVCO 0x048 ++#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c ++#define QSERDES_COM_LOCK_CMP2_MODE0 0x050 ++#define QSERDES_COM_LOCK_CMP3_MODE0 0x054 ++#define QSERDES_COM_LOCK_CMP1_MODE1 0x058 ++#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c ++#define QSERDES_COM_LOCK_CMP3_MODE1 0x060 ++#define QSERDES_COM_BG_TRIM 0x070 ++#define QSERDES_COM_CLK_EP_DIV 0x074 ++#define QSERDES_COM_CP_CTRL_MODE0 0x078 ++#define QSERDES_COM_CP_CTRL_MODE1 0x07c ++#define QSERDES_COM_PLL_RCTRL_MODE0 0x084 ++#define QSERDES_COM_PLL_RCTRL_MODE1 0x088 ++#define QSERDES_COM_PLL_CCTRL_MODE0 0x090 ++#define QSERDES_COM_PLL_CCTRL_MODE1 0x094 ++#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac ++#define QSERDES_COM_RESETSM_CNTRL 0x0b4 ++#define QSERDES_COM_RESTRIM_CTRL 0x0bc ++#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 ++#define QSERDES_COM_LOCK_CMP_EN 0x0c8 ++#define QSERDES_COM_LOCK_CMP_CFG 0x0cc ++#define QSERDES_COM_DEC_START_MODE0 0x0d0 ++#define QSERDES_COM_DEC_START_MODE1 0x0d4 ++#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc ++#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0 ++#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4 ++#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 ++#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec ++#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 ++#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 ++#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c ++#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 ++#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114 ++#define QSERDES_COM_VCO_TUNE_CTRL 0x124 ++#define QSERDES_COM_VCO_TUNE_MAP 0x128 ++#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c ++#define QSERDES_COM_VCO_TUNE2_MODE0 0x130 ++#define QSERDES_COM_VCO_TUNE1_MODE1 0x134 ++#define QSERDES_COM_VCO_TUNE2_MODE1 0x138 ++#define QSERDES_COM_VCO_TUNE_TIMER1 0x144 ++#define QSERDES_COM_VCO_TUNE_TIMER2 0x148 ++#define QSERDES_COM_BG_CTRL 0x170 ++#define QSERDES_COM_CLK_SELECT 0x174 ++#define QSERDES_COM_HSCLK_SEL 0x178 ++#define QSERDES_COM_CORECLK_DIV 0x184 ++#define QSERDES_COM_CORE_CLK_EN 0x18c ++#define QSERDES_COM_C_READY_STATUS 0x190 ++#define QSERDES_COM_CMN_CONFIG 0x194 ++#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c ++#define QSERDES_COM_DEBUG_BUS0 0x1a0 ++#define QSERDES_COM_DEBUG_BUS1 0x1a4 ++#define QSERDES_COM_DEBUG_BUS2 0x1a8 ++#define QSERDES_COM_DEBUG_BUS3 0x1ac ++#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0 ++#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc ++ ++/* QMP PHY TX registers */ ++#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054 ++#define QSERDES_TX_DEBUG_BUS_SEL 0x064 ++#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068 ++#define QSERDES_TX_LANE_MODE 0x094 ++#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac ++ ++/* QMP PHY RX registers */ ++#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010 ++#define QSERDES_RX_UCDR_SO_GAIN 0x01c ++#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040 ++#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048 ++#define QSERDES_RX_RX_TERM_BW 0x090 ++#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4 ++#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8 ++#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc ++#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0 ++#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8 ++#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc ++#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0 ++#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108 ++#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c ++#define QSERDES_RX_SIGDET_ENABLES 0x110 ++#define QSERDES_RX_SIGDET_CNTRL 0x114 ++#define QSERDES_RX_SIGDET_LVL 0x118 ++#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c ++#define QSERDES_RX_RX_BAND 0x120 ++#define QSERDES_RX_RX_INTERFACE_MODE 0x12c ++ ++/* QMP PHY PCS registers */ ++#define QPHY_POWER_DOWN_CONTROL 0x04 ++#define QPHY_TXDEEMPH_M6DB_V0 0x24 ++#define QPHY_TXDEEMPH_M3P5DB_V0 0x28 ++#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54 ++#define QPHY_RX_IDLE_DTCT_CNTRL 0x58 ++#define QPHY_POWER_STATE_CONFIG1 0x60 ++#define QPHY_POWER_STATE_CONFIG2 0x64 ++#define QPHY_POWER_STATE_CONFIG4 0x6c ++#define QPHY_LOCK_DETECT_CONFIG1 0x80 ++#define QPHY_LOCK_DETECT_CONFIG2 0x84 ++#define QPHY_LOCK_DETECT_CONFIG3 0x88 ++#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0 ++#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4 ++ ++/* QPHY_SW_RESET bit */ ++#define SW_RESET BIT(0) ++/* QPHY_POWER_DOWN_CONTROL */ ++#define SW_PWRDN BIT(0) ++#define REFCLK_DRV_DSBL BIT(1) ++/* QPHY_START_CONTROL bits */ ++#define SERDES_START BIT(0) ++#define PCS_START BIT(1) ++#define PLL_READY_GATE_EN BIT(3) ++/* QPHY_PCS_STATUS bit */ ++#define PHYSTATUS BIT(6) ++/* QPHY_COM_PCS_READY_STATUS bit */ ++#define PCS_READY BIT(0) ++ ++#define PHY_INIT_COMPLETE_TIMEOUT 1000 ++#define POWER_DOWN_DELAY_US_MIN 10 ++#define POWER_DOWN_DELAY_US_MAX 11 ++ ++#define MAX_PROP_NAME 32 ++ ++struct qmp_phy_init_tbl { ++ unsigned int offset; ++ unsigned int val; ++ /* ++ * register part of layout ? ++ * if yes, then offset gives index in the reg-layout ++ */ ++ int in_layout; ++}; ++ ++#define QMP_PHY_INIT_CFG(o, v) \ ++ { \ ++ .offset = o, \ ++ .val = v, \ ++ } ++ ++#define QMP_PHY_INIT_CFG_L(o, v) \ ++ { \ ++ .offset = o, \ ++ .val = v, \ ++ .in_layout = 1, \ ++ } ++ ++/* set of registers with offsets different per-PHY */ ++enum qphy_reg_layout { ++ /* Common block control registers */ ++ QPHY_COM_SW_RESET, ++ QPHY_COM_POWER_DOWN_CONTROL, ++ QPHY_COM_START_CONTROL, ++ QPHY_COM_PCS_READY_STATUS, ++ /* PCS registers */ ++ QPHY_PLL_LOCK_CHK_DLY_TIME, ++ QPHY_FLL_CNTRL1, ++ QPHY_FLL_CNTRL2, ++ QPHY_FLL_CNT_VAL_L, ++ QPHY_FLL_CNT_VAL_H_TOL, ++ QPHY_FLL_MAN_CODE, ++ QPHY_SW_RESET, ++ QPHY_START_CTRL, ++ QPHY_PCS_READY_STATUS, ++}; ++ ++static const unsigned int pciephy_regs_layout[] = { ++ [QPHY_COM_SW_RESET] = 0x400, ++ [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, ++ [QPHY_COM_START_CONTROL] = 0x408, ++ [QPHY_COM_PCS_READY_STATUS] = 0x448, ++ [QPHY_PLL_LOCK_CHK_DLY_TIME] = 0xa8, ++ [QPHY_FLL_CNTRL1] = 0xc4, ++ [QPHY_FLL_CNTRL2] = 0xc8, ++ [QPHY_FLL_CNT_VAL_L] = 0xcc, ++ [QPHY_FLL_CNT_VAL_H_TOL] = 0xd0, ++ [QPHY_FLL_MAN_CODE] = 0xd4, ++ [QPHY_SW_RESET] = 0x00, ++ [QPHY_START_CTRL] = 0x08, ++ [QPHY_PCS_READY_STATUS] = 0x174, ++}; ++ ++static const unsigned int usb3phy_regs_layout[] = { ++ [QPHY_FLL_CNTRL1] = 0xc0, ++ [QPHY_FLL_CNTRL2] = 0xc4, ++ [QPHY_FLL_CNT_VAL_L] = 0xc8, ++ [QPHY_FLL_CNT_VAL_H_TOL] = 0xcc, ++ [QPHY_FLL_MAN_CODE] = 0xd0, ++ [QPHY_SW_RESET] = 0x00, ++ [QPHY_START_CTRL] = 0x08, ++ [QPHY_PCS_READY_STATUS] = 0x17c, ++}; ++ ++static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), ++ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42), ++ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), ++ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), ++ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), ++ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09), ++ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), ++ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), ++ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), ++ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), ++ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a), ++ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), ++ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), ++ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), ++ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), ++ QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15), ++ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), ++ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), ++ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40), ++}; ++ ++static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), ++ QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), ++}; ++ ++static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18), ++ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), ++ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04), ++ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), ++ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), ++ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19), ++}; ++ ++static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = { ++ QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c), ++ QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00), ++ QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), ++ ++ QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05), ++ ++ QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05), ++ QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02), ++ QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00), ++ QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3), ++ QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e), ++}; ++ ++static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), ++ QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), ++ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04), ++ /* PLL and Loop filter settings */ ++ QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), ++ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), ++ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), ++ QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), ++ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), ++ QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), ++ QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), ++ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), ++ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), ++ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), ++ /* SSC settings */ ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), ++ QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), ++}; ++ ++static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), ++ QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), ++ QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), ++}; ++ ++static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = { ++ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), ++ QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), ++ QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), ++ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), ++ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18), ++ QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), ++}; ++ ++static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { ++ /* FLL settings */ ++ QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03), ++ QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02), ++ QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09), ++ QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42), ++ QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85), ++ ++ /* Lock Det settings */ ++ QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1), ++ QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f), ++ QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47), ++ QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08), ++}; ++ ++/* struct qmp_phy_cfg - per-PHY initialization config */ ++struct qmp_phy_cfg { ++ /* phy-type - PCIE/UFS/USB */ ++ unsigned int type; ++ /* number of lanes provided by phy */ ++ int nlanes; ++ ++ /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ ++ const struct qmp_phy_init_tbl *serdes_tbl; ++ int serdes_tbl_num; ++ const struct qmp_phy_init_tbl *tx_tbl; ++ int tx_tbl_num; ++ const struct qmp_phy_init_tbl *rx_tbl; ++ int rx_tbl_num; ++ const struct qmp_phy_init_tbl *pcs_tbl; ++ int pcs_tbl_num; ++ ++ /* clock ids to be requested */ ++ const char * const *clk_list; ++ int num_clks; ++ /* resets to be requested */ ++ const char * const *reset_list; ++ int num_resets; ++ /* regulators to be requested */ ++ const char * const *vreg_list; ++ int num_vregs; ++ ++ /* array of registers with different offsets */ ++ const unsigned int *regs; ++ ++ unsigned int start_ctrl; ++ unsigned int pwrdn_ctrl; ++ unsigned int mask_pcs_ready; ++ unsigned int mask_com_pcs_ready; ++ ++ /* true, if PHY has a separate PHY_COM control block */ ++ bool has_phy_com_ctrl; ++ /* true, if PHY has a reset for individual lanes */ ++ bool has_lane_rst; ++ /* true, if PHY needs delay after POWER_DOWN */ ++ bool has_pwrdn_delay; ++ /* power_down delay in usec */ ++ int pwrdn_delay_min; ++ int pwrdn_delay_max; ++}; ++ ++/** ++ * struct qmp_phy - per-lane phy descriptor ++ * ++ * @phy: generic phy ++ * @tx: iomapped memory space for lane's tx ++ * @rx: iomapped memory space for lane's rx ++ * @pcs: iomapped memory space for lane's pcs ++ * @pipe_clk: pipe lock ++ * @index: lane index ++ * @qmp: QMP phy to which this lane belongs ++ * @lane_rst: lane's reset controller ++ */ ++struct qmp_phy { ++ struct phy *phy; ++ void __iomem *tx; ++ void __iomem *rx; ++ void __iomem *pcs; ++ struct clk *pipe_clk; ++ unsigned int index; ++ struct qcom_qmp *qmp; ++ struct reset_control *lane_rst; ++}; ++ ++/** ++ * struct qcom_qmp - structure holding QMP phy block attributes ++ * ++ * @dev: device ++ * @serdes: iomapped memory space for phy's serdes ++ * ++ * @clks: array of clocks required by phy ++ * @resets: array of resets required by phy ++ * @vregs: regulator supplies bulk data ++ * ++ * @cfg: phy specific configuration ++ * @phys: array of per-lane phy descriptors ++ * @phy_mutex: mutex lock for PHY common block initialization ++ * @init_count: phy common block initialization count ++ */ ++struct qcom_qmp { ++ struct device *dev; ++ void __iomem *serdes; ++ ++ struct clk **clks; ++ struct reset_control **resets; ++ struct regulator_bulk_data *vregs; ++ ++ const struct qmp_phy_cfg *cfg; ++ struct qmp_phy **phys; ++ ++ struct mutex phy_mutex; ++ int init_count; ++}; ++ ++static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) ++{ ++ u32 reg; ++ ++ reg = readl(base + offset); ++ reg |= val; ++ writel(reg, base + offset); ++ ++ /* ensure that above write is through */ ++ readl(base + offset); ++} ++ ++static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) ++{ ++ u32 reg; ++ ++ reg = readl(base + offset); ++ reg &= ~val; ++ writel(reg, base + offset); ++ ++ /* ensure that above write is through */ ++ readl(base + offset); ++} ++ ++/* list of clocks required by phy */ ++static const char * const msm8996_phy_clk_l[] = { ++ "aux", "cfg_ahb", "ref", ++}; ++ ++/* list of resets */ ++static const char * const msm8996_pciephy_reset_l[] = { ++ "phy", "common", "cfg", ++}; ++ ++static const char * const msm8996_usb3phy_reset_l[] = { ++ "phy", "common", ++}; ++ ++/* list of regulators */ ++static const char * const msm8996_phy_vreg_l[] = { ++ "vdda-phy", "vdda-pll", ++}; ++ ++static const struct qmp_phy_cfg msm8996_pciephy_cfg = { ++ .type = PHY_TYPE_PCIE, ++ .nlanes = 3, ++ ++ .serdes_tbl = msm8996_pcie_serdes_tbl, ++ .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl), ++ .tx_tbl = msm8996_pcie_tx_tbl, ++ .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl), ++ .rx_tbl = msm8996_pcie_rx_tbl, ++ .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl), ++ .pcs_tbl = msm8996_pcie_pcs_tbl, ++ .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl), ++ .clk_list = msm8996_phy_clk_l, ++ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), ++ .reset_list = msm8996_pciephy_reset_l, ++ .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l), ++ .vreg_list = msm8996_phy_vreg_l, ++ .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), ++ .regs = pciephy_regs_layout, ++ ++ .start_ctrl = PCS_START | PLL_READY_GATE_EN, ++ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, ++ .mask_com_pcs_ready = PCS_READY, ++ ++ .has_phy_com_ctrl = true, ++ .has_lane_rst = true, ++ .has_pwrdn_delay = true, ++ .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, ++ .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, ++}; ++ ++static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { ++ .type = PHY_TYPE_USB3, ++ .nlanes = 1, ++ ++ .serdes_tbl = msm8996_usb3_serdes_tbl, ++ .serdes_tbl_num = ARRAY_SIZE(msm8996_usb3_serdes_tbl), ++ .tx_tbl = msm8996_usb3_tx_tbl, ++ .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), ++ .rx_tbl = msm8996_usb3_rx_tbl, ++ .rx_tbl_num = ARRAY_SIZE(msm8996_usb3_rx_tbl), ++ .pcs_tbl = msm8996_usb3_pcs_tbl, ++ .pcs_tbl_num = ARRAY_SIZE(msm8996_usb3_pcs_tbl), ++ .clk_list = msm8996_phy_clk_l, ++ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), ++ .reset_list = msm8996_usb3phy_reset_l, ++ .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), ++ .vreg_list = msm8996_phy_vreg_l, ++ .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), ++ .regs = usb3phy_regs_layout, ++ ++ .start_ctrl = SERDES_START | PCS_START, ++ .pwrdn_ctrl = SW_PWRDN, ++ .mask_pcs_ready = PHYSTATUS, ++}; ++ ++static void qcom_qmp_phy_configure(void __iomem *base, ++ const unsigned int *regs, ++ const struct qmp_phy_init_tbl tbl[], ++ int num) ++{ ++ int i; ++ const struct qmp_phy_init_tbl *t = tbl; ++ ++ if (!t) ++ return; ++ ++ for (i = 0; i < num; i++, t++) { ++ if (t->in_layout) ++ writel(t->val, base + regs[t->offset]); ++ else ++ writel(t->val, base + t->offset); ++ } ++} ++ ++static int qcom_qmp_phy_poweron(struct phy *phy) ++{ ++ struct qmp_phy *qphy = phy_get_drvdata(phy); ++ struct qcom_qmp *qmp = qphy->qmp; ++ int num = qmp->cfg->num_vregs; ++ int ret; ++ ++ dev_vdbg(&phy->dev, "Powering on QMP phy\n"); ++ ++ /* turn on regulator supplies */ ++ ret = regulator_bulk_enable(num, qmp->vregs); ++ if (ret) { ++ dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(qphy->pipe_clk); ++ if (ret) { ++ dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret); ++ regulator_bulk_disable(num, qmp->vregs); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qcom_qmp_phy_poweroff(struct phy *phy) ++{ ++ struct qmp_phy *qphy = phy_get_drvdata(phy); ++ struct qcom_qmp *qmp = qphy->qmp; ++ ++ clk_disable_unprepare(qphy->pipe_clk); ++ ++ regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs); ++ ++ return 0; ++} ++ ++static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp) ++{ ++ const struct qmp_phy_cfg *cfg = qmp->cfg; ++ void __iomem *serdes = qmp->serdes; ++ int ret, i; ++ ++ mutex_lock(&qmp->phy_mutex); ++ if (qmp->init_count++) { ++ mutex_unlock(&qmp->phy_mutex); ++ return 0; ++ } ++ ++ for (i = 0; i < cfg->num_resets; i++) { ++ ret = reset_control_deassert(qmp->resets[i]); ++ if (ret) { ++ dev_err(qmp->dev, "%s reset deassert failed\n", ++ qmp->cfg->reset_list[i]); ++ while (--i >= 0) ++ reset_control_assert(qmp->resets[i]); ++ goto err_rst; ++ } ++ } ++ ++ if (cfg->has_phy_com_ctrl) ++ qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], ++ SW_PWRDN); ++ ++ /* Serdes configuration */ ++ qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl, ++ cfg->serdes_tbl_num); ++ ++ if (cfg->has_phy_com_ctrl) { ++ void __iomem *status; ++ unsigned int mask, val; ++ ++ qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); ++ qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], ++ SERDES_START | PCS_START); ++ ++ status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS]; ++ mask = cfg->mask_com_pcs_ready; ++ ++ ret = readl_poll_timeout(status, val, (val & mask), 10, ++ PHY_INIT_COMPLETE_TIMEOUT); ++ if (ret) { ++ dev_err(qmp->dev, ++ "phy common block init timed-out\n"); ++ goto err_com_init; ++ } ++ } ++ ++ mutex_unlock(&qmp->phy_mutex); ++ ++ return 0; ++ ++err_com_init: ++ while (--i >= 0) ++ reset_control_assert(qmp->resets[i]); ++err_rst: ++ mutex_unlock(&qmp->phy_mutex); ++ return ret; ++} ++ ++static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp) ++{ ++ const struct qmp_phy_cfg *cfg = qmp->cfg; ++ void __iomem *serdes = qmp->serdes; ++ int i = cfg->num_resets; ++ ++ mutex_lock(&qmp->phy_mutex); ++ if (--qmp->init_count) { ++ mutex_unlock(&qmp->phy_mutex); ++ return 0; ++ } ++ ++ if (cfg->has_phy_com_ctrl) { ++ qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], ++ SERDES_START | PCS_START); ++ qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], ++ SW_RESET); ++ qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], ++ SW_PWRDN); ++ } ++ ++ while (--i >= 0) ++ reset_control_assert(qmp->resets[i]); ++ ++ mutex_unlock(&qmp->phy_mutex); ++ ++ return 0; ++} ++ ++/* PHY Initialization */ ++static int qcom_qmp_phy_init(struct phy *phy) ++{ ++ struct qmp_phy *qphy = phy_get_drvdata(phy); ++ struct qcom_qmp *qmp = qphy->qmp; ++ const struct qmp_phy_cfg *cfg = qmp->cfg; ++ void __iomem *tx = qphy->tx; ++ void __iomem *rx = qphy->rx; ++ void __iomem *pcs = qphy->pcs; ++ void __iomem *status; ++ unsigned int mask, val; ++ int ret, i; ++ ++ dev_vdbg(qmp->dev, "Initializing QMP phy\n"); ++ ++ for (i = 0; i < qmp->cfg->num_clks; i++) { ++ ret = clk_prepare_enable(qmp->clks[i]); ++ if (ret) { ++ dev_err(qmp->dev, "failed to enable %s clk, err=%d\n", ++ qmp->cfg->clk_list[i], ret); ++ while (--i >= 0) ++ clk_disable_unprepare(qmp->clks[i]); ++ } ++ } ++ ++ ret = qcom_qmp_phy_com_init(qmp); ++ if (ret) ++ goto err_com_init; ++ ++ if (cfg->has_lane_rst) { ++ ret = reset_control_deassert(qphy->lane_rst); ++ if (ret) { ++ dev_err(qmp->dev, "lane%d reset deassert failed\n", ++ qphy->index); ++ goto err_lane_rst; ++ } ++ } ++ ++ /* Tx, Rx, and PCS configurations */ ++ qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); ++ qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); ++ qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); ++ ++ /* ++ * Pull out PHY from POWER DOWN state. ++ * This is active low enable signal to power-down PHY. ++ */ ++ qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); ++ ++ if (cfg->has_pwrdn_delay) ++ usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); ++ ++ /* start SerDes and Phy-Coding-Sublayer */ ++ qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); ++ ++ /* Pull PHY out of reset state */ ++ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); ++ ++ status = pcs + cfg->regs[QPHY_PCS_READY_STATUS]; ++ mask = cfg->mask_pcs_ready; ++ ++ ret = readl_poll_timeout(status, val, !(val & mask), 1, ++ PHY_INIT_COMPLETE_TIMEOUT); ++ if (ret) { ++ dev_err(qmp->dev, "phy initialization timed-out\n"); ++ goto err_pcs_ready; ++ } ++ ++ return ret; ++ ++err_pcs_ready: ++ if (cfg->has_lane_rst) ++ reset_control_assert(qphy->lane_rst); ++err_lane_rst: ++ qcom_qmp_phy_com_exit(qmp); ++err_com_init: ++ while (--i >= 0) ++ clk_disable_unprepare(qmp->clks[i]); ++ ++ return ret; ++} ++ ++static int qcom_qmp_phy_exit(struct phy *phy) ++{ ++ struct qmp_phy *qphy = phy_get_drvdata(phy); ++ struct qcom_qmp *qmp = qphy->qmp; ++ const struct qmp_phy_cfg *cfg = qmp->cfg; ++ int i = cfg->num_clks; ++ ++ /* PHY reset */ ++ qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); ++ ++ /* stop SerDes and Phy-Coding-Sublayer */ ++ qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); ++ ++ /* Put PHY into POWER DOWN state: active low */ ++ qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); ++ ++ if (cfg->has_lane_rst) ++ reset_control_assert(qphy->lane_rst); ++ ++ qcom_qmp_phy_com_exit(qmp); ++ ++ while (--i >= 0) ++ clk_disable_unprepare(qmp->clks[i]); ++ ++ return 0; ++} ++ ++static int qcom_qmp_phy_vreg_init(struct device *dev) ++{ ++ struct qcom_qmp *qmp = dev_get_drvdata(dev); ++ int num = qmp->cfg->num_vregs; ++ int i; ++ ++ qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); ++ if (!qmp->vregs) ++ return -ENOMEM; ++ ++ for (i = 0; i < num; i++) ++ qmp->vregs[i].supply = qmp->cfg->vreg_list[i]; ++ ++ return devm_regulator_bulk_get(dev, num, qmp->vregs); ++} ++ ++static int qcom_qmp_phy_reset_init(struct device *dev) ++{ ++ struct qcom_qmp *qmp = dev_get_drvdata(dev); ++ int i; ++ ++ qmp->resets = devm_kcalloc(dev, qmp->cfg->num_resets, ++ sizeof(*qmp->resets), GFP_KERNEL); ++ if (!qmp->resets) ++ return -ENOMEM; ++ ++ for (i = 0; i < qmp->cfg->num_resets; i++) { ++ struct reset_control *rst; ++ const char *name = qmp->cfg->reset_list[i]; ++ ++ rst = devm_reset_control_get(dev, name); ++ if (IS_ERR(rst)) { ++ dev_err(dev, "failed to get %s reset\n", name); ++ return PTR_ERR(rst); ++ } ++ qmp->resets[i] = rst; ++ } ++ ++ return 0; ++} ++ ++static int qcom_qmp_phy_clk_init(struct device *dev) ++{ ++ struct qcom_qmp *qmp = dev_get_drvdata(dev); ++ int ret, i; ++ ++ qmp->clks = devm_kcalloc(dev, qmp->cfg->num_clks, ++ sizeof(*qmp->clks), GFP_KERNEL); ++ if (!qmp->clks) ++ return -ENOMEM; ++ ++ for (i = 0; i < qmp->cfg->num_clks; i++) { ++ struct clk *_clk; ++ const char *name = qmp->cfg->clk_list[i]; ++ ++ _clk = devm_clk_get(dev, name); ++ if (IS_ERR(_clk)) { ++ ret = PTR_ERR(_clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to get %s clk, %d\n", ++ name, ret); ++ return ret; ++ } ++ qmp->clks[i] = _clk; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Register a fixed rate pipe clock. ++ * ++ * The _pipe_clksrc generated by PHY goes to the GCC that gate ++ * controls it. The _pipe_clk coming out of the GCC is requested ++ * by the PHY driver for its operations. ++ * We register the _pipe_clksrc here. The gcc driver takes care ++ * of assigning this _pipe_clksrc as parent to _pipe_clk. ++ * Below picture shows this relationship. ++ * ++ * +---------------+ ++ * | PHY block |<<---------------------------------------+ ++ * | | | ++ * | +-------+ | +-----+ | ++ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ ++ * clk | +-------+ | +-----+ ++ * +---------------+ ++ */ ++static int phy_pipe_clk_register(struct qcom_qmp *qmp, int id) ++{ ++ char name[24]; ++ struct clk_fixed_rate *fixed; ++ struct clk_init_data init = { }; ++ ++ switch (qmp->cfg->type) { ++ case PHY_TYPE_USB3: ++ snprintf(name, sizeof(name), "usb3_phy_pipe_clk_src"); ++ break; ++ case PHY_TYPE_PCIE: ++ snprintf(name, sizeof(name), "pcie_%d_pipe_clk_src", id); ++ break; ++ default: ++ /* not all phys register pipe clocks, so return success */ ++ return 0; ++ } ++ ++ fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); ++ if (!fixed) ++ return -ENOMEM; ++ ++ init.name = name; ++ init.ops = &clk_fixed_rate_ops; ++ ++ /* controllers using QMP phys use 125MHz pipe clock interface */ ++ fixed->fixed_rate = 125000000; ++ fixed->hw.init = &init; ++ ++ return devm_clk_hw_register(qmp->dev, &fixed->hw); ++} ++ ++static const struct phy_ops qcom_qmp_phy_gen_ops = { ++ .init = qcom_qmp_phy_init, ++ .exit = qcom_qmp_phy_exit, ++ .power_on = qcom_qmp_phy_poweron, ++ .power_off = qcom_qmp_phy_poweroff, ++ .owner = THIS_MODULE, ++}; ++ ++static ++int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) ++{ ++ struct qcom_qmp *qmp = dev_get_drvdata(dev); ++ struct phy *generic_phy; ++ struct qmp_phy *qphy; ++ char prop_name[MAX_PROP_NAME]; ++ int ret; ++ ++ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); ++ if (!qphy) ++ return -ENOMEM; ++ ++ /* ++ * Get memory resources for each phy lane: ++ * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. ++ */ ++ qphy->tx = of_iomap(np, 0); ++ if (!qphy->tx) ++ return -ENOMEM; ++ ++ qphy->rx = of_iomap(np, 1); ++ if (!qphy->rx) ++ return -ENOMEM; ++ ++ qphy->pcs = of_iomap(np, 2); ++ if (!qphy->pcs) ++ return -ENOMEM; ++ ++ /* ++ * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3 ++ * based phys, so they essentially have pipe clock. So, ++ * we return error in case phy is USB3 or PIPE type. ++ * Otherwise, we initialize pipe clock to NULL for ++ * all phys that don't need this. ++ */ ++ snprintf(prop_name, sizeof(prop_name), "pipe%d", id); ++ qphy->pipe_clk = of_clk_get_by_name(np, prop_name); ++ if (IS_ERR(qphy->pipe_clk)) { ++ if (qmp->cfg->type == PHY_TYPE_PCIE || ++ qmp->cfg->type == PHY_TYPE_USB3) { ++ ret = PTR_ERR(qphy->pipe_clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, ++ "failed to get lane%d pipe_clk, %d\n", ++ id, ret); ++ return ret; ++ } ++ qphy->pipe_clk = NULL; ++ } ++ ++ /* Get lane reset, if any */ ++ if (qmp->cfg->has_lane_rst) { ++ snprintf(prop_name, sizeof(prop_name), "lane%d", id); ++ qphy->lane_rst = of_reset_control_get(np, prop_name); ++ if (IS_ERR(qphy->lane_rst)) { ++ dev_err(dev, "failed to get lane%d reset\n", id); ++ return PTR_ERR(qphy->lane_rst); ++ } ++ } ++ ++ generic_phy = devm_phy_create(dev, np, &qcom_qmp_phy_gen_ops); ++ if (IS_ERR(generic_phy)) { ++ ret = PTR_ERR(generic_phy); ++ dev_err(dev, "failed to create qphy %d\n", ret); ++ return ret; ++ } ++ ++ qphy->phy = generic_phy; ++ qphy->index = id; ++ qphy->qmp = qmp; ++ qmp->phys[id] = qphy; ++ phy_set_drvdata(generic_phy, qphy); ++ ++ return 0; ++} ++ ++static const struct of_device_id qcom_qmp_phy_of_match_table[] = { ++ { ++ .compatible = "qcom,msm8996-qmp-pcie-phy", ++ .data = &msm8996_pciephy_cfg, ++ }, { ++ .compatible = "qcom,msm8996-qmp-usb3-phy", ++ .data = &msm8996_usb3phy_cfg, ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table); ++ ++static int qcom_qmp_phy_probe(struct platform_device *pdev) ++{ ++ struct qcom_qmp *qmp; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ struct device_node *child; ++ struct phy_provider *phy_provider; ++ void __iomem *base; ++ int num, id; ++ int ret; ++ ++ qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); ++ if (!qmp) ++ return -ENOMEM; ++ ++ qmp->dev = dev; ++ dev_set_drvdata(dev, qmp); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ /* per PHY serdes; usually located at base address */ ++ qmp->serdes = base; ++ ++ mutex_init(&qmp->phy_mutex); ++ ++ /* Get the specific init parameters of QMP phy */ ++ qmp->cfg = of_device_get_match_data(dev); ++ ++ ret = qcom_qmp_phy_clk_init(dev); ++ if (ret) ++ return ret; ++ ++ ret = qcom_qmp_phy_reset_init(dev); ++ if (ret) ++ return ret; ++ ++ ret = qcom_qmp_phy_vreg_init(dev); ++ if (ret) { ++ dev_err(dev, "failed to get regulator supplies\n"); ++ return ret; ++ } ++ ++ num = of_get_available_child_count(dev->of_node); ++ /* do we have a rogue child node ? */ ++ if (num > qmp->cfg->nlanes) ++ return -EINVAL; ++ ++ qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); ++ if (!qmp->phys) ++ return -ENOMEM; ++ ++ id = 0; ++ for_each_available_child_of_node(dev->of_node, child) { ++ /* Create per-lane phy */ ++ ret = qcom_qmp_phy_create(dev, child, id); ++ if (ret) { ++ dev_err(dev, "failed to create lane%d phy, %d\n", ++ id, ret); ++ return ret; ++ } ++ ++ /* ++ * Register the pipe clock provided by phy. ++ * See function description to see details of this pipe clock. ++ */ ++ ret = phy_pipe_clk_register(qmp, id); ++ if (ret) { ++ dev_err(qmp->dev, ++ "failed to register pipe clock source\n"); ++ return ret; ++ } ++ id++; ++ } ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (!IS_ERR(phy_provider)) ++ dev_info(dev, "Registered Qcom-QMP phy\n"); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static struct platform_driver qcom_qmp_phy_driver = { ++ .probe = qcom_qmp_phy_probe, ++ .driver = { ++ .name = "qcom-qmp-phy", ++ .of_match_table = qcom_qmp_phy_of_match_table, ++ }, ++}; ++ ++module_platform_driver(qcom_qmp_phy_driver); ++ ++MODULE_AUTHOR("Vivek Gautam "); ++MODULE_DESCRIPTION("Qualcomm QMP PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c +new file mode 100644 +index 000000000000..6c575244c0fb +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c +@@ -0,0 +1,493 @@ ++/* ++ * Copyright (c) 2017, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define QUSB2PHY_PLL_TEST 0x04 ++#define CLK_REF_SEL BIT(7) ++ ++#define QUSB2PHY_PLL_TUNE 0x08 ++#define QUSB2PHY_PLL_USER_CTL1 0x0c ++#define QUSB2PHY_PLL_USER_CTL2 0x10 ++#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c ++#define QUSB2PHY_PLL_PWR_CTRL 0x18 ++ ++#define QUSB2PHY_PLL_STATUS 0x38 ++#define PLL_LOCKED BIT(5) ++ ++#define QUSB2PHY_PORT_TUNE1 0x80 ++#define QUSB2PHY_PORT_TUNE2 0x84 ++#define QUSB2PHY_PORT_TUNE3 0x88 ++#define QUSB2PHY_PORT_TUNE4 0x8c ++#define QUSB2PHY_PORT_TUNE5 0x90 ++#define QUSB2PHY_PORT_TEST2 0x9c ++ ++#define QUSB2PHY_PORT_POWERDOWN 0xb4 ++#define CLAMP_N_EN BIT(5) ++#define FREEZIO_N BIT(1) ++#define POWER_DOWN BIT(0) ++ ++#define QUSB2PHY_REFCLK_ENABLE BIT(0) ++ ++#define PHY_CLK_SCHEME_SEL BIT(0) ++ ++struct qusb2_phy_init_tbl { ++ unsigned int offset; ++ unsigned int val; ++}; ++ ++#define QUSB2_PHY_INIT_CFG(o, v) \ ++ { \ ++ .offset = o, \ ++ .val = v, \ ++ } ++ ++static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), ++ QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), ++}; ++ ++struct qusb2_phy_cfg { ++ const struct qusb2_phy_init_tbl *tbl; ++ /* number of entries in the table */ ++ unsigned int tbl_num; ++ /* offset to PHY_CLK_SCHEME register in TCSR map */ ++ unsigned int clk_scheme_offset; ++}; ++ ++static const struct qusb2_phy_cfg msm8996_phy_cfg = { ++ .tbl = msm8996_init_tbl, ++ .tbl_num = ARRAY_SIZE(msm8996_init_tbl), ++}; ++ ++static const char * const qusb2_phy_vreg_names[] = { ++ "vdda-pll", "vdda-phy-dpdm", ++}; ++ ++#define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) ++ ++/** ++ * struct qusb2_phy - structure holding qusb2 phy attributes ++ * ++ * @phy: generic phy ++ * @base: iomapped memory space for qubs2 phy ++ * ++ * @cfg_ahb_clk: AHB2PHY interface clock ++ * @ref_clk: phy reference clock ++ * @iface_clk: phy interface clock ++ * @phy_reset: phy reset control ++ * @vregs: regulator supplies bulk data ++ * ++ * @tcsr: TCSR syscon register map ++ * @cell: nvmem cell containing phy tuning value ++ * ++ * @cfg: phy config data ++ * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme ++ */ ++struct qusb2_phy { ++ struct phy *phy; ++ void __iomem *base; ++ ++ struct clk *cfg_ahb_clk; ++ struct clk *ref_clk; ++ struct clk *iface_clk; ++ struct reset_control *phy_reset; ++ struct regulator_bulk_data vregs[QUSB2_NUM_VREGS]; ++ ++ struct regmap *tcsr; ++ struct nvmem_cell *cell; ++ ++ const struct qusb2_phy_cfg *cfg; ++ bool has_se_clk_scheme; ++}; ++ ++static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val) ++{ ++ u32 reg; ++ ++ reg = readl(base + offset); ++ reg |= val; ++ writel(reg, base + offset); ++ ++ /* Ensure above write is completed */ ++ readl(base + offset); ++} ++ ++static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val) ++{ ++ u32 reg; ++ ++ reg = readl(base + offset); ++ reg &= ~val; ++ writel(reg, base + offset); ++ ++ /* Ensure above write is completed */ ++ readl(base + offset); ++} ++ ++static inline ++void qcom_qusb2_phy_configure(void __iomem *base, ++ const struct qusb2_phy_init_tbl tbl[], int num) ++{ ++ int i; ++ ++ for (i = 0; i < num; i++) ++ writel(tbl[i].val, base + tbl[i].offset); ++} ++ ++/* ++ * Fetches HS Tx tuning value from nvmem and sets the ++ * QUSB2PHY_PORT_TUNE2 register. ++ * For error case, skip setting the value and use the default value. ++ */ ++static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) ++{ ++ struct device *dev = &qphy->phy->dev; ++ u8 *val; ++ ++ /* ++ * Read efuse register having TUNE2 parameter's high nibble. ++ * If efuse register shows value as 0x0, or if we fail to find ++ * a valid efuse register settings, then use default value ++ * as 0xB for high nibble that we have already set while ++ * configuring phy. ++ */ ++ val = nvmem_cell_read(qphy->cell, NULL); ++ if (IS_ERR(val) || !val[0]) { ++ dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); ++ return; ++ } ++ ++ /* Fused TUNE2 value is the higher nibble only */ ++ qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4); ++} ++ ++static int qusb2_phy_poweron(struct phy *phy) ++{ ++ struct qusb2_phy *qphy = phy_get_drvdata(phy); ++ int num = ARRAY_SIZE(qphy->vregs); ++ int ret; ++ ++ dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__); ++ ++ /* turn on regulator supplies */ ++ ret = regulator_bulk_enable(num, qphy->vregs); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(qphy->iface_clk); ++ if (ret) { ++ dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret); ++ regulator_bulk_disable(num, qphy->vregs); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int qusb2_phy_poweroff(struct phy *phy) ++{ ++ struct qusb2_phy *qphy = phy_get_drvdata(phy); ++ ++ clk_disable_unprepare(qphy->iface_clk); ++ ++ regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs); ++ ++ return 0; ++} ++ ++static int qusb2_phy_init(struct phy *phy) ++{ ++ struct qusb2_phy *qphy = phy_get_drvdata(phy); ++ unsigned int val; ++ unsigned int clk_scheme; ++ int ret; ++ ++ dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__); ++ ++ /* enable ahb interface clock to program phy */ ++ ret = clk_prepare_enable(qphy->cfg_ahb_clk); ++ if (ret) { ++ dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); ++ return ret; ++ } ++ ++ /* Perform phy reset */ ++ ret = reset_control_assert(qphy->phy_reset); ++ if (ret) { ++ dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret); ++ goto disable_ahb_clk; ++ } ++ ++ /* 100 us delay to keep PHY in reset mode */ ++ usleep_range(100, 150); ++ ++ ret = reset_control_deassert(qphy->phy_reset); ++ if (ret) { ++ dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret); ++ goto disable_ahb_clk; ++ } ++ ++ /* Disable the PHY */ ++ qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, ++ CLAMP_N_EN | FREEZIO_N | POWER_DOWN); ++ ++ /* save reset value to override reference clock scheme later */ ++ val = readl(qphy->base + QUSB2PHY_PLL_TEST); ++ ++ qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl, ++ qphy->cfg->tbl_num); ++ ++ /* Set efuse value for tuning the PHY */ ++ qusb2_phy_set_tune2_param(qphy); ++ ++ /* Enable the PHY */ ++ qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN); ++ ++ /* Required to get phy pll lock successfully */ ++ usleep_range(150, 160); ++ ++ /* Default is single-ended clock on msm8996 */ ++ qphy->has_se_clk_scheme = true; ++ /* ++ * read TCSR_PHY_CLK_SCHEME register to check if single-ended ++ * clock scheme is selected. If yes, then disable differential ++ * ref_clk and use single-ended clock, otherwise use differential ++ * ref_clk only. ++ */ ++ if (qphy->tcsr) { ++ ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset, ++ &clk_scheme); ++ if (ret) { ++ dev_err(&phy->dev, "failed to read clk scheme reg\n"); ++ goto assert_phy_reset; ++ } ++ ++ /* is it a differential clock scheme ? */ ++ if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) { ++ dev_vdbg(&phy->dev, "%s(): select differential clk\n", ++ __func__); ++ qphy->has_se_clk_scheme = false; ++ } else { ++ dev_vdbg(&phy->dev, "%s(): select single-ended clk\n", ++ __func__); ++ } ++ } ++ ++ if (!qphy->has_se_clk_scheme) { ++ val &= ~CLK_REF_SEL; ++ ret = clk_prepare_enable(qphy->ref_clk); ++ if (ret) { ++ dev_err(&phy->dev, "failed to enable ref clk, %d\n", ++ ret); ++ goto assert_phy_reset; ++ } ++ } else { ++ val |= CLK_REF_SEL; ++ } ++ ++ writel(val, qphy->base + QUSB2PHY_PLL_TEST); ++ ++ /* ensure above write is through */ ++ readl(qphy->base + QUSB2PHY_PLL_TEST); ++ ++ /* Required to get phy pll lock successfully */ ++ usleep_range(100, 110); ++ ++ val = readb(qphy->base + QUSB2PHY_PLL_STATUS); ++ if (!(val & PLL_LOCKED)) { ++ dev_err(&phy->dev, ++ "QUSB2PHY pll lock failed: status reg = %x\n", val); ++ ret = -EBUSY; ++ goto disable_ref_clk; ++ } ++ ++ return 0; ++ ++disable_ref_clk: ++ if (!qphy->has_se_clk_scheme) ++ clk_disable_unprepare(qphy->ref_clk); ++assert_phy_reset: ++ reset_control_assert(qphy->phy_reset); ++disable_ahb_clk: ++ clk_disable_unprepare(qphy->cfg_ahb_clk); ++ return ret; ++} ++ ++static int qusb2_phy_exit(struct phy *phy) ++{ ++ struct qusb2_phy *qphy = phy_get_drvdata(phy); ++ ++ /* Disable the PHY */ ++ qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, ++ CLAMP_N_EN | FREEZIO_N | POWER_DOWN); ++ ++ if (!qphy->has_se_clk_scheme) ++ clk_disable_unprepare(qphy->ref_clk); ++ ++ reset_control_assert(qphy->phy_reset); ++ ++ clk_disable_unprepare(qphy->cfg_ahb_clk); ++ ++ return 0; ++} ++ ++static const struct phy_ops qusb2_phy_gen_ops = { ++ .init = qusb2_phy_init, ++ .exit = qusb2_phy_exit, ++ .power_on = qusb2_phy_poweron, ++ .power_off = qusb2_phy_poweroff, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct of_device_id qusb2_phy_of_match_table[] = { ++ { ++ .compatible = "qcom,msm8996-qusb2-phy", ++ .data = &msm8996_phy_cfg, ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table); ++ ++static int qusb2_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct qusb2_phy *qphy; ++ struct phy_provider *phy_provider; ++ struct phy *generic_phy; ++ struct resource *res; ++ int ret, i; ++ int num; ++ ++ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); ++ if (!qphy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ qphy->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(qphy->base)) ++ return PTR_ERR(qphy->base); ++ ++ qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb"); ++ if (IS_ERR(qphy->cfg_ahb_clk)) { ++ ret = PTR_ERR(qphy->cfg_ahb_clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to get cfg ahb clk, %d\n", ret); ++ return ret; ++ } ++ ++ qphy->ref_clk = devm_clk_get(dev, "ref"); ++ if (IS_ERR(qphy->ref_clk)) { ++ ret = PTR_ERR(qphy->ref_clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to get ref clk, %d\n", ret); ++ return ret; ++ } ++ ++ qphy->iface_clk = devm_clk_get(dev, "iface"); ++ if (IS_ERR(qphy->iface_clk)) { ++ ret = PTR_ERR(qphy->iface_clk); ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ qphy->iface_clk = NULL; ++ dev_dbg(dev, "failed to get iface clk, %d\n", ret); ++ } ++ ++ qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0); ++ if (IS_ERR(qphy->phy_reset)) { ++ dev_err(dev, "failed to get phy core reset\n"); ++ return PTR_ERR(qphy->phy_reset); ++ } ++ ++ num = ARRAY_SIZE(qphy->vregs); ++ for (i = 0; i < num; i++) ++ qphy->vregs[i].supply = qusb2_phy_vreg_names[i]; ++ ++ ret = devm_regulator_bulk_get(dev, num, qphy->vregs); ++ if (ret) { ++ dev_err(dev, "failed to get regulator supplies\n"); ++ return ret; ++ } ++ ++ /* Get the specific init parameters of QMP phy */ ++ qphy->cfg = of_device_get_match_data(dev); ++ ++ qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "qcom,tcsr-syscon"); ++ if (IS_ERR(qphy->tcsr)) { ++ dev_dbg(dev, "failed to lookup TCSR regmap\n"); ++ qphy->tcsr = NULL; ++ } ++ ++ qphy->cell = devm_nvmem_cell_get(dev, NULL); ++ if (IS_ERR(qphy->cell)) { ++ if (PTR_ERR(qphy->cell) == -EPROBE_DEFER) ++ return -EPROBE_DEFER; ++ qphy->cell = NULL; ++ dev_dbg(dev, "failed to lookup tune2 hstx trim value\n"); ++ } ++ ++ generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops); ++ if (IS_ERR(generic_phy)) { ++ ret = PTR_ERR(generic_phy); ++ dev_err(dev, "failed to create phy, %d\n", ret); ++ return ret; ++ } ++ qphy->phy = generic_phy; ++ ++ dev_set_drvdata(dev, qphy); ++ phy_set_drvdata(generic_phy, qphy); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (!IS_ERR(phy_provider)) ++ dev_info(dev, "Registered Qcom-QUSB2 phy\n"); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static struct platform_driver qusb2_phy_driver = { ++ .probe = qusb2_phy_probe, ++ .driver = { ++ .name = "qcom-qusb2-phy", ++ .of_match_table = qusb2_phy_of_match_table, ++ }, ++}; ++ ++module_platform_driver(qusb2_phy_driver); ++ ++MODULE_AUTHOR("Vivek Gautam "); ++MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h +new file mode 100644 +index 000000000000..13b02b7de30b +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h +@@ -0,0 +1,155 @@ ++/* ++ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef UFS_QCOM_PHY_I_H_ ++#define UFS_QCOM_PHY_I_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ ++({ \ ++ ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \ ++ might_sleep_if(timeout_us); \ ++ for (;;) { \ ++ (val) = readl(addr); \ ++ if (cond) \ ++ break; \ ++ if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \ ++ (val) = readl(addr); \ ++ break; \ ++ } \ ++ if (sleep_us) \ ++ usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \ ++ } \ ++ (cond) ? 0 : -ETIMEDOUT; \ ++}) ++ ++#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ ++ { \ ++ .reg_offset = reg, \ ++ .cfg_value = val, \ ++ } ++ ++#define UFS_QCOM_PHY_NAME_LEN 30 ++ ++enum { ++ MASK_SERDES_START = 0x1, ++ MASK_PCS_READY = 0x1, ++}; ++ ++enum { ++ OFFSET_SERDES_START = 0x0, ++}; ++ ++struct ufs_qcom_phy_stored_attributes { ++ u32 att; ++ u32 value; ++}; ++ ++ ++struct ufs_qcom_phy_calibration { ++ u32 reg_offset; ++ u32 cfg_value; ++}; ++ ++struct ufs_qcom_phy_vreg { ++ const char *name; ++ struct regulator *reg; ++ int max_uA; ++ int min_uV; ++ int max_uV; ++ bool enabled; ++}; ++ ++struct ufs_qcom_phy { ++ struct list_head list; ++ struct device *dev; ++ void __iomem *mmio; ++ void __iomem *dev_ref_clk_ctrl_mmio; ++ struct clk *tx_iface_clk; ++ struct clk *rx_iface_clk; ++ bool is_iface_clk_enabled; ++ struct clk *ref_clk_src; ++ struct clk *ref_clk_parent; ++ struct clk *ref_clk; ++ bool is_ref_clk_enabled; ++ bool is_dev_ref_clk_enabled; ++ struct ufs_qcom_phy_vreg vdda_pll; ++ struct ufs_qcom_phy_vreg vdda_phy; ++ struct ufs_qcom_phy_vreg vddp_ref_clk; ++ unsigned int quirks; ++ ++ /* ++ * If UFS link is put into Hibern8 and if UFS PHY analog hardware is ++ * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8 ++ * exit might fail even after powering on UFS PHY analog hardware. ++ * Enabling this quirk will help to solve above issue by doing ++ * custom PHY settings just before PHY analog power collapse. ++ */ ++ #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0) ++ ++ u8 host_ctrl_rev_major; ++ u16 host_ctrl_rev_minor; ++ u16 host_ctrl_rev_step; ++ ++ char name[UFS_QCOM_PHY_NAME_LEN]; ++ struct ufs_qcom_phy_calibration *cached_regs; ++ int cached_regs_table_size; ++ bool is_powered_on; ++ struct ufs_qcom_phy_specific_ops *phy_spec_ops; ++}; ++ ++/** ++ * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a ++ * specific implementation per phy. Each UFS phy, should implement ++ * those functions according to its spec and requirements ++ * @calibrate_phy: pointer to a function that calibrate the phy ++ * @start_serdes: pointer to a function that starts the serdes ++ * @is_physical_coding_sublayer_ready: pointer to a function that ++ * checks pcs readiness. returns 0 for success and non-zero for error. ++ * @set_tx_lane_enable: pointer to a function that enable tx lanes ++ * @power_control: pointer to a function that controls analog rail of phy ++ * and writes to QSERDES_RX_SIGDET_CNTRL attribute ++ */ ++struct ufs_qcom_phy_specific_ops { ++ int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B); ++ void (*start_serdes)(struct ufs_qcom_phy *phy); ++ int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); ++ void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); ++ void (*power_control)(struct ufs_qcom_phy *phy, bool val); ++}; ++ ++struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy); ++int ufs_qcom_phy_power_on(struct phy *generic_phy); ++int ufs_qcom_phy_power_off(struct phy *generic_phy); ++int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common); ++int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common); ++int ufs_qcom_phy_remove(struct phy *generic_phy, ++ struct ufs_qcom_phy *ufs_qcom_phy); ++struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, ++ struct ufs_qcom_phy *common_cfg, ++ const struct phy_ops *ufs_qcom_phy_gen_ops, ++ struct ufs_qcom_phy_specific_ops *phy_spec_ops); ++int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, ++ struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, ++ struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, ++ bool is_rate_B); ++#endif +diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +new file mode 100644 +index 000000000000..12a1b498dc4b +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +@@ -0,0 +1,178 @@ ++/* ++ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include "phy-qcom-ufs-qmp-14nm.h" ++ ++#define UFS_PHY_NAME "ufs_phy_qmp_14nm" ++#define UFS_PHY_VDDA_PHY_UV (925000) ++ ++static ++int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, ++ bool is_rate_B) ++{ ++ int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); ++ int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); ++ int err; ++ ++ err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, ++ tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); ++ ++ if (err) ++ dev_err(ufs_qcom_phy->dev, ++ "%s: ufs_qcom_phy_calibrate() failed %d\n", ++ __func__, err); ++ return err; ++} ++ ++static ++void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) ++{ ++ phy_common->quirks = ++ UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; ++} ++ ++static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) ++{ ++ return 0; ++} ++ ++static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) ++{ ++ return 0; ++} ++ ++static ++void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) ++{ ++ writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); ++ /* ++ * Before any transactions involving PHY, ensure PHY knows ++ * that it's analog rail is powered ON (or OFF). ++ */ ++ mb(); ++} ++ ++static inline ++void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) ++{ ++ /* ++ * 14nm PHY does not have TX_LANE_ENABLE register. ++ * Implement this function so as not to propagate error to caller. ++ */ ++} ++ ++static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) ++{ ++ u32 tmp; ++ ++ tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); ++ tmp &= ~MASK_SERDES_START; ++ tmp |= (1 << OFFSET_SERDES_START); ++ writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); ++ /* Ensure register value is committed */ ++ mb(); ++} ++ ++static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) ++{ ++ int err = 0; ++ u32 val; ++ ++ err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, ++ val, (val & MASK_PCS_READY), 10, 1000000); ++ if (err) ++ dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", ++ __func__, err); ++ return err; ++} ++ ++static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { ++ .init = ufs_qcom_phy_qmp_14nm_init, ++ .exit = ufs_qcom_phy_qmp_14nm_exit, ++ .power_on = ufs_qcom_phy_power_on, ++ .power_off = ufs_qcom_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { ++ .calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate, ++ .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, ++ .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, ++ .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, ++ .power_control = ufs_qcom_phy_qmp_14nm_power_control, ++}; ++ ++static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *generic_phy; ++ struct ufs_qcom_phy_qmp_14nm *phy; ++ struct ufs_qcom_phy *phy_common; ++ int err = 0; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) { ++ err = -ENOMEM; ++ goto out; ++ } ++ phy_common = &phy->common_cfg; ++ ++ generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, ++ &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops); ++ ++ if (!generic_phy) { ++ err = -EIO; ++ goto out; ++ } ++ ++ err = ufs_qcom_phy_init_clks(phy_common); ++ if (err) ++ goto out; ++ ++ err = ufs_qcom_phy_init_vregulators(phy_common); ++ if (err) ++ goto out; ++ ++ phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; ++ phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; ++ ++ ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); ++ ++ phy_set_drvdata(generic_phy, phy); ++ ++ strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); ++ ++out: ++ return err; ++} ++ ++static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = { ++ {.compatible = "qcom,ufs-phy-qmp-14nm"}, ++ {.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match); ++ ++static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = { ++ .probe = ufs_qcom_phy_qmp_14nm_probe, ++ .driver = { ++ .of_match_table = ufs_qcom_phy_qmp_14nm_of_match, ++ .name = "ufs_qcom_phy_qmp_14nm", ++ }, ++}; ++ ++module_platform_driver(ufs_qcom_phy_qmp_14nm_driver); ++ ++MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h +new file mode 100644 +index 000000000000..3aefdbacbcd0 +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h +@@ -0,0 +1,177 @@ ++/* ++ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef UFS_QCOM_PHY_QMP_14NM_H_ ++#define UFS_QCOM_PHY_QMP_14NM_H_ ++ ++#include "phy-qcom-ufs-i.h" ++ ++/* QCOM UFS PHY control registers */ ++#define COM_OFF(x) (0x000 + x) ++#define PHY_OFF(x) (0xC00 + x) ++#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) ++#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) ++ ++/* UFS PHY QSERDES COM registers */ ++#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) ++#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) ++#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) ++#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C) ++#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50) ++#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54) ++#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58) ++#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C) ++#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60) ++#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) ++#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) ++#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) ++#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88) ++#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90) ++#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) ++#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) ++#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) ++#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) ++#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) ++#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) ++#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4) ++#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC) ++#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0) ++#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4) ++#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8) ++#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC) ++#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0) ++#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108) ++#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C) ++#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110) ++#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114) ++#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124) ++#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128) ++#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C) ++#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) ++#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) ++#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) ++#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) ++#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) ++#define QSERDES_COM_CLK_SELECT COM_OFF(0x174) ++#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) ++#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) ++#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) ++#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) ++#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) ++#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) ++ ++/* UFS PHY registers */ ++#define UFS_PHY_PHY_START PHY_OFF(0x00) ++#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) ++#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) ++ ++/* UFS PHY TX registers */ ++#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68) ++#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94) ++ ++/* UFS PHY RX registers */ ++#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40) ++#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90) ++#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4) ++#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8) ++#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC) ++#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0) ++#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8) ++#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114) ++#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118) ++#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C) ++#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C) ++ ++/* ++ * This structure represents the 14nm specific phy. ++ * common_cfg MUST remain the first field in this structure ++ * in case extra fields are added. This way, when calling ++ * get_ufs_qcom_phy() of generic phy, we can extract the ++ * common phy structure (struct ufs_qcom_phy) out of it ++ * regardless of the relevant specific phy. ++ */ ++struct ufs_qcom_phy_qmp_14nm { ++ struct ufs_qcom_phy common_cfg; ++}; ++ ++static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { ++ UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), ++ ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), ++ ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), ++}; ++ ++static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), ++}; ++ ++#endif +diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +new file mode 100644 +index 000000000000..4f68acb58b73 +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +@@ -0,0 +1,232 @@ ++/* ++ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include "phy-qcom-ufs-qmp-20nm.h" ++ ++#define UFS_PHY_NAME "ufs_phy_qmp_20nm" ++ ++static ++int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, ++ bool is_rate_B) ++{ ++ struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; ++ int tbl_size_A, tbl_size_B; ++ u8 major = ufs_qcom_phy->host_ctrl_rev_major; ++ u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; ++ u16 step = ufs_qcom_phy->host_ctrl_rev_step; ++ int err; ++ ++ if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) { ++ tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0); ++ tbl_A = phy_cal_table_rate_A_1_2_0; ++ } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) { ++ tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); ++ tbl_A = phy_cal_table_rate_A_1_3_0; ++ } else { ++ dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n", ++ __func__); ++ err = -ENODEV; ++ goto out; ++ } ++ ++ tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); ++ tbl_B = phy_cal_table_rate_B; ++ ++ err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, ++ tbl_B, tbl_size_B, is_rate_B); ++ ++ if (err) ++ dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", ++ __func__, err); ++ ++out: ++ return err; ++} ++ ++static ++void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) ++{ ++ phy_common->quirks = ++ UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; ++} ++ ++static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy) ++{ ++ return 0; ++} ++ ++static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) ++{ ++ return 0; ++} ++ ++static ++void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val) ++{ ++ bool hibern8_exit_after_pwr_collapse = phy->quirks & ++ UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; ++ ++ if (val) { ++ writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); ++ /* ++ * Before any transactions involving PHY, ensure PHY knows ++ * that it's analog rail is powered ON. ++ */ ++ mb(); ++ ++ if (hibern8_exit_after_pwr_collapse) { ++ /* ++ * Give atleast 1us delay after restoring PHY analog ++ * power. ++ */ ++ usleep_range(1, 2); ++ writel_relaxed(0x0A, phy->mmio + ++ QSERDES_COM_SYSCLK_EN_SEL_TXBAND); ++ writel_relaxed(0x08, phy->mmio + ++ QSERDES_COM_SYSCLK_EN_SEL_TXBAND); ++ /* ++ * Make sure workaround is deactivated before proceeding ++ * with normal PHY operations. ++ */ ++ mb(); ++ } ++ } else { ++ if (hibern8_exit_after_pwr_collapse) { ++ writel_relaxed(0x0A, phy->mmio + ++ QSERDES_COM_SYSCLK_EN_SEL_TXBAND); ++ writel_relaxed(0x02, phy->mmio + ++ QSERDES_COM_SYSCLK_EN_SEL_TXBAND); ++ /* ++ * Make sure that above workaround is activated before ++ * PHY analog power collapse. ++ */ ++ mb(); ++ } ++ ++ writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); ++ /* ++ * ensure that PHY knows its PHY analog rail is going ++ * to be powered down ++ */ ++ mb(); ++ } ++} ++ ++static ++void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) ++{ ++ writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK, ++ phy->mmio + UFS_PHY_TX_LANE_ENABLE); ++ mb(); ++} ++ ++static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy) ++{ ++ u32 tmp; ++ ++ tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); ++ tmp &= ~MASK_SERDES_START; ++ tmp |= (1 << OFFSET_SERDES_START); ++ writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); ++ mb(); ++} ++ ++static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) ++{ ++ int err = 0; ++ u32 val; ++ ++ err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, ++ val, (val & MASK_PCS_READY), 10, 1000000); ++ if (err) ++ dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", ++ __func__, err); ++ return err; ++} ++ ++static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { ++ .init = ufs_qcom_phy_qmp_20nm_init, ++ .exit = ufs_qcom_phy_qmp_20nm_exit, ++ .power_on = ufs_qcom_phy_power_on, ++ .power_off = ufs_qcom_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { ++ .calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate, ++ .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, ++ .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, ++ .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, ++ .power_control = ufs_qcom_phy_qmp_20nm_power_control, ++}; ++ ++static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct phy *generic_phy; ++ struct ufs_qcom_phy_qmp_20nm *phy; ++ struct ufs_qcom_phy *phy_common; ++ int err = 0; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) { ++ err = -ENOMEM; ++ goto out; ++ } ++ phy_common = &phy->common_cfg; ++ ++ generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, ++ &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops); ++ ++ if (!generic_phy) { ++ err = -EIO; ++ goto out; ++ } ++ ++ err = ufs_qcom_phy_init_clks(phy_common); ++ if (err) ++ goto out; ++ ++ err = ufs_qcom_phy_init_vregulators(phy_common); ++ if (err) ++ goto out; ++ ++ ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common); ++ ++ phy_set_drvdata(generic_phy, phy); ++ ++ strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); ++ ++out: ++ return err; ++} ++ ++static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = { ++ {.compatible = "qcom,ufs-phy-qmp-20nm"}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match); ++ ++static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = { ++ .probe = ufs_qcom_phy_qmp_20nm_probe, ++ .driver = { ++ .of_match_table = ufs_qcom_phy_qmp_20nm_of_match, ++ .name = "ufs_qcom_phy_qmp_20nm", ++ }, ++}; ++ ++module_platform_driver(ufs_qcom_phy_qmp_20nm_driver); ++ ++MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h +new file mode 100644 +index 000000000000..4f3076bb3d71 +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h +@@ -0,0 +1,235 @@ ++/* ++ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#ifndef UFS_QCOM_PHY_QMP_20NM_H_ ++#define UFS_QCOM_PHY_QMP_20NM_H_ ++ ++#include "phy-qcom-ufs-i.h" ++ ++/* QCOM UFS PHY control registers */ ++ ++#define COM_OFF(x) (0x000 + x) ++#define PHY_OFF(x) (0xC00 + x) ++#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) ++#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) ++ ++/* UFS PHY PLL block registers */ ++#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0) ++#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04) ++#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14) ++#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24) ++#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28) ++#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30) ++#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34) ++#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38) ++#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C) ++#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48) ++#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C) ++#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50) ++#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90) ++#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94) ++#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98) ++#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C) ++#define QSERDES_COM_BGTC COM_OFF(0xA0) ++#define QSERDES_COM_DEC_START1 COM_OFF(0xAC) ++#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0) ++#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8) ++#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC) ++#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100) ++#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104) ++#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108) ++#define QSERDES_COM_DEC_START2 COM_OFF(0x10C) ++#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110) ++#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114) ++#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118) ++ ++/* TX LANE n (0, 1) registers */ ++#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08) ++#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C) ++#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54) ++ ++/* RX LANE n (0, 1) registers */ ++#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0) ++#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8) ++#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8) ++#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC) ++#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0) ++#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4) ++#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC) ++#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC) ++#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100) ++ ++/* UFS PHY registers */ ++#define UFS_PHY_PHY_START PHY_OFF(0x00) ++#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4) ++#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44) ++#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08) ++#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C) ++#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10) ++#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14) ++#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34) ++#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38) ++#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C) ++#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40) ++#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68) ++#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28) ++#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C) ++#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48) ++#define UFS_PHY_PLL_CNTL PHY_OFF(0x50) ++#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54) ++#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C) ++#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58) ++#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60) ++#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64) ++#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C) ++#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4) ++#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0) ++#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8) ++#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4) ++#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC) ++#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8) ++#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC) ++#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100) ++#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c) ++#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160) ++#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7) ++#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6) ++#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5) ++#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4) ++#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3) ++#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2) ++#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1) ++#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0) ++#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164) ++#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168) ++#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C) ++#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170) ++#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174) ++ ++#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3 ++ ++/* ++ * This structure represents the 20nm specific phy. ++ * common_cfg MUST remain the first field in this structure ++ * in case extra fields are added. This way, when calling ++ * get_ufs_qcom_phy() of generic phy, we can extract the ++ * common phy structure (struct ufs_qcom_phy) out of it ++ * regardless of the relevant specific phy. ++ */ ++struct ufs_qcom_phy_qmp_20nm { ++ struct ufs_qcom_phy common_cfg; ++}; ++ ++static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = { ++ UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), ++ UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), ++}; ++ ++static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = { ++ UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), ++ UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), ++}; ++ ++static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65), ++ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e), ++}; ++ ++#endif +diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c +new file mode 100644 +index 000000000000..43865ef340e2 +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-ufs.c +@@ -0,0 +1,691 @@ ++/* ++ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include "phy-qcom-ufs-i.h" ++ ++#define MAX_PROP_NAME 32 ++#define VDDA_PHY_MIN_UV 1000000 ++#define VDDA_PHY_MAX_UV 1000000 ++#define VDDA_PLL_MIN_UV 1800000 ++#define VDDA_PLL_MAX_UV 1800000 ++#define VDDP_REF_CLK_MIN_UV 1200000 ++#define VDDP_REF_CLK_MAX_UV 1200000 ++ ++int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, ++ struct ufs_qcom_phy_calibration *tbl_A, ++ int tbl_size_A, ++ struct ufs_qcom_phy_calibration *tbl_B, ++ int tbl_size_B, bool is_rate_B) ++{ ++ int i; ++ int ret = 0; ++ ++ if (!tbl_A) { ++ dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); ++ ret = EINVAL; ++ goto out; ++ } ++ ++ for (i = 0; i < tbl_size_A; i++) ++ writel_relaxed(tbl_A[i].cfg_value, ++ ufs_qcom_phy->mmio + tbl_A[i].reg_offset); ++ ++ /* ++ * In case we would like to work in rate B, we need ++ * to override a registers that were configured in rate A table ++ * with registers of rate B table. ++ * table. ++ */ ++ if (is_rate_B) { ++ if (!tbl_B) { ++ dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", ++ __func__); ++ ret = EINVAL; ++ goto out; ++ } ++ ++ for (i = 0; i < tbl_size_B; i++) ++ writel_relaxed(tbl_B[i].cfg_value, ++ ufs_qcom_phy->mmio + tbl_B[i].reg_offset); ++ } ++ ++ /* flush buffered writes */ ++ mb(); ++ ++out: ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); ++ ++/* ++ * This assumes the embedded phy structure inside generic_phy is of type ++ * struct ufs_qcom_phy. In order to function properly it's crucial ++ * to keep the embedded struct "struct ufs_qcom_phy common_cfg" ++ * as the first inside generic_phy. ++ */ ++struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy) ++{ ++ return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy); ++} ++EXPORT_SYMBOL_GPL(get_ufs_qcom_phy); ++ ++static ++int ufs_qcom_phy_base_init(struct platform_device *pdev, ++ struct ufs_qcom_phy *phy_common) ++{ ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ int err = 0; ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); ++ phy_common->mmio = devm_ioremap_resource(dev, res); ++ if (IS_ERR((void const *)phy_common->mmio)) { ++ err = PTR_ERR((void const *)phy_common->mmio); ++ phy_common->mmio = NULL; ++ dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", ++ __func__, err); ++ return err; ++ } ++ ++ /* "dev_ref_clk_ctrl_mem" is optional resource */ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "dev_ref_clk_ctrl_mem"); ++ phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); ++ if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) ++ phy_common->dev_ref_clk_ctrl_mmio = NULL; ++ ++ return 0; ++} ++ ++struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, ++ struct ufs_qcom_phy *common_cfg, ++ const struct phy_ops *ufs_qcom_phy_gen_ops, ++ struct ufs_qcom_phy_specific_ops *phy_spec_ops) ++{ ++ int err; ++ struct device *dev = &pdev->dev; ++ struct phy *generic_phy = NULL; ++ struct phy_provider *phy_provider; ++ ++ err = ufs_qcom_phy_base_init(pdev, common_cfg); ++ if (err) { ++ dev_err(dev, "%s: phy base init failed %d\n", __func__, err); ++ goto out; ++ } ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ err = PTR_ERR(phy_provider); ++ dev_err(dev, "%s: failed to register phy %d\n", __func__, err); ++ goto out; ++ } ++ ++ generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops); ++ if (IS_ERR(generic_phy)) { ++ err = PTR_ERR(generic_phy); ++ dev_err(dev, "%s: failed to create phy %d\n", __func__, err); ++ generic_phy = NULL; ++ goto out; ++ } ++ ++ common_cfg->phy_spec_ops = phy_spec_ops; ++ common_cfg->dev = dev; ++ ++out: ++ return generic_phy; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); ++ ++static int __ufs_qcom_phy_clk_get(struct device *dev, ++ const char *name, struct clk **clk_out, bool err_print) ++{ ++ struct clk *clk; ++ int err = 0; ++ ++ clk = devm_clk_get(dev, name); ++ if (IS_ERR(clk)) { ++ err = PTR_ERR(clk); ++ if (err_print) ++ dev_err(dev, "failed to get %s err %d", name, err); ++ } else { ++ *clk_out = clk; ++ } ++ ++ return err; ++} ++ ++static int ufs_qcom_phy_clk_get(struct device *dev, ++ const char *name, struct clk **clk_out) ++{ ++ return __ufs_qcom_phy_clk_get(dev, name, clk_out, true); ++} ++ ++int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) ++{ ++ int err; ++ ++ if (of_device_is_compatible(phy_common->dev->of_node, ++ "qcom,msm8996-ufs-phy-qmp-14nm")) ++ goto skip_txrx_clk; ++ ++ err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk", ++ &phy_common->tx_iface_clk); ++ if (err) ++ goto out; ++ ++ err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk", ++ &phy_common->rx_iface_clk); ++ if (err) ++ goto out; ++ ++skip_txrx_clk: ++ err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", ++ &phy_common->ref_clk_src); ++ if (err) ++ goto out; ++ ++ /* ++ * "ref_clk_parent" is optional hence don't abort init if it's not ++ * found. ++ */ ++ __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent", ++ &phy_common->ref_clk_parent, false); ++ ++ err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk", ++ &phy_common->ref_clk); ++ ++out: ++ return err; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); ++ ++static int ufs_qcom_phy_init_vreg(struct device *dev, ++ struct ufs_qcom_phy_vreg *vreg, ++ const char *name) ++{ ++ int err = 0; ++ ++ char prop_name[MAX_PROP_NAME]; ++ ++ vreg->name = name; ++ vreg->reg = devm_regulator_get(dev, name); ++ if (IS_ERR(vreg->reg)) { ++ err = PTR_ERR(vreg->reg); ++ dev_err(dev, "failed to get %s, %d\n", name, err); ++ goto out; ++ } ++ ++ if (dev->of_node) { ++ snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); ++ err = of_property_read_u32(dev->of_node, ++ prop_name, &vreg->max_uA); ++ if (err && err != -EINVAL) { ++ dev_err(dev, "%s: failed to read %s\n", ++ __func__, prop_name); ++ goto out; ++ } else if (err == -EINVAL || !vreg->max_uA) { ++ if (regulator_count_voltages(vreg->reg) > 0) { ++ dev_err(dev, "%s: %s is mandatory\n", ++ __func__, prop_name); ++ goto out; ++ } ++ err = 0; ++ } ++ } ++ ++ if (!strcmp(name, "vdda-pll")) { ++ vreg->max_uV = VDDA_PLL_MAX_UV; ++ vreg->min_uV = VDDA_PLL_MIN_UV; ++ } else if (!strcmp(name, "vdda-phy")) { ++ vreg->max_uV = VDDA_PHY_MAX_UV; ++ vreg->min_uV = VDDA_PHY_MIN_UV; ++ } else if (!strcmp(name, "vddp-ref-clk")) { ++ vreg->max_uV = VDDP_REF_CLK_MAX_UV; ++ vreg->min_uV = VDDP_REF_CLK_MIN_UV; ++ } ++ ++out: ++ return err; ++} ++ ++int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) ++{ ++ int err; ++ ++ err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll, ++ "vdda-pll"); ++ if (err) ++ goto out; ++ ++ err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy, ++ "vdda-phy"); ++ ++ if (err) ++ goto out; ++ ++ err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, ++ "vddp-ref-clk"); ++ ++out: ++ return err; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators); ++ ++static int ufs_qcom_phy_cfg_vreg(struct device *dev, ++ struct ufs_qcom_phy_vreg *vreg, bool on) ++{ ++ int ret = 0; ++ struct regulator *reg = vreg->reg; ++ const char *name = vreg->name; ++ int min_uV; ++ int uA_load; ++ ++ if (regulator_count_voltages(reg) > 0) { ++ min_uV = on ? vreg->min_uV : 0; ++ ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); ++ if (ret) { ++ dev_err(dev, "%s: %s set voltage failed, err=%d\n", ++ __func__, name, ret); ++ goto out; ++ } ++ uA_load = on ? vreg->max_uA : 0; ++ ret = regulator_set_load(reg, uA_load); ++ if (ret >= 0) { ++ /* ++ * regulator_set_load() returns new regulator ++ * mode upon success. ++ */ ++ ret = 0; ++ } else { ++ dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", ++ __func__, name, uA_load, ret); ++ goto out; ++ } ++ } ++out: ++ return ret; ++} ++ ++static int ufs_qcom_phy_enable_vreg(struct device *dev, ++ struct ufs_qcom_phy_vreg *vreg) ++{ ++ int ret = 0; ++ ++ if (!vreg || vreg->enabled) ++ goto out; ++ ++ ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true); ++ if (ret) { ++ dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n", ++ __func__, ret); ++ goto out; ++ } ++ ++ ret = regulator_enable(vreg->reg); ++ if (ret) { ++ dev_err(dev, "%s: enable failed, err=%d\n", ++ __func__, ret); ++ goto out; ++ } ++ ++ vreg->enabled = true; ++out: ++ return ret; ++} ++ ++static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy) ++{ ++ int ret = 0; ++ ++ if (phy->is_ref_clk_enabled) ++ goto out; ++ ++ /* ++ * reference clock is propagated in a daisy-chained manner from ++ * source to phy, so ungate them at each stage. ++ */ ++ ret = clk_prepare_enable(phy->ref_clk_src); ++ if (ret) { ++ dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", ++ __func__, ret); ++ goto out; ++ } ++ ++ /* ++ * "ref_clk_parent" is optional clock hence make sure that clk reference ++ * is available before trying to enable the clock. ++ */ ++ if (phy->ref_clk_parent) { ++ ret = clk_prepare_enable(phy->ref_clk_parent); ++ if (ret) { ++ dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", ++ __func__, ret); ++ goto out_disable_src; ++ } ++ } ++ ++ ret = clk_prepare_enable(phy->ref_clk); ++ if (ret) { ++ dev_err(phy->dev, "%s: ref_clk enable failed %d\n", ++ __func__, ret); ++ goto out_disable_parent; ++ } ++ ++ phy->is_ref_clk_enabled = true; ++ goto out; ++ ++out_disable_parent: ++ if (phy->ref_clk_parent) ++ clk_disable_unprepare(phy->ref_clk_parent); ++out_disable_src: ++ clk_disable_unprepare(phy->ref_clk_src); ++out: ++ return ret; ++} ++ ++static int ufs_qcom_phy_disable_vreg(struct device *dev, ++ struct ufs_qcom_phy_vreg *vreg) ++{ ++ int ret = 0; ++ ++ if (!vreg || !vreg->enabled) ++ goto out; ++ ++ ret = regulator_disable(vreg->reg); ++ ++ if (!ret) { ++ /* ignore errors on applying disable config */ ++ ufs_qcom_phy_cfg_vreg(dev, vreg, false); ++ vreg->enabled = false; ++ } else { ++ dev_err(dev, "%s: %s disable failed, err=%d\n", ++ __func__, vreg->name, ret); ++ } ++out: ++ return ret; ++} ++ ++static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy) ++{ ++ if (phy->is_ref_clk_enabled) { ++ clk_disable_unprepare(phy->ref_clk); ++ /* ++ * "ref_clk_parent" is optional clock hence make sure that clk ++ * reference is available before trying to disable the clock. ++ */ ++ if (phy->ref_clk_parent) ++ clk_disable_unprepare(phy->ref_clk_parent); ++ clk_disable_unprepare(phy->ref_clk_src); ++ phy->is_ref_clk_enabled = false; ++ } ++} ++ ++#define UFS_REF_CLK_EN (1 << 5) ++ ++static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable) ++{ ++ struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy); ++ ++ if (phy->dev_ref_clk_ctrl_mmio && ++ (enable ^ phy->is_dev_ref_clk_enabled)) { ++ u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio); ++ ++ if (enable) ++ temp |= UFS_REF_CLK_EN; ++ else ++ temp &= ~UFS_REF_CLK_EN; ++ ++ /* ++ * If we are here to disable this clock immediately after ++ * entering into hibern8, we need to make sure that device ++ * ref_clk is active atleast 1us after the hibern8 enter. ++ */ ++ if (!enable) ++ udelay(1); ++ ++ writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio); ++ /* ensure that ref_clk is enabled/disabled before we return */ ++ wmb(); ++ /* ++ * If we call hibern8 exit after this, we need to make sure that ++ * device ref_clk is stable for atleast 1us before the hibern8 ++ * exit command. ++ */ ++ if (enable) ++ udelay(1); ++ ++ phy->is_dev_ref_clk_enabled = enable; ++ } ++} ++ ++void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy) ++{ ++ ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true); ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_enable_dev_ref_clk); ++ ++void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy) ++{ ++ ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false); ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_disable_dev_ref_clk); ++ ++/* Turn ON M-PHY RMMI interface clocks */ ++static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy) ++{ ++ int ret = 0; ++ ++ if (phy->is_iface_clk_enabled) ++ goto out; ++ ++ ret = clk_prepare_enable(phy->tx_iface_clk); ++ if (ret) { ++ dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", ++ __func__, ret); ++ goto out; ++ } ++ ret = clk_prepare_enable(phy->rx_iface_clk); ++ if (ret) { ++ clk_disable_unprepare(phy->tx_iface_clk); ++ dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n", ++ __func__, ret); ++ goto out; ++ } ++ phy->is_iface_clk_enabled = true; ++ ++out: ++ return ret; ++} ++ ++/* Turn OFF M-PHY RMMI interface clocks */ ++void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) ++{ ++ if (phy->is_iface_clk_enabled) { ++ clk_disable_unprepare(phy->tx_iface_clk); ++ clk_disable_unprepare(phy->rx_iface_clk); ++ phy->is_iface_clk_enabled = false; ++ } ++} ++ ++int ufs_qcom_phy_start_serdes(struct phy *generic_phy) ++{ ++ struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); ++ int ret = 0; ++ ++ if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { ++ dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n", ++ __func__); ++ ret = -ENOTSUPP; ++ } else { ++ ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_start_serdes); ++ ++int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) ++{ ++ struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); ++ int ret = 0; ++ ++ if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { ++ dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", ++ __func__); ++ ret = -ENOTSUPP; ++ } else { ++ ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, ++ tx_lanes); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); ++ ++void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, ++ u8 major, u16 minor, u16 step) ++{ ++ struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); ++ ++ ufs_qcom_phy->host_ctrl_rev_major = major; ++ ufs_qcom_phy->host_ctrl_rev_minor = minor; ++ ufs_qcom_phy->host_ctrl_rev_step = step; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); ++ ++int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B) ++{ ++ struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); ++ int ret = 0; ++ ++ if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) { ++ dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n", ++ __func__); ++ ret = -ENOTSUPP; ++ } else { ++ ret = ufs_qcom_phy->phy_spec_ops-> ++ calibrate_phy(ufs_qcom_phy, is_rate_B); ++ if (ret) ++ dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n", ++ __func__, ret); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy); ++ ++int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) ++{ ++ struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); ++ ++ if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { ++ dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", ++ __func__); ++ return -ENOTSUPP; ++ } ++ ++ return ufs_qcom_phy->phy_spec_ops-> ++ is_physical_coding_sublayer_ready(ufs_qcom_phy); ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_is_pcs_ready); ++ ++int ufs_qcom_phy_power_on(struct phy *generic_phy) ++{ ++ struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); ++ struct device *dev = phy_common->dev; ++ int err; ++ ++ if (phy_common->is_powered_on) ++ return 0; ++ ++ err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); ++ if (err) { ++ dev_err(dev, "%s enable vdda_phy failed, err=%d\n", ++ __func__, err); ++ goto out; ++ } ++ ++ phy_common->phy_spec_ops->power_control(phy_common, true); ++ ++ /* vdda_pll also enables ref clock LDOs so enable it first */ ++ err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll); ++ if (err) { ++ dev_err(dev, "%s enable vdda_pll failed, err=%d\n", ++ __func__, err); ++ goto out_disable_phy; ++ } ++ ++ err = ufs_qcom_phy_enable_iface_clk(phy_common); ++ if (err) { ++ dev_err(dev, "%s enable phy iface clock failed, err=%d\n", ++ __func__, err); ++ goto out_disable_pll; ++ } ++ ++ err = ufs_qcom_phy_enable_ref_clk(phy_common); ++ if (err) { ++ dev_err(dev, "%s enable phy ref clock failed, err=%d\n", ++ __func__, err); ++ goto out_disable_iface_clk; ++ } ++ ++ /* enable device PHY ref_clk pad rail */ ++ if (phy_common->vddp_ref_clk.reg) { ++ err = ufs_qcom_phy_enable_vreg(dev, ++ &phy_common->vddp_ref_clk); ++ if (err) { ++ dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n", ++ __func__, err); ++ goto out_disable_ref_clk; ++ } ++ } ++ ++ phy_common->is_powered_on = true; ++ goto out; ++ ++out_disable_ref_clk: ++ ufs_qcom_phy_disable_ref_clk(phy_common); ++out_disable_iface_clk: ++ ufs_qcom_phy_disable_iface_clk(phy_common); ++out_disable_pll: ++ ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll); ++out_disable_phy: ++ ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy); ++out: ++ return err; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on); ++ ++int ufs_qcom_phy_power_off(struct phy *generic_phy) ++{ ++ struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); ++ ++ if (!phy_common->is_powered_on) ++ return 0; ++ ++ phy_common->phy_spec_ops->power_control(phy_common, false); ++ ++ if (phy_common->vddp_ref_clk.reg) ++ ufs_qcom_phy_disable_vreg(phy_common->dev, ++ &phy_common->vddp_ref_clk); ++ ufs_qcom_phy_disable_ref_clk(phy_common); ++ ufs_qcom_phy_disable_iface_clk(phy_common); ++ ++ ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); ++ ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); ++ phy_common->is_powered_on = false; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); +diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c +new file mode 100644 +index 000000000000..4b20abc3ae2f +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c +@@ -0,0 +1,295 @@ ++/** ++ * Copyright (C) 2016 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ULPI_PWR_CLK_MNG_REG 0x88 ++# define ULPI_PWR_OTG_COMP_DISABLE BIT(0) ++ ++#define ULPI_MISC_A 0x96 ++# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) ++# define ULPI_MISC_A_VBUSVLDEXT BIT(0) ++ ++ ++struct ulpi_seq { ++ u8 addr; ++ u8 val; ++}; ++ ++struct qcom_usb_hs_phy { ++ struct ulpi *ulpi; ++ struct phy *phy; ++ struct clk *ref_clk; ++ struct clk *sleep_clk; ++ struct regulator *v1p8; ++ struct regulator *v3p3; ++ struct reset_control *reset; ++ struct ulpi_seq *init_seq; ++ struct extcon_dev *vbus_edev; ++ struct notifier_block vbus_notify; ++}; ++ ++static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode) ++{ ++ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); ++ u8 addr; ++ int ret; ++ ++ if (!uphy->vbus_edev) { ++ u8 val = 0; ++ ++ switch (mode) { ++ case PHY_MODE_USB_OTG: ++ case PHY_MODE_USB_HOST: ++ val |= ULPI_INT_IDGRD; ++ case PHY_MODE_USB_DEVICE: ++ val |= ULPI_INT_SESS_VALID; ++ default: ++ break; ++ } ++ ++ ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val); ++ if (ret) ++ return ret; ++ ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val); ++ } else { ++ switch (mode) { ++ case PHY_MODE_USB_OTG: ++ case PHY_MODE_USB_DEVICE: ++ addr = ULPI_SET(ULPI_MISC_A); ++ break; ++ case PHY_MODE_USB_HOST: ++ addr = ULPI_CLR(ULPI_MISC_A); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG), ++ ULPI_PWR_OTG_COMP_DISABLE); ++ if (ret) ++ return ret; ++ ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL); ++ } ++ ++ return ret; ++} ++ ++static int ++qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event, ++ void *ptr) ++{ ++ struct qcom_usb_hs_phy *uphy; ++ u8 addr; ++ ++ uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify); ++ ++ if (event) ++ addr = ULPI_SET(ULPI_MISC_A); ++ else ++ addr = ULPI_CLR(ULPI_MISC_A); ++ ++ return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT); ++} ++ ++static int qcom_usb_hs_phy_power_on(struct phy *phy) ++{ ++ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); ++ struct ulpi *ulpi = uphy->ulpi; ++ const struct ulpi_seq *seq; ++ int ret, state; ++ ++ ret = clk_prepare_enable(uphy->ref_clk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(uphy->sleep_clk); ++ if (ret) ++ goto err_sleep; ++ ++ ret = regulator_set_load(uphy->v1p8, 50000); ++ if (ret < 0) ++ goto err_1p8; ++ ++ ret = regulator_enable(uphy->v1p8); ++ if (ret) ++ goto err_1p8; ++ ++ ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000, ++ 3300000); ++ if (ret) ++ goto err_3p3; ++ ++ ret = regulator_set_load(uphy->v3p3, 50000); ++ if (ret < 0) ++ goto err_3p3; ++ ++ ret = regulator_enable(uphy->v3p3); ++ if (ret) ++ goto err_3p3; ++ ++ for (seq = uphy->init_seq; seq->addr; seq++) { ++ ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr, ++ seq->val); ++ if (ret) ++ goto err_ulpi; ++ } ++ ++ if (uphy->reset) { ++ ret = reset_control_reset(uphy->reset); ++ if (ret) ++ goto err_ulpi; ++ } ++ ++ if (uphy->vbus_edev) { ++ state = extcon_get_cable_state_(uphy->vbus_edev, EXTCON_USB); ++ /* setup initial state */ ++ qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state, ++ uphy->vbus_edev); ++ ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB, ++ &uphy->vbus_notify); ++ if (ret) ++ goto err_ulpi; ++ } ++ ++ return 0; ++err_ulpi: ++ regulator_disable(uphy->v3p3); ++err_3p3: ++ regulator_disable(uphy->v1p8); ++err_1p8: ++ clk_disable_unprepare(uphy->sleep_clk); ++err_sleep: ++ clk_disable_unprepare(uphy->ref_clk); ++ return ret; ++} ++ ++static int qcom_usb_hs_phy_power_off(struct phy *phy) ++{ ++ int ret; ++ struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); ++ ++ if (uphy->vbus_edev) { ++ ret = extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB, ++ &uphy->vbus_notify); ++ if (ret) ++ return ret; ++ } ++ ++ regulator_disable(uphy->v3p3); ++ regulator_disable(uphy->v1p8); ++ clk_disable_unprepare(uphy->sleep_clk); ++ clk_disable_unprepare(uphy->ref_clk); ++ ++ return 0; ++} ++ ++static const struct phy_ops qcom_usb_hs_phy_ops = { ++ .power_on = qcom_usb_hs_phy_power_on, ++ .power_off = qcom_usb_hs_phy_power_off, ++ .set_mode = qcom_usb_hs_phy_set_mode, ++ .owner = THIS_MODULE, ++}; ++ ++static int qcom_usb_hs_phy_probe(struct ulpi *ulpi) ++{ ++ struct qcom_usb_hs_phy *uphy; ++ struct phy_provider *p; ++ struct clk *clk; ++ struct regulator *reg; ++ struct reset_control *reset; ++ int size; ++ int ret; ++ ++ uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); ++ if (!uphy) ++ return -ENOMEM; ++ ulpi_set_drvdata(ulpi, uphy); ++ uphy->ulpi = ulpi; ++ ++ size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq"); ++ if (size < 0) ++ size = 0; ++ uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1, ++ sizeof(*uphy->init_seq), GFP_KERNEL); ++ if (!uphy->init_seq) ++ return -ENOMEM; ++ ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq", ++ (u8 *)uphy->init_seq, size); ++ if (ret && size) ++ return ret; ++ /* NUL terminate */ ++ uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0; ++ ++ uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref"); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep"); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8"); ++ if (IS_ERR(reg)) ++ return PTR_ERR(reg); ++ ++ uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3"); ++ if (IS_ERR(reg)) ++ return PTR_ERR(reg); ++ ++ uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por"); ++ if (IS_ERR(reset)) { ++ if (PTR_ERR(reset) == -EPROBE_DEFER) ++ return PTR_ERR(reset); ++ uphy->reset = NULL; ++ } ++ ++ uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, ++ &qcom_usb_hs_phy_ops); ++ if (IS_ERR(uphy->phy)) ++ return PTR_ERR(uphy->phy); ++ ++ uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0); ++ if (IS_ERR(uphy->vbus_edev)) { ++ if (PTR_ERR(uphy->vbus_edev) != -ENODEV) ++ return PTR_ERR(uphy->vbus_edev); ++ uphy->vbus_edev = NULL; ++ } ++ ++ uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier; ++ phy_set_drvdata(uphy->phy, uphy); ++ ++ p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(p); ++} ++ ++static const struct of_device_id qcom_usb_hs_phy_match[] = { ++ { .compatible = "qcom,usb-hs-phy", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match); ++ ++static struct ulpi_driver qcom_usb_hs_phy_driver = { ++ .probe = qcom_usb_hs_phy_probe, ++ .driver = { ++ .name = "qcom_usb_hs_phy", ++ .of_match_table = qcom_usb_hs_phy_match, ++ }, ++}; ++module_ulpi_driver(qcom_usb_hs_phy_driver); ++ ++MODULE_DESCRIPTION("Qualcomm USB HS phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hsic.c b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c +new file mode 100644 +index 000000000000..c110563a73cb +--- /dev/null ++++ b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c +@@ -0,0 +1,159 @@ ++/** ++ * Copyright (C) 2016 Linaro Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ULPI_HSIC_CFG 0x30 ++#define ULPI_HSIC_IO_CAL 0x33 ++ ++struct qcom_usb_hsic_phy { ++ struct ulpi *ulpi; ++ struct phy *phy; ++ struct pinctrl *pctl; ++ struct clk *phy_clk; ++ struct clk *cal_clk; ++ struct clk *cal_sleep_clk; ++}; ++ ++static int qcom_usb_hsic_phy_power_on(struct phy *phy) ++{ ++ struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); ++ struct ulpi *ulpi = uphy->ulpi; ++ struct pinctrl_state *pins_default; ++ int ret; ++ ++ ret = clk_prepare_enable(uphy->phy_clk); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(uphy->cal_clk); ++ if (ret) ++ goto err_cal; ++ ++ ret = clk_prepare_enable(uphy->cal_sleep_clk); ++ if (ret) ++ goto err_sleep; ++ ++ /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ ++ ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff); ++ if (ret) ++ goto err_ulpi; ++ ++ /* Enable periodic IO calibration in HSIC_CFG register */ ++ ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8); ++ if (ret) ++ goto err_ulpi; ++ ++ /* Configure pins for HSIC functionality */ ++ pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT); ++ if (IS_ERR(pins_default)) ++ return PTR_ERR(pins_default); ++ ++ ret = pinctrl_select_state(uphy->pctl, pins_default); ++ if (ret) ++ goto err_ulpi; ++ ++ /* Enable HSIC mode in HSIC_CFG register */ ++ ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01); ++ if (ret) ++ goto err_ulpi; ++ ++ /* Disable auto-resume */ ++ ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL), ++ ULPI_IFC_CTRL_AUTORESUME); ++ if (ret) ++ goto err_ulpi; ++ ++ return ret; ++err_ulpi: ++ clk_disable_unprepare(uphy->cal_sleep_clk); ++err_sleep: ++ clk_disable_unprepare(uphy->cal_clk); ++err_cal: ++ clk_disable_unprepare(uphy->phy_clk); ++ return ret; ++} ++ ++static int qcom_usb_hsic_phy_power_off(struct phy *phy) ++{ ++ struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); ++ ++ clk_disable_unprepare(uphy->cal_sleep_clk); ++ clk_disable_unprepare(uphy->cal_clk); ++ clk_disable_unprepare(uphy->phy_clk); ++ ++ return 0; ++} ++ ++static const struct phy_ops qcom_usb_hsic_phy_ops = { ++ .power_on = qcom_usb_hsic_phy_power_on, ++ .power_off = qcom_usb_hsic_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi) ++{ ++ struct qcom_usb_hsic_phy *uphy; ++ struct phy_provider *p; ++ struct clk *clk; ++ ++ uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); ++ if (!uphy) ++ return -ENOMEM; ++ ulpi_set_drvdata(ulpi, uphy); ++ ++ uphy->ulpi = ulpi; ++ uphy->pctl = devm_pinctrl_get(&ulpi->dev); ++ if (IS_ERR(uphy->pctl)) ++ return PTR_ERR(uphy->pctl); ++ ++ uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy"); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal"); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep"); ++ if (IS_ERR(clk)) ++ return PTR_ERR(clk); ++ ++ uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, ++ &qcom_usb_hsic_phy_ops); ++ if (IS_ERR(uphy->phy)) ++ return PTR_ERR(uphy->phy); ++ phy_set_drvdata(uphy->phy, uphy); ++ ++ p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(p); ++} ++ ++static const struct of_device_id qcom_usb_hsic_phy_match[] = { ++ { .compatible = "qcom,usb-hsic-phy", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); ++ ++static struct ulpi_driver qcom_usb_hsic_phy_driver = { ++ .probe = qcom_usb_hsic_phy_probe, ++ .driver = { ++ .name = "qcom_usb_hsic_phy", ++ .of_match_table = qcom_usb_hsic_phy_match, ++ }, ++}; ++module_ulpi_driver(qcom_usb_hsic_phy_driver); ++ ++MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/renesas/Kconfig b/drivers/phy/renesas/Kconfig +new file mode 100644 +index 000000000000..432e2715e9c4 +--- /dev/null ++++ b/drivers/phy/renesas/Kconfig +@@ -0,0 +1,17 @@ ++# ++# Phy drivers for Renesas platforms ++# ++config PHY_RCAR_GEN2 ++ tristate "Renesas R-Car generation 2 USB PHY driver" ++ depends on ARCH_RENESAS ++ depends on GENERIC_PHY ++ help ++ Support for USB PHY found on Renesas R-Car generation 2 SoCs. ++ ++config PHY_RCAR_GEN3_USB2 ++ tristate "Renesas R-Car generation 3 USB 2.0 PHY driver" ++ depends on ARCH_RENESAS ++ depends on EXTCON ++ select GENERIC_PHY ++ help ++ Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs. +diff --git a/drivers/phy/renesas/Makefile b/drivers/phy/renesas/Makefile +new file mode 100644 +index 000000000000..695241aebf69 +--- /dev/null ++++ b/drivers/phy/renesas/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o ++obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o +diff --git a/drivers/phy/renesas/phy-rcar-gen2.c b/drivers/phy/renesas/phy-rcar-gen2.c +new file mode 100644 +index 000000000000..97d4dd6ea924 +--- /dev/null ++++ b/drivers/phy/renesas/phy-rcar-gen2.c +@@ -0,0 +1,337 @@ ++/* ++ * Renesas R-Car Gen2 PHY driver ++ * ++ * Copyright (C) 2014 Renesas Solutions Corp. ++ * Copyright (C) 2014 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define USBHS_LPSTS 0x02 ++#define USBHS_UGCTRL 0x80 ++#define USBHS_UGCTRL2 0x84 ++#define USBHS_UGSTS 0x88 /* From technical update */ ++ ++/* Low Power Status register (LPSTS) */ ++#define USBHS_LPSTS_SUSPM 0x4000 ++ ++/* USB General control register (UGCTRL) */ ++#define USBHS_UGCTRL_CONNECT 0x00000004 ++#define USBHS_UGCTRL_PLLRESET 0x00000001 ++ ++/* USB General control register 2 (UGCTRL2) */ ++#define USBHS_UGCTRL2_USB2SEL 0x80000000 ++#define USBHS_UGCTRL2_USB2SEL_PCI 0x00000000 ++#define USBHS_UGCTRL2_USB2SEL_USB30 0x80000000 ++#define USBHS_UGCTRL2_USB0SEL 0x00000030 ++#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010 ++#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030 ++ ++/* USB General status register (UGSTS) */ ++#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */ ++ ++#define PHYS_PER_CHANNEL 2 ++ ++struct rcar_gen2_phy { ++ struct phy *phy; ++ struct rcar_gen2_channel *channel; ++ int number; ++ u32 select_value; ++}; ++ ++struct rcar_gen2_channel { ++ struct device_node *of_node; ++ struct rcar_gen2_phy_driver *drv; ++ struct rcar_gen2_phy phys[PHYS_PER_CHANNEL]; ++ int selected_phy; ++ u32 select_mask; ++}; ++ ++struct rcar_gen2_phy_driver { ++ void __iomem *base; ++ struct clk *clk; ++ spinlock_t lock; ++ int num_channels; ++ struct rcar_gen2_channel *channels; ++}; ++ ++static int rcar_gen2_phy_init(struct phy *p) ++{ ++ struct rcar_gen2_phy *phy = phy_get_drvdata(p); ++ struct rcar_gen2_channel *channel = phy->channel; ++ struct rcar_gen2_phy_driver *drv = channel->drv; ++ unsigned long flags; ++ u32 ugctrl2; ++ ++ /* ++ * Try to acquire exclusive access to PHY. The first driver calling ++ * phy_init() on a given channel wins, and all attempts to use another ++ * PHY on this channel will fail until phy_exit() is called by the first ++ * driver. Achieving this with cmpxcgh() should be SMP-safe. ++ */ ++ if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1) ++ return -EBUSY; ++ ++ clk_prepare_enable(drv->clk); ++ ++ spin_lock_irqsave(&drv->lock, flags); ++ ugctrl2 = readl(drv->base + USBHS_UGCTRL2); ++ ugctrl2 &= ~channel->select_mask; ++ ugctrl2 |= phy->select_value; ++ writel(ugctrl2, drv->base + USBHS_UGCTRL2); ++ spin_unlock_irqrestore(&drv->lock, flags); ++ return 0; ++} ++ ++static int rcar_gen2_phy_exit(struct phy *p) ++{ ++ struct rcar_gen2_phy *phy = phy_get_drvdata(p); ++ struct rcar_gen2_channel *channel = phy->channel; ++ ++ clk_disable_unprepare(channel->drv->clk); ++ ++ channel->selected_phy = -1; ++ ++ return 0; ++} ++ ++static int rcar_gen2_phy_power_on(struct phy *p) ++{ ++ struct rcar_gen2_phy *phy = phy_get_drvdata(p); ++ struct rcar_gen2_phy_driver *drv = phy->channel->drv; ++ void __iomem *base = drv->base; ++ unsigned long flags; ++ u32 value; ++ int err = 0, i; ++ ++ /* Skip if it's not USBHS */ ++ if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) ++ return 0; ++ ++ spin_lock_irqsave(&drv->lock, flags); ++ ++ /* Power on USBHS PHY */ ++ value = readl(base + USBHS_UGCTRL); ++ value &= ~USBHS_UGCTRL_PLLRESET; ++ writel(value, base + USBHS_UGCTRL); ++ ++ value = readw(base + USBHS_LPSTS); ++ value |= USBHS_LPSTS_SUSPM; ++ writew(value, base + USBHS_LPSTS); ++ ++ for (i = 0; i < 20; i++) { ++ value = readl(base + USBHS_UGSTS); ++ if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { ++ value = readl(base + USBHS_UGCTRL); ++ value |= USBHS_UGCTRL_CONNECT; ++ writel(value, base + USBHS_UGCTRL); ++ goto out; ++ } ++ udelay(1); ++ } ++ ++ /* Timed out waiting for the PLL lock */ ++ err = -ETIMEDOUT; ++ ++out: ++ spin_unlock_irqrestore(&drv->lock, flags); ++ ++ return err; ++} ++ ++static int rcar_gen2_phy_power_off(struct phy *p) ++{ ++ struct rcar_gen2_phy *phy = phy_get_drvdata(p); ++ struct rcar_gen2_phy_driver *drv = phy->channel->drv; ++ void __iomem *base = drv->base; ++ unsigned long flags; ++ u32 value; ++ ++ /* Skip if it's not USBHS */ ++ if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) ++ return 0; ++ ++ spin_lock_irqsave(&drv->lock, flags); ++ ++ /* Power off USBHS PHY */ ++ value = readl(base + USBHS_UGCTRL); ++ value &= ~USBHS_UGCTRL_CONNECT; ++ writel(value, base + USBHS_UGCTRL); ++ ++ value = readw(base + USBHS_LPSTS); ++ value &= ~USBHS_LPSTS_SUSPM; ++ writew(value, base + USBHS_LPSTS); ++ ++ value = readl(base + USBHS_UGCTRL); ++ value |= USBHS_UGCTRL_PLLRESET; ++ writel(value, base + USBHS_UGCTRL); ++ ++ spin_unlock_irqrestore(&drv->lock, flags); ++ ++ return 0; ++} ++ ++static const struct phy_ops rcar_gen2_phy_ops = { ++ .init = rcar_gen2_phy_init, ++ .exit = rcar_gen2_phy_exit, ++ .power_on = rcar_gen2_phy_power_on, ++ .power_off = rcar_gen2_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct of_device_id rcar_gen2_phy_match_table[] = { ++ { .compatible = "renesas,usb-phy-r8a7790" }, ++ { .compatible = "renesas,usb-phy-r8a7791" }, ++ { .compatible = "renesas,usb-phy-r8a7794" }, ++ { .compatible = "renesas,rcar-gen2-usb-phy" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); ++ ++static struct phy *rcar_gen2_phy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct rcar_gen2_phy_driver *drv; ++ struct device_node *np = args->np; ++ int i; ++ ++ drv = dev_get_drvdata(dev); ++ if (!drv) ++ return ERR_PTR(-EINVAL); ++ ++ for (i = 0; i < drv->num_channels; i++) { ++ if (np == drv->channels[i].of_node) ++ break; ++ } ++ ++ if (i >= drv->num_channels || args->args[0] >= 2) ++ return ERR_PTR(-ENODEV); ++ ++ return drv->channels[i].phys[args->args[0]].phy; ++} ++ ++static const u32 select_mask[] = { ++ [0] = USBHS_UGCTRL2_USB0SEL, ++ [2] = USBHS_UGCTRL2_USB2SEL, ++}; ++ ++static const u32 select_value[][PHYS_PER_CHANNEL] = { ++ [0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB }, ++ [2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 }, ++}; ++ ++static int rcar_gen2_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rcar_gen2_phy_driver *drv; ++ struct phy_provider *provider; ++ struct device_node *np; ++ struct resource *res; ++ void __iomem *base; ++ struct clk *clk; ++ int i = 0; ++ ++ if (!dev->of_node) { ++ dev_err(dev, ++ "This driver is required to be instantiated from device tree\n"); ++ return -EINVAL; ++ } ++ ++ clk = devm_clk_get(dev, "usbhs"); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "Can't get USBHS clock\n"); ++ return PTR_ERR(clk); ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); ++ if (!drv) ++ return -ENOMEM; ++ ++ spin_lock_init(&drv->lock); ++ ++ drv->clk = clk; ++ drv->base = base; ++ ++ drv->num_channels = of_get_child_count(dev->of_node); ++ drv->channels = devm_kcalloc(dev, drv->num_channels, ++ sizeof(struct rcar_gen2_channel), ++ GFP_KERNEL); ++ if (!drv->channels) ++ return -ENOMEM; ++ ++ for_each_child_of_node(dev->of_node, np) { ++ struct rcar_gen2_channel *channel = drv->channels + i; ++ u32 channel_num; ++ int error, n; ++ ++ channel->of_node = np; ++ channel->drv = drv; ++ channel->selected_phy = -1; ++ ++ error = of_property_read_u32(np, "reg", &channel_num); ++ if (error || channel_num > 2) { ++ dev_err(dev, "Invalid \"reg\" property\n"); ++ return error; ++ } ++ channel->select_mask = select_mask[channel_num]; ++ ++ for (n = 0; n < PHYS_PER_CHANNEL; n++) { ++ struct rcar_gen2_phy *phy = &channel->phys[n]; ++ ++ phy->channel = channel; ++ phy->number = n; ++ phy->select_value = select_value[channel_num][n]; ++ ++ phy->phy = devm_phy_create(dev, NULL, ++ &rcar_gen2_phy_ops); ++ if (IS_ERR(phy->phy)) { ++ dev_err(dev, "Failed to create PHY\n"); ++ return PTR_ERR(phy->phy); ++ } ++ phy_set_drvdata(phy->phy, phy); ++ } ++ ++ i++; ++ } ++ ++ provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate); ++ if (IS_ERR(provider)) { ++ dev_err(dev, "Failed to register PHY provider\n"); ++ return PTR_ERR(provider); ++ } ++ ++ dev_set_drvdata(dev, drv); ++ ++ return 0; ++} ++ ++static struct platform_driver rcar_gen2_phy_driver = { ++ .driver = { ++ .name = "phy_rcar_gen2", ++ .of_match_table = rcar_gen2_phy_match_table, ++ }, ++ .probe = rcar_gen2_phy_probe, ++}; ++ ++module_platform_driver(rcar_gen2_phy_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY"); ++MODULE_AUTHOR("Sergei Shtylyov "); +diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c +new file mode 100644 +index 000000000000..54c34298a000 +--- /dev/null ++++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c +@@ -0,0 +1,507 @@ ++/* ++ * Renesas R-Car Gen3 for USB2.0 PHY driver ++ * ++ * Copyright (C) 2015 Renesas Electronics Corporation ++ * ++ * This is based on the phy-rcar-gen2 driver: ++ * Copyright (C) 2014 Renesas Solutions Corp. ++ * Copyright (C) 2014 Cogent Embedded, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/******* USB2.0 Host registers (original offset is +0x200) *******/ ++#define USB2_INT_ENABLE 0x000 ++#define USB2_USBCTR 0x00c ++#define USB2_SPD_RSM_TIMSET 0x10c ++#define USB2_OC_TIMSET 0x110 ++#define USB2_COMMCTRL 0x600 ++#define USB2_OBINTSTA 0x604 ++#define USB2_OBINTEN 0x608 ++#define USB2_VBCTRL 0x60c ++#define USB2_LINECTRL1 0x610 ++#define USB2_ADPCTRL 0x630 ++ ++/* INT_ENABLE */ ++#define USB2_INT_ENABLE_UCOM_INTEN BIT(3) ++#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) ++#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) ++#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \ ++ USB2_INT_ENABLE_USBH_INTB_EN | \ ++ USB2_INT_ENABLE_USBH_INTA_EN) ++ ++/* USBCTR */ ++#define USB2_USBCTR_DIRPD BIT(2) ++#define USB2_USBCTR_PLL_RST BIT(1) ++ ++/* SPD_RSM_TIMSET */ ++#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b ++ ++/* OC_TIMSET */ ++#define USB2_OC_TIMSET_INIT 0x000209ab ++ ++/* COMMCTRL */ ++#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */ ++ ++/* OBINTSTA and OBINTEN */ ++#define USB2_OBINT_SESSVLDCHG BIT(12) ++#define USB2_OBINT_IDDIGCHG BIT(11) ++#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \ ++ USB2_OBINT_IDDIGCHG) ++ ++/* VBCTRL */ ++#define USB2_VBCTRL_DRVVBUSSEL BIT(8) ++ ++/* LINECTRL1 */ ++#define USB2_LINECTRL1_DPRPD_EN BIT(19) ++#define USB2_LINECTRL1_DP_RPD BIT(18) ++#define USB2_LINECTRL1_DMRPD_EN BIT(17) ++#define USB2_LINECTRL1_DM_RPD BIT(16) ++#define USB2_LINECTRL1_OPMODE_NODRV BIT(6) ++ ++/* ADPCTRL */ ++#define USB2_ADPCTRL_OTGSESSVLD BIT(20) ++#define USB2_ADPCTRL_IDDIG BIT(19) ++#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ ++#define USB2_ADPCTRL_DRVVBUS BIT(4) ++ ++struct rcar_gen3_chan { ++ void __iomem *base; ++ struct extcon_dev *extcon; ++ struct phy *phy; ++ struct regulator *vbus; ++ struct work_struct work; ++ bool extcon_host; ++ bool has_otg; ++}; ++ ++static void rcar_gen3_phy_usb2_work(struct work_struct *work) ++{ ++ struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan, ++ work); ++ ++ if (ch->extcon_host) { ++ extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, true); ++ extcon_set_state_sync(ch->extcon, EXTCON_USB, false); ++ } else { ++ extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, false); ++ extcon_set_state_sync(ch->extcon, EXTCON_USB, true); ++ } ++} ++ ++static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) ++{ ++ void __iomem *usb2_base = ch->base; ++ u32 val = readl(usb2_base + USB2_COMMCTRL); ++ ++ dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host); ++ if (host) ++ val &= ~USB2_COMMCTRL_OTG_PERI; ++ else ++ val |= USB2_COMMCTRL_OTG_PERI; ++ writel(val, usb2_base + USB2_COMMCTRL); ++} ++ ++static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) ++{ ++ void __iomem *usb2_base = ch->base; ++ u32 val = readl(usb2_base + USB2_LINECTRL1); ++ ++ dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm); ++ val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); ++ if (dp) ++ val |= USB2_LINECTRL1_DP_RPD; ++ if (dm) ++ val |= USB2_LINECTRL1_DM_RPD; ++ writel(val, usb2_base + USB2_LINECTRL1); ++} ++ ++static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) ++{ ++ void __iomem *usb2_base = ch->base; ++ u32 val = readl(usb2_base + USB2_ADPCTRL); ++ ++ dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus); ++ if (vbus) ++ val |= USB2_ADPCTRL_DRVVBUS; ++ else ++ val &= ~USB2_ADPCTRL_DRVVBUS; ++ writel(val, usb2_base + USB2_ADPCTRL); ++} ++ ++static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch) ++{ ++ rcar_gen3_set_linectrl(ch, 1, 1); ++ rcar_gen3_set_host_mode(ch, 1); ++ rcar_gen3_enable_vbus_ctrl(ch, 1); ++ ++ ch->extcon_host = true; ++ schedule_work(&ch->work); ++} ++ ++static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) ++{ ++ rcar_gen3_set_linectrl(ch, 0, 1); ++ rcar_gen3_set_host_mode(ch, 0); ++ rcar_gen3_enable_vbus_ctrl(ch, 0); ++ ++ ch->extcon_host = false; ++ schedule_work(&ch->work); ++} ++ ++static void rcar_gen3_init_for_b_host(struct rcar_gen3_chan *ch) ++{ ++ void __iomem *usb2_base = ch->base; ++ u32 val; ++ ++ val = readl(usb2_base + USB2_LINECTRL1); ++ writel(val | USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); ++ ++ rcar_gen3_set_linectrl(ch, 1, 1); ++ rcar_gen3_set_host_mode(ch, 1); ++ rcar_gen3_enable_vbus_ctrl(ch, 0); ++ ++ val = readl(usb2_base + USB2_LINECTRL1); ++ writel(val & ~USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); ++} ++ ++static void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch) ++{ ++ rcar_gen3_set_linectrl(ch, 0, 1); ++ rcar_gen3_set_host_mode(ch, 0); ++ rcar_gen3_enable_vbus_ctrl(ch, 1); ++} ++ ++static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) ++{ ++ void __iomem *usb2_base = ch->base; ++ u32 val; ++ ++ val = readl(usb2_base + USB2_OBINTEN); ++ writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); ++ ++ rcar_gen3_enable_vbus_ctrl(ch, 0); ++ rcar_gen3_init_for_host(ch); ++ ++ writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); ++} ++ ++static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) ++{ ++ return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); ++} ++ ++static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) ++{ ++ if (!rcar_gen3_check_id(ch)) ++ rcar_gen3_init_for_host(ch); ++ else ++ rcar_gen3_init_for_peri(ch); ++} ++ ++static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) ++{ ++ return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); ++} ++ ++static ssize_t role_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct rcar_gen3_chan *ch = dev_get_drvdata(dev); ++ bool is_b_device, is_host, new_mode_is_host; ++ ++ if (!ch->has_otg || !ch->phy->init_count) ++ return -EIO; ++ ++ /* ++ * is_b_device: true is B-Device. false is A-Device. ++ * If {new_mode_}is_host: true is Host mode. false is Peripheral mode. ++ */ ++ is_b_device = rcar_gen3_check_id(ch); ++ is_host = rcar_gen3_is_host(ch); ++ if (!strncmp(buf, "host", strlen("host"))) ++ new_mode_is_host = true; ++ else if (!strncmp(buf, "peripheral", strlen("peripheral"))) ++ new_mode_is_host = false; ++ else ++ return -EINVAL; ++ ++ /* If current and new mode is the same, this returns the error */ ++ if (is_host == new_mode_is_host) ++ return -EINVAL; ++ ++ if (new_mode_is_host) { /* And is_host must be false */ ++ if (!is_b_device) /* A-Peripheral */ ++ rcar_gen3_init_from_a_peri_to_a_host(ch); ++ else /* B-Peripheral */ ++ rcar_gen3_init_for_b_host(ch); ++ } else { /* And is_host must be true */ ++ if (!is_b_device) /* A-Host */ ++ rcar_gen3_init_for_a_peri(ch); ++ else /* B-Host */ ++ rcar_gen3_init_for_peri(ch); ++ } ++ ++ return count; ++} ++ ++static ssize_t role_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct rcar_gen3_chan *ch = dev_get_drvdata(dev); ++ ++ if (!ch->has_otg || !ch->phy->init_count) ++ return -EIO; ++ ++ return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : ++ "peripheral"); ++} ++static DEVICE_ATTR_RW(role); ++ ++static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) ++{ ++ void __iomem *usb2_base = ch->base; ++ u32 val; ++ ++ val = readl(usb2_base + USB2_VBCTRL); ++ writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); ++ writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); ++ val = readl(usb2_base + USB2_OBINTEN); ++ writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); ++ val = readl(usb2_base + USB2_ADPCTRL); ++ writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); ++ val = readl(usb2_base + USB2_LINECTRL1); ++ rcar_gen3_set_linectrl(ch, 0, 0); ++ writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN, ++ usb2_base + USB2_LINECTRL1); ++ ++ rcar_gen3_device_recognition(ch); ++} ++ ++static int rcar_gen3_phy_usb2_init(struct phy *p) ++{ ++ struct rcar_gen3_chan *channel = phy_get_drvdata(p); ++ void __iomem *usb2_base = channel->base; ++ ++ /* Initialize USB2 part */ ++ writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); ++ writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); ++ writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); ++ ++ /* Initialize otg part */ ++ if (channel->has_otg) ++ rcar_gen3_init_otg(channel); ++ ++ return 0; ++} ++ ++static int rcar_gen3_phy_usb2_exit(struct phy *p) ++{ ++ struct rcar_gen3_chan *channel = phy_get_drvdata(p); ++ ++ writel(0, channel->base + USB2_INT_ENABLE); ++ ++ return 0; ++} ++ ++static int rcar_gen3_phy_usb2_power_on(struct phy *p) ++{ ++ struct rcar_gen3_chan *channel = phy_get_drvdata(p); ++ void __iomem *usb2_base = channel->base; ++ u32 val; ++ int ret; ++ ++ if (channel->vbus) { ++ ret = regulator_enable(channel->vbus); ++ if (ret) ++ return ret; ++ } ++ ++ val = readl(usb2_base + USB2_USBCTR); ++ val |= USB2_USBCTR_PLL_RST; ++ writel(val, usb2_base + USB2_USBCTR); ++ val &= ~USB2_USBCTR_PLL_RST; ++ writel(val, usb2_base + USB2_USBCTR); ++ ++ return 0; ++} ++ ++static int rcar_gen3_phy_usb2_power_off(struct phy *p) ++{ ++ struct rcar_gen3_chan *channel = phy_get_drvdata(p); ++ int ret = 0; ++ ++ if (channel->vbus) ++ ret = regulator_disable(channel->vbus); ++ ++ return ret; ++} ++ ++static const struct phy_ops rcar_gen3_phy_usb2_ops = { ++ .init = rcar_gen3_phy_usb2_init, ++ .exit = rcar_gen3_phy_usb2_exit, ++ .power_on = rcar_gen3_phy_usb2_power_on, ++ .power_off = rcar_gen3_phy_usb2_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) ++{ ++ struct rcar_gen3_chan *ch = _ch; ++ void __iomem *usb2_base = ch->base; ++ u32 status = readl(usb2_base + USB2_OBINTSTA); ++ irqreturn_t ret = IRQ_NONE; ++ ++ if (status & USB2_OBINT_BITS) { ++ dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status); ++ writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); ++ rcar_gen3_device_recognition(ch); ++ ret = IRQ_HANDLED; ++ } ++ ++ return ret; ++} ++ ++static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { ++ { .compatible = "renesas,usb2-phy-r8a7795" }, ++ { .compatible = "renesas,usb2-phy-r8a7796" }, ++ { .compatible = "renesas,rcar-gen3-usb2-phy" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); ++ ++static const unsigned int rcar_gen3_phy_cable[] = { ++ EXTCON_USB, ++ EXTCON_USB_HOST, ++ EXTCON_NONE, ++}; ++ ++static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rcar_gen3_chan *channel; ++ struct phy_provider *provider; ++ struct resource *res; ++ int irq, ret = 0; ++ ++ if (!dev->of_node) { ++ dev_err(dev, "This driver needs device tree\n"); ++ return -EINVAL; ++ } ++ ++ channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); ++ if (!channel) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ channel->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(channel->base)) ++ return PTR_ERR(channel->base); ++ ++ /* call request_irq for OTG */ ++ irq = platform_get_irq(pdev, 0); ++ if (irq >= 0) { ++ int ret; ++ ++ INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); ++ irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, ++ IRQF_SHARED, dev_name(dev), channel); ++ if (irq < 0) ++ dev_err(dev, "No irq handler (%d)\n", irq); ++ channel->has_otg = true; ++ channel->extcon = devm_extcon_dev_allocate(dev, ++ rcar_gen3_phy_cable); ++ if (IS_ERR(channel->extcon)) ++ return PTR_ERR(channel->extcon); ++ ++ ret = devm_extcon_dev_register(dev, channel->extcon); ++ if (ret < 0) { ++ dev_err(dev, "Failed to register extcon\n"); ++ return ret; ++ } ++ } ++ ++ /* ++ * devm_phy_create() will call pm_runtime_enable(&phy->dev); ++ * And then, phy-core will manage runtime pm for this device. ++ */ ++ pm_runtime_enable(dev); ++ channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops); ++ if (IS_ERR(channel->phy)) { ++ dev_err(dev, "Failed to create USB2 PHY\n"); ++ ret = PTR_ERR(channel->phy); ++ goto error; ++ } ++ ++ channel->vbus = devm_regulator_get_optional(dev, "vbus"); ++ if (IS_ERR(channel->vbus)) { ++ if (PTR_ERR(channel->vbus) == -EPROBE_DEFER) { ++ ret = PTR_ERR(channel->vbus); ++ goto error; ++ } ++ channel->vbus = NULL; ++ } ++ ++ platform_set_drvdata(pdev, channel); ++ phy_set_drvdata(channel->phy, channel); ++ ++ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(provider)) { ++ dev_err(dev, "Failed to register PHY provider\n"); ++ ret = PTR_ERR(provider); ++ goto error; ++ } else if (channel->has_otg) { ++ int ret; ++ ++ ret = device_create_file(dev, &dev_attr_role); ++ if (ret < 0) ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ pm_runtime_disable(dev); ++ ++ return ret; ++} ++ ++static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) ++{ ++ struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); ++ ++ if (channel->has_otg) ++ device_remove_file(&pdev->dev, &dev_attr_role); ++ ++ pm_runtime_disable(&pdev->dev); ++ ++ return 0; ++}; ++ ++static struct platform_driver rcar_gen3_phy_usb2_driver = { ++ .driver = { ++ .name = "phy_rcar_gen3_usb2", ++ .of_match_table = rcar_gen3_phy_usb2_match_table, ++ }, ++ .probe = rcar_gen3_phy_usb2_probe, ++ .remove = rcar_gen3_phy_usb2_remove, ++}; ++module_platform_driver(rcar_gen3_phy_usb2_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY"); ++MODULE_AUTHOR("Yoshihiro Shimoda "); +diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig +new file mode 100644 +index 000000000000..f5325b2b679e +--- /dev/null ++++ b/drivers/phy/rockchip/Kconfig +@@ -0,0 +1,51 @@ ++# ++# Phy drivers for Rockchip platforms ++# ++config PHY_ROCKCHIP_DP ++ tristate "Rockchip Display Port PHY Driver" ++ depends on ARCH_ROCKCHIP && OF ++ select GENERIC_PHY ++ help ++ Enable this to support the Rockchip Display Port PHY. ++ ++config PHY_ROCKCHIP_EMMC ++ tristate "Rockchip EMMC PHY Driver" ++ depends on ARCH_ROCKCHIP && OF ++ select GENERIC_PHY ++ help ++ Enable this to support the Rockchip EMMC PHY. ++ ++config PHY_ROCKCHIP_INNO_USB2 ++ tristate "Rockchip INNO USB2PHY Driver" ++ depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF ++ depends on COMMON_CLK ++ depends on EXTCON ++ depends on USB_SUPPORT ++ select GENERIC_PHY ++ select USB_COMMON ++ help ++ Support for Rockchip USB2.0 PHY with Innosilicon IP block. ++ ++config PHY_ROCKCHIP_PCIE ++ tristate "Rockchip PCIe PHY Driver" ++ depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST ++ select GENERIC_PHY ++ select MFD_SYSCON ++ help ++ Enable this to support the Rockchip PCIe PHY. ++ ++config PHY_ROCKCHIP_TYPEC ++ tristate "Rockchip TYPEC PHY Driver" ++ depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) ++ select EXTCON ++ select GENERIC_PHY ++ select RESET_CONTROLLER ++ help ++ Enable this to support the Rockchip USB TYPEC PHY. ++ ++config PHY_ROCKCHIP_USB ++ tristate "Rockchip USB2 PHY Driver" ++ depends on ARCH_ROCKCHIP && OF ++ select GENERIC_PHY ++ help ++ Enable this to support the Rockchip USB 2.0 PHY. +diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile +new file mode 100644 +index 000000000000..bd0acdf38e0f +--- /dev/null ++++ b/drivers/phy/rockchip/Makefile +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o ++obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o ++obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o ++obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o ++obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o ++obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o +diff --git a/drivers/phy/rockchip/phy-rockchip-dp.c b/drivers/phy/rockchip/phy-rockchip-dp.c +new file mode 100644 +index 000000000000..8b267a746576 +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-dp.c +@@ -0,0 +1,154 @@ ++/* ++ * Rockchip DP PHY driver ++ * ++ * Copyright (C) 2016 FuZhou Rockchip Co., Ltd. ++ * Author: Yakir Yang ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define GRF_SOC_CON12 0x0274 ++ ++#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK BIT(20) ++#define GRF_EDP_REF_CLK_SEL_INTER BIT(4) ++ ++#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK BIT(21) ++#define GRF_EDP_PHY_SIDDQ_ON 0 ++#define GRF_EDP_PHY_SIDDQ_OFF BIT(5) ++ ++struct rockchip_dp_phy { ++ struct device *dev; ++ struct regmap *grf; ++ struct clk *phy_24m; ++}; ++ ++static int rockchip_set_phy_state(struct phy *phy, bool enable) ++{ ++ struct rockchip_dp_phy *dp = phy_get_drvdata(phy); ++ int ret; ++ ++ if (enable) { ++ ret = regmap_write(dp->grf, GRF_SOC_CON12, ++ GRF_EDP_PHY_SIDDQ_HIWORD_MASK | ++ GRF_EDP_PHY_SIDDQ_ON); ++ if (ret < 0) { ++ dev_err(dp->dev, "Can't enable PHY power %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(dp->phy_24m); ++ } else { ++ clk_disable_unprepare(dp->phy_24m); ++ ++ ret = regmap_write(dp->grf, GRF_SOC_CON12, ++ GRF_EDP_PHY_SIDDQ_HIWORD_MASK | ++ GRF_EDP_PHY_SIDDQ_OFF); ++ } ++ ++ return ret; ++} ++ ++static int rockchip_dp_phy_power_on(struct phy *phy) ++{ ++ return rockchip_set_phy_state(phy, true); ++} ++ ++static int rockchip_dp_phy_power_off(struct phy *phy) ++{ ++ return rockchip_set_phy_state(phy, false); ++} ++ ++static const struct phy_ops rockchip_dp_phy_ops = { ++ .power_on = rockchip_dp_phy_power_on, ++ .power_off = rockchip_dp_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int rockchip_dp_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct phy_provider *phy_provider; ++ struct rockchip_dp_phy *dp; ++ struct phy *phy; ++ int ret; ++ ++ if (!np) ++ return -ENODEV; ++ ++ if (!dev->parent || !dev->parent->of_node) ++ return -ENODEV; ++ ++ dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); ++ if (!dp) ++ return -ENOMEM; ++ ++ dp->dev = dev; ++ ++ dp->phy_24m = devm_clk_get(dev, "24m"); ++ if (IS_ERR(dp->phy_24m)) { ++ dev_err(dev, "cannot get clock 24m\n"); ++ return PTR_ERR(dp->phy_24m); ++ } ++ ++ ret = clk_set_rate(dp->phy_24m, 24000000); ++ if (ret < 0) { ++ dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret); ++ return ret; ++ } ++ ++ dp->grf = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(dp->grf)) { ++ dev_err(dev, "rk3288-dp needs the General Register Files syscon\n"); ++ return PTR_ERR(dp->grf); ++ } ++ ++ ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER | ++ GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK); ++ if (ret != 0) { ++ dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret); ++ return ret; ++ } ++ ++ phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create phy\n"); ++ return PTR_ERR(phy); ++ } ++ phy_set_drvdata(phy, dp); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id rockchip_dp_phy_dt_ids[] = { ++ { .compatible = "rockchip,rk3288-dp-phy" }, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids); ++ ++static struct platform_driver rockchip_dp_phy_driver = { ++ .probe = rockchip_dp_phy_probe, ++ .driver = { ++ .name = "rockchip-dp-phy", ++ .of_match_table = rockchip_dp_phy_dt_ids, ++ }, ++}; ++ ++module_platform_driver(rockchip_dp_phy_driver); ++ ++MODULE_AUTHOR("Yakir Yang "); ++MODULE_DESCRIPTION("Rockchip DP PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c +new file mode 100644 +index 000000000000..f1b24f18e9b2 +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-emmc.c +@@ -0,0 +1,383 @@ ++/* ++ * Rockchip emmc PHY driver ++ * ++ * Copyright (C) 2016 Shawn Lin ++ * Copyright (C) 2016 ROCKCHIP, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * The higher 16-bit of this register is used for write protection ++ * only if BIT(x + 16) set to 1 the BIT(x) can be written. ++ */ ++#define HIWORD_UPDATE(val, mask, shift) \ ++ ((val) << (shift) | (mask) << ((shift) + 16)) ++ ++/* Register definition */ ++#define GRF_EMMCPHY_CON0 0x0 ++#define GRF_EMMCPHY_CON1 0x4 ++#define GRF_EMMCPHY_CON2 0x8 ++#define GRF_EMMCPHY_CON3 0xc ++#define GRF_EMMCPHY_CON4 0x10 ++#define GRF_EMMCPHY_CON5 0x14 ++#define GRF_EMMCPHY_CON6 0x18 ++#define GRF_EMMCPHY_STATUS 0x20 ++ ++#define PHYCTRL_PDB_MASK 0x1 ++#define PHYCTRL_PDB_SHIFT 0x0 ++#define PHYCTRL_PDB_PWR_ON 0x1 ++#define PHYCTRL_PDB_PWR_OFF 0x0 ++#define PHYCTRL_ENDLL_MASK 0x1 ++#define PHYCTRL_ENDLL_SHIFT 0x1 ++#define PHYCTRL_ENDLL_ENABLE 0x1 ++#define PHYCTRL_ENDLL_DISABLE 0x0 ++#define PHYCTRL_CALDONE_MASK 0x1 ++#define PHYCTRL_CALDONE_SHIFT 0x6 ++#define PHYCTRL_CALDONE_DONE 0x1 ++#define PHYCTRL_CALDONE_GOING 0x0 ++#define PHYCTRL_DLLRDY_MASK 0x1 ++#define PHYCTRL_DLLRDY_SHIFT 0x5 ++#define PHYCTRL_DLLRDY_DONE 0x1 ++#define PHYCTRL_DLLRDY_GOING 0x0 ++#define PHYCTRL_FREQSEL_200M 0x0 ++#define PHYCTRL_FREQSEL_50M 0x1 ++#define PHYCTRL_FREQSEL_100M 0x2 ++#define PHYCTRL_FREQSEL_150M 0x3 ++#define PHYCTRL_FREQSEL_MASK 0x3 ++#define PHYCTRL_FREQSEL_SHIFT 0xc ++#define PHYCTRL_DR_MASK 0x7 ++#define PHYCTRL_DR_SHIFT 0x4 ++#define PHYCTRL_DR_50OHM 0x0 ++#define PHYCTRL_DR_33OHM 0x1 ++#define PHYCTRL_DR_66OHM 0x2 ++#define PHYCTRL_DR_100OHM 0x3 ++#define PHYCTRL_DR_40OHM 0x4 ++#define PHYCTRL_OTAPDLYENA 0x1 ++#define PHYCTRL_OTAPDLYENA_MASK 0x1 ++#define PHYCTRL_OTAPDLYENA_SHIFT 0xb ++#define PHYCTRL_OTAPDLYSEL_MASK 0xf ++#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 ++ ++struct rockchip_emmc_phy { ++ unsigned int reg_offset; ++ struct regmap *reg_base; ++ struct clk *emmcclk; ++}; ++ ++static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) ++{ ++ struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); ++ unsigned int caldone; ++ unsigned int dllrdy; ++ unsigned int freqsel = PHYCTRL_FREQSEL_200M; ++ unsigned long rate; ++ unsigned long timeout; ++ ++ /* ++ * Keep phyctrl_pdb and phyctrl_endll low to allow ++ * initialization of CALIO state M/C DFFs ++ */ ++ regmap_write(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_CON6, ++ HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, ++ PHYCTRL_PDB_MASK, ++ PHYCTRL_PDB_SHIFT)); ++ regmap_write(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_CON6, ++ HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, ++ PHYCTRL_ENDLL_MASK, ++ PHYCTRL_ENDLL_SHIFT)); ++ ++ /* Already finish power_off above */ ++ if (on_off == PHYCTRL_PDB_PWR_OFF) ++ return 0; ++ ++ rate = clk_get_rate(rk_phy->emmcclk); ++ ++ if (rate != 0) { ++ unsigned long ideal_rate; ++ unsigned long diff; ++ ++ switch (rate) { ++ case 1 ... 74999999: ++ ideal_rate = 50000000; ++ freqsel = PHYCTRL_FREQSEL_50M; ++ break; ++ case 75000000 ... 124999999: ++ ideal_rate = 100000000; ++ freqsel = PHYCTRL_FREQSEL_100M; ++ break; ++ case 125000000 ... 174999999: ++ ideal_rate = 150000000; ++ freqsel = PHYCTRL_FREQSEL_150M; ++ break; ++ default: ++ ideal_rate = 200000000; ++ break; ++ } ++ ++ diff = (rate > ideal_rate) ? ++ rate - ideal_rate : ideal_rate - rate; ++ ++ /* ++ * In order for tuning delays to be accurate we need to be ++ * pretty spot on for the DLL range, so warn if we're too ++ * far off. Also warn if we're above the 200 MHz max. Don't ++ * warn for really slow rates since we won't be tuning then. ++ */ ++ if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) ++ dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); ++ } ++ ++ /* ++ * According to the user manual, calpad calibration ++ * cycle takes more than 2us without the minimal recommended ++ * value, so we may need a little margin here ++ */ ++ udelay(3); ++ regmap_write(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_CON6, ++ HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON, ++ PHYCTRL_PDB_MASK, ++ PHYCTRL_PDB_SHIFT)); ++ ++ /* ++ * According to the user manual, it asks driver to ++ * wait 5us for calpad busy trimming ++ */ ++ udelay(5); ++ regmap_read(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_STATUS, ++ &caldone); ++ caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; ++ if (caldone != PHYCTRL_CALDONE_DONE) { ++ pr_err("rockchip_emmc_phy_power: caldone timeout.\n"); ++ return -ETIMEDOUT; ++ } ++ ++ /* Set the frequency of the DLL operation */ ++ regmap_write(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_CON0, ++ HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, ++ PHYCTRL_FREQSEL_SHIFT)); ++ ++ /* Turn on the DLL */ ++ regmap_write(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_CON6, ++ HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, ++ PHYCTRL_ENDLL_MASK, ++ PHYCTRL_ENDLL_SHIFT)); ++ ++ /* ++ * We turned on the DLL even though the rate was 0 because we the ++ * clock might be turned on later. ...but we can't wait for the DLL ++ * to lock when the rate is 0 because it will never lock with no ++ * input clock. ++ * ++ * Technically we should be checking the lock later when the clock ++ * is turned on, but for now we won't. ++ */ ++ if (rate == 0) ++ return 0; ++ ++ /* ++ * After enabling analog DLL circuits docs say that we need 10.2 us if ++ * our source clock is at 50 MHz and that lock time scales linearly ++ * with clock speed. If we are powering on the PHY and the card clock ++ * is super slow (like 100 kHZ) this could take as long as 5.1 ms as ++ * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms ++ * Hopefully we won't be running at 100 kHz, but we should still make ++ * sure we wait long enough. ++ * ++ * NOTE: There appear to be corner cases where the DLL seems to take ++ * extra long to lock for reasons that aren't understood. In some ++ * extreme cases we've seen it take up to over 10ms (!). We'll be ++ * generous and give it 50ms. We still busy wait here because: ++ * - In most cases it should be super fast. ++ * - This is not called lots during normal operation so it shouldn't ++ * be a power or performance problem to busy wait. We expect it ++ * only at boot / resume. In both cases, eMMC is probably on the ++ * critical path so busy waiting a little extra time should be OK. ++ */ ++ timeout = jiffies + msecs_to_jiffies(50); ++ do { ++ udelay(1); ++ ++ regmap_read(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_STATUS, ++ &dllrdy); ++ dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; ++ if (dllrdy == PHYCTRL_DLLRDY_DONE) ++ break; ++ } while (!time_after(jiffies, timeout)); ++ ++ if (dllrdy != PHYCTRL_DLLRDY_DONE) { ++ pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static int rockchip_emmc_phy_init(struct phy *phy) ++{ ++ struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); ++ int ret = 0; ++ ++ /* ++ * We purposely get the clock here and not in probe to avoid the ++ * circular dependency problem. We expect: ++ * - PHY driver to probe ++ * - SDHCI driver to start probe ++ * - SDHCI driver to register it's clock ++ * - SDHCI driver to get the PHY ++ * - SDHCI driver to init the PHY ++ * ++ * The clock is optional, so upon any error we just set to NULL. ++ * ++ * NOTE: we don't do anything special for EPROBE_DEFER here. Given the ++ * above expected use case, EPROBE_DEFER isn't sensible to expect, so ++ * it's just like any other error. ++ */ ++ rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); ++ if (IS_ERR(rk_phy->emmcclk)) { ++ dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); ++ rk_phy->emmcclk = NULL; ++ } ++ ++ return ret; ++} ++ ++static int rockchip_emmc_phy_exit(struct phy *phy) ++{ ++ struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); ++ ++ clk_put(rk_phy->emmcclk); ++ ++ return 0; ++} ++ ++static int rockchip_emmc_phy_power_off(struct phy *phy) ++{ ++ /* Power down emmc phy analog blocks */ ++ return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); ++} ++ ++static int rockchip_emmc_phy_power_on(struct phy *phy) ++{ ++ struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); ++ ++ /* Drive impedance: 50 Ohm */ ++ regmap_write(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_CON6, ++ HIWORD_UPDATE(PHYCTRL_DR_50OHM, ++ PHYCTRL_DR_MASK, ++ PHYCTRL_DR_SHIFT)); ++ ++ /* Output tap delay: enable */ ++ regmap_write(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_CON0, ++ HIWORD_UPDATE(PHYCTRL_OTAPDLYENA, ++ PHYCTRL_OTAPDLYENA_MASK, ++ PHYCTRL_OTAPDLYENA_SHIFT)); ++ ++ /* Output tap delay */ ++ regmap_write(rk_phy->reg_base, ++ rk_phy->reg_offset + GRF_EMMCPHY_CON0, ++ HIWORD_UPDATE(4, ++ PHYCTRL_OTAPDLYSEL_MASK, ++ PHYCTRL_OTAPDLYSEL_SHIFT)); ++ ++ /* Power up emmc phy analog blocks */ ++ return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); ++} ++ ++static const struct phy_ops ops = { ++ .init = rockchip_emmc_phy_init, ++ .exit = rockchip_emmc_phy_exit, ++ .power_on = rockchip_emmc_phy_power_on, ++ .power_off = rockchip_emmc_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int rockchip_emmc_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rockchip_emmc_phy *rk_phy; ++ struct phy *generic_phy; ++ struct phy_provider *phy_provider; ++ struct regmap *grf; ++ unsigned int reg_offset; ++ ++ if (!dev->parent || !dev->parent->of_node) ++ return -ENODEV; ++ ++ grf = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(grf)) { ++ dev_err(dev, "Missing rockchip,grf property\n"); ++ return PTR_ERR(grf); ++ } ++ ++ rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); ++ if (!rk_phy) ++ return -ENOMEM; ++ ++ if (of_property_read_u32(dev->of_node, "reg", ®_offset)) { ++ dev_err(dev, "missing reg property in node %s\n", ++ dev->of_node->name); ++ return -EINVAL; ++ } ++ ++ rk_phy->reg_offset = reg_offset; ++ rk_phy->reg_base = grf; ++ ++ generic_phy = devm_phy_create(dev, dev->of_node, &ops); ++ if (IS_ERR(generic_phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ return PTR_ERR(generic_phy); ++ } ++ ++ phy_set_drvdata(generic_phy, rk_phy); ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id rockchip_emmc_phy_dt_ids[] = { ++ { .compatible = "rockchip,rk3399-emmc-phy" }, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids); ++ ++static struct platform_driver rockchip_emmc_driver = { ++ .probe = rockchip_emmc_phy_probe, ++ .driver = { ++ .name = "rockchip-emmc-phy", ++ .of_match_table = rockchip_emmc_phy_dt_ids, ++ }, ++}; ++ ++module_platform_driver(rockchip_emmc_driver); ++ ++MODULE_AUTHOR("Shawn Lin "); ++MODULE_DESCRIPTION("Rockchip EMMC PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +new file mode 100644 +index 000000000000..8efe78a49916 +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -0,0 +1,1284 @@ ++/* ++ * Rockchip USB2.0 PHY with Innosilicon IP block driver ++ * ++ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define BIT_WRITEABLE_SHIFT 16 ++#define SCHEDULE_DELAY (60 * HZ) ++#define OTG_SCHEDULE_DELAY (2 * HZ) ++ ++enum rockchip_usb2phy_port_id { ++ USB2PHY_PORT_OTG, ++ USB2PHY_PORT_HOST, ++ USB2PHY_NUM_PORTS, ++}; ++ ++enum rockchip_usb2phy_host_state { ++ PHY_STATE_HS_ONLINE = 0, ++ PHY_STATE_DISCONNECT = 1, ++ PHY_STATE_CONNECT = 2, ++ PHY_STATE_FS_LS_ONLINE = 4, ++}; ++ ++/** ++ * Different states involved in USB charger detection. ++ * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection ++ * process is not yet started. ++ * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. ++ * USB_CHG_STATE_DCD_DONE Data pin contact is detected. ++ * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects ++ * between SDP and DCP/CDP). ++ * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects ++ * between DCP and CDP). ++ * USB_CHG_STATE_DETECTED USB charger type is determined. ++ */ ++enum usb_chg_state { ++ USB_CHG_STATE_UNDEFINED = 0, ++ USB_CHG_STATE_WAIT_FOR_DCD, ++ USB_CHG_STATE_DCD_DONE, ++ USB_CHG_STATE_PRIMARY_DONE, ++ USB_CHG_STATE_SECONDARY_DONE, ++ USB_CHG_STATE_DETECTED, ++}; ++ ++static const unsigned int rockchip_usb2phy_extcon_cable[] = { ++ EXTCON_USB, ++ EXTCON_USB_HOST, ++ EXTCON_CHG_USB_SDP, ++ EXTCON_CHG_USB_CDP, ++ EXTCON_CHG_USB_DCP, ++ EXTCON_CHG_USB_SLOW, ++ EXTCON_NONE, ++}; ++ ++struct usb2phy_reg { ++ unsigned int offset; ++ unsigned int bitend; ++ unsigned int bitstart; ++ unsigned int disable; ++ unsigned int enable; ++}; ++ ++/** ++ * struct rockchip_chg_det_reg: usb charger detect registers ++ * @cp_det: charging port detected successfully. ++ * @dcp_det: dedicated charging port detected successfully. ++ * @dp_det: assert data pin connect successfully. ++ * @idm_sink_en: open dm sink curren. ++ * @idp_sink_en: open dp sink current. ++ * @idp_src_en: open dm source current. ++ * @rdm_pdwn_en: open dm pull down resistor. ++ * @vdm_src_en: open dm voltage source. ++ * @vdp_src_en: open dp voltage source. ++ * @opmode: utmi operational mode. ++ */ ++struct rockchip_chg_det_reg { ++ struct usb2phy_reg cp_det; ++ struct usb2phy_reg dcp_det; ++ struct usb2phy_reg dp_det; ++ struct usb2phy_reg idm_sink_en; ++ struct usb2phy_reg idp_sink_en; ++ struct usb2phy_reg idp_src_en; ++ struct usb2phy_reg rdm_pdwn_en; ++ struct usb2phy_reg vdm_src_en; ++ struct usb2phy_reg vdp_src_en; ++ struct usb2phy_reg opmode; ++}; ++ ++/** ++ * struct rockchip_usb2phy_port_cfg: usb-phy port configuration. ++ * @phy_sus: phy suspend register. ++ * @bvalid_det_en: vbus valid rise detection enable register. ++ * @bvalid_det_st: vbus valid rise detection status register. ++ * @bvalid_det_clr: vbus valid rise detection clear register. ++ * @ls_det_en: linestate detection enable register. ++ * @ls_det_st: linestate detection state register. ++ * @ls_det_clr: linestate detection clear register. ++ * @utmi_avalid: utmi vbus avalid status register. ++ * @utmi_bvalid: utmi vbus bvalid status register. ++ * @utmi_ls: utmi linestate state register. ++ * @utmi_hstdet: utmi host disconnect register. ++ */ ++struct rockchip_usb2phy_port_cfg { ++ struct usb2phy_reg phy_sus; ++ struct usb2phy_reg bvalid_det_en; ++ struct usb2phy_reg bvalid_det_st; ++ struct usb2phy_reg bvalid_det_clr; ++ struct usb2phy_reg ls_det_en; ++ struct usb2phy_reg ls_det_st; ++ struct usb2phy_reg ls_det_clr; ++ struct usb2phy_reg utmi_avalid; ++ struct usb2phy_reg utmi_bvalid; ++ struct usb2phy_reg utmi_ls; ++ struct usb2phy_reg utmi_hstdet; ++}; ++ ++/** ++ * struct rockchip_usb2phy_cfg: usb-phy configuration. ++ * @reg: the address offset of grf for usb-phy config. ++ * @num_ports: specify how many ports that the phy has. ++ * @clkout_ctl: keep on/turn off output clk of phy. ++ * @chg_det: charger detection registers. ++ */ ++struct rockchip_usb2phy_cfg { ++ unsigned int reg; ++ unsigned int num_ports; ++ struct usb2phy_reg clkout_ctl; ++ const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; ++ const struct rockchip_chg_det_reg chg_det; ++}; ++ ++/** ++ * struct rockchip_usb2phy_port: usb-phy port data. ++ * @port_id: flag for otg port or host port. ++ * @suspended: phy suspended flag. ++ * @utmi_avalid: utmi avalid status usage flag. ++ * true - use avalid to get vbus status ++ * flase - use bvalid to get vbus status ++ * @vbus_attached: otg device vbus status. ++ * @bvalid_irq: IRQ number assigned for vbus valid rise detection. ++ * @ls_irq: IRQ number assigned for linestate detection. ++ * @mutex: for register updating in sm_work. ++ * @chg_work: charge detect work. ++ * @otg_sm_work: OTG state machine work. ++ * @sm_work: HOST state machine work. ++ * @phy_cfg: port register configuration, assigned by driver data. ++ * @event_nb: hold event notification callback. ++ * @state: define OTG enumeration states before device reset. ++ * @mode: the dr_mode of the controller. ++ */ ++struct rockchip_usb2phy_port { ++ struct phy *phy; ++ unsigned int port_id; ++ bool suspended; ++ bool utmi_avalid; ++ bool vbus_attached; ++ int bvalid_irq; ++ int ls_irq; ++ struct mutex mutex; ++ struct delayed_work chg_work; ++ struct delayed_work otg_sm_work; ++ struct delayed_work sm_work; ++ const struct rockchip_usb2phy_port_cfg *port_cfg; ++ struct notifier_block event_nb; ++ enum usb_otg_state state; ++ enum usb_dr_mode mode; ++}; ++ ++/** ++ * struct rockchip_usb2phy: usb2.0 phy driver data. ++ * @grf: General Register Files regmap. ++ * @clk: clock struct of phy input clk. ++ * @clk480m: clock struct of phy output clk. ++ * @clk_hw: clock struct of phy output clk management. ++ * @chg_state: states involved in USB charger detection. ++ * @chg_type: USB charger types. ++ * @dcd_retries: The retry count used to track Data contact ++ * detection process. ++ * @edev: extcon device for notification registration ++ * @phy_cfg: phy register configuration, assigned by driver data. ++ * @ports: phy port instance. ++ */ ++struct rockchip_usb2phy { ++ struct device *dev; ++ struct regmap *grf; ++ struct clk *clk; ++ struct clk *clk480m; ++ struct clk_hw clk480m_hw; ++ enum usb_chg_state chg_state; ++ enum power_supply_type chg_type; ++ u8 dcd_retries; ++ struct extcon_dev *edev; ++ const struct rockchip_usb2phy_cfg *phy_cfg; ++ struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; ++}; ++ ++static inline int property_enable(struct rockchip_usb2phy *rphy, ++ const struct usb2phy_reg *reg, bool en) ++{ ++ unsigned int val, mask, tmp; ++ ++ tmp = en ? reg->enable : reg->disable; ++ mask = GENMASK(reg->bitend, reg->bitstart); ++ val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); ++ ++ return regmap_write(rphy->grf, reg->offset, val); ++} ++ ++static inline bool property_enabled(struct rockchip_usb2phy *rphy, ++ const struct usb2phy_reg *reg) ++{ ++ int ret; ++ unsigned int tmp, orig; ++ unsigned int mask = GENMASK(reg->bitend, reg->bitstart); ++ ++ ret = regmap_read(rphy->grf, reg->offset, &orig); ++ if (ret) ++ return false; ++ ++ tmp = (orig & mask) >> reg->bitstart; ++ return tmp == reg->enable; ++} ++ ++static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) ++{ ++ struct rockchip_usb2phy *rphy = ++ container_of(hw, struct rockchip_usb2phy, clk480m_hw); ++ int ret; ++ ++ /* turn on 480m clk output if it is off */ ++ if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) { ++ ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true); ++ if (ret) ++ return ret; ++ ++ /* waiting for the clk become stable */ ++ usleep_range(1200, 1300); ++ } ++ ++ return 0; ++} ++ ++static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) ++{ ++ struct rockchip_usb2phy *rphy = ++ container_of(hw, struct rockchip_usb2phy, clk480m_hw); ++ ++ /* turn off 480m clk output */ ++ property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false); ++} ++ ++static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) ++{ ++ struct rockchip_usb2phy *rphy = ++ container_of(hw, struct rockchip_usb2phy, clk480m_hw); ++ ++ return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl); ++} ++ ++static unsigned long ++rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ return 480000000; ++} ++ ++static const struct clk_ops rockchip_usb2phy_clkout_ops = { ++ .prepare = rockchip_usb2phy_clk480m_prepare, ++ .unprepare = rockchip_usb2phy_clk480m_unprepare, ++ .is_prepared = rockchip_usb2phy_clk480m_prepared, ++ .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate, ++}; ++ ++static void rockchip_usb2phy_clk480m_unregister(void *data) ++{ ++ struct rockchip_usb2phy *rphy = data; ++ ++ of_clk_del_provider(rphy->dev->of_node); ++ clk_unregister(rphy->clk480m); ++} ++ ++static int ++rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) ++{ ++ struct device_node *node = rphy->dev->of_node; ++ struct clk_init_data init; ++ const char *clk_name; ++ int ret; ++ ++ init.flags = 0; ++ init.name = "clk_usbphy_480m"; ++ init.ops = &rockchip_usb2phy_clkout_ops; ++ ++ /* optional override of the clockname */ ++ of_property_read_string(node, "clock-output-names", &init.name); ++ ++ if (rphy->clk) { ++ clk_name = __clk_get_name(rphy->clk); ++ init.parent_names = &clk_name; ++ init.num_parents = 1; ++ } else { ++ init.parent_names = NULL; ++ init.num_parents = 0; ++ } ++ ++ rphy->clk480m_hw.init = &init; ++ ++ /* register the clock */ ++ rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw); ++ if (IS_ERR(rphy->clk480m)) { ++ ret = PTR_ERR(rphy->clk480m); ++ goto err_ret; ++ } ++ ++ ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m); ++ if (ret < 0) ++ goto err_clk_provider; ++ ++ ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, ++ rphy); ++ if (ret < 0) ++ goto err_unreg_action; ++ ++ return 0; ++ ++err_unreg_action: ++ of_clk_del_provider(node); ++err_clk_provider: ++ clk_unregister(rphy->clk480m); ++err_ret: ++ return ret; ++} ++ ++static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) ++{ ++ int ret; ++ struct device_node *node = rphy->dev->of_node; ++ struct extcon_dev *edev; ++ ++ if (of_property_read_bool(node, "extcon")) { ++ edev = extcon_get_edev_by_phandle(rphy->dev, 0); ++ if (IS_ERR(edev)) { ++ if (PTR_ERR(edev) != -EPROBE_DEFER) ++ dev_err(rphy->dev, "Invalid or missing extcon\n"); ++ return PTR_ERR(edev); ++ } ++ } else { ++ /* Initialize extcon device */ ++ edev = devm_extcon_dev_allocate(rphy->dev, ++ rockchip_usb2phy_extcon_cable); ++ ++ if (IS_ERR(edev)) ++ return -ENOMEM; ++ ++ ret = devm_extcon_dev_register(rphy->dev, edev); ++ if (ret) { ++ dev_err(rphy->dev, "failed to register extcon device\n"); ++ return ret; ++ } ++ } ++ ++ rphy->edev = edev; ++ ++ return 0; ++} ++ ++static int rockchip_usb2phy_init(struct phy *phy) ++{ ++ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); ++ int ret = 0; ++ ++ mutex_lock(&rport->mutex); ++ ++ if (rport->port_id == USB2PHY_PORT_OTG) { ++ if (rport->mode != USB_DR_MODE_HOST) { ++ /* clear bvalid status and enable bvalid detect irq */ ++ ret = property_enable(rphy, ++ &rport->port_cfg->bvalid_det_clr, ++ true); ++ if (ret) ++ goto out; ++ ++ ret = property_enable(rphy, ++ &rport->port_cfg->bvalid_det_en, ++ true); ++ if (ret) ++ goto out; ++ ++ schedule_delayed_work(&rport->otg_sm_work, ++ OTG_SCHEDULE_DELAY); ++ } else { ++ /* If OTG works in host only mode, do nothing. */ ++ dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); ++ } ++ } else if (rport->port_id == USB2PHY_PORT_HOST) { ++ /* clear linestate and enable linestate detect irq */ ++ ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true); ++ if (ret) ++ goto out; ++ ++ ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true); ++ if (ret) ++ goto out; ++ ++ schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); ++ } ++ ++out: ++ mutex_unlock(&rport->mutex); ++ return ret; ++} ++ ++static int rockchip_usb2phy_power_on(struct phy *phy) ++{ ++ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); ++ int ret; ++ ++ dev_dbg(&rport->phy->dev, "port power on\n"); ++ ++ if (!rport->suspended) ++ return 0; ++ ++ ret = clk_prepare_enable(rphy->clk480m); ++ if (ret) ++ return ret; ++ ++ ret = property_enable(rphy, &rport->port_cfg->phy_sus, false); ++ if (ret) ++ return ret; ++ ++ rport->suspended = false; ++ return 0; ++} ++ ++static int rockchip_usb2phy_power_off(struct phy *phy) ++{ ++ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); ++ int ret; ++ ++ dev_dbg(&rport->phy->dev, "port power off\n"); ++ ++ if (rport->suspended) ++ return 0; ++ ++ ret = property_enable(rphy, &rport->port_cfg->phy_sus, true); ++ if (ret) ++ return ret; ++ ++ rport->suspended = true; ++ clk_disable_unprepare(rphy->clk480m); ++ ++ return 0; ++} ++ ++static int rockchip_usb2phy_exit(struct phy *phy) ++{ ++ struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); ++ ++ if (rport->port_id == USB2PHY_PORT_OTG && ++ rport->mode != USB_DR_MODE_HOST) { ++ cancel_delayed_work_sync(&rport->otg_sm_work); ++ cancel_delayed_work_sync(&rport->chg_work); ++ } else if (rport->port_id == USB2PHY_PORT_HOST) ++ cancel_delayed_work_sync(&rport->sm_work); ++ ++ return 0; ++} ++ ++static const struct phy_ops rockchip_usb2phy_ops = { ++ .init = rockchip_usb2phy_init, ++ .exit = rockchip_usb2phy_exit, ++ .power_on = rockchip_usb2phy_power_on, ++ .power_off = rockchip_usb2phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) ++{ ++ struct rockchip_usb2phy_port *rport = ++ container_of(work, struct rockchip_usb2phy_port, ++ otg_sm_work.work); ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); ++ static unsigned int cable; ++ unsigned long delay; ++ bool vbus_attach, sch_work, notify_charger; ++ ++ if (rport->utmi_avalid) ++ vbus_attach = ++ property_enabled(rphy, &rport->port_cfg->utmi_avalid); ++ else ++ vbus_attach = ++ property_enabled(rphy, &rport->port_cfg->utmi_bvalid); ++ ++ sch_work = false; ++ notify_charger = false; ++ delay = OTG_SCHEDULE_DELAY; ++ dev_dbg(&rport->phy->dev, "%s otg sm work\n", ++ usb_otg_state_string(rport->state)); ++ ++ switch (rport->state) { ++ case OTG_STATE_UNDEFINED: ++ rport->state = OTG_STATE_B_IDLE; ++ if (!vbus_attach) ++ rockchip_usb2phy_power_off(rport->phy); ++ /* fall through */ ++ case OTG_STATE_B_IDLE: ++ if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) { ++ dev_dbg(&rport->phy->dev, "usb otg host connect\n"); ++ rport->state = OTG_STATE_A_HOST; ++ rockchip_usb2phy_power_on(rport->phy); ++ return; ++ } else if (vbus_attach) { ++ dev_dbg(&rport->phy->dev, "vbus_attach\n"); ++ switch (rphy->chg_state) { ++ case USB_CHG_STATE_UNDEFINED: ++ schedule_delayed_work(&rport->chg_work, 0); ++ return; ++ case USB_CHG_STATE_DETECTED: ++ switch (rphy->chg_type) { ++ case POWER_SUPPLY_TYPE_USB: ++ dev_dbg(&rport->phy->dev, "sdp cable is connected\n"); ++ rockchip_usb2phy_power_on(rport->phy); ++ rport->state = OTG_STATE_B_PERIPHERAL; ++ notify_charger = true; ++ sch_work = true; ++ cable = EXTCON_CHG_USB_SDP; ++ break; ++ case POWER_SUPPLY_TYPE_USB_DCP: ++ dev_dbg(&rport->phy->dev, "dcp cable is connected\n"); ++ rockchip_usb2phy_power_off(rport->phy); ++ notify_charger = true; ++ sch_work = true; ++ cable = EXTCON_CHG_USB_DCP; ++ break; ++ case POWER_SUPPLY_TYPE_USB_CDP: ++ dev_dbg(&rport->phy->dev, "cdp cable is connected\n"); ++ rockchip_usb2phy_power_on(rport->phy); ++ rport->state = OTG_STATE_B_PERIPHERAL; ++ notify_charger = true; ++ sch_work = true; ++ cable = EXTCON_CHG_USB_CDP; ++ break; ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ } else { ++ notify_charger = true; ++ rphy->chg_state = USB_CHG_STATE_UNDEFINED; ++ rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; ++ } ++ ++ if (rport->vbus_attached != vbus_attach) { ++ rport->vbus_attached = vbus_attach; ++ ++ if (notify_charger && rphy->edev) { ++ extcon_set_cable_state_(rphy->edev, ++ cable, vbus_attach); ++ if (cable == EXTCON_CHG_USB_SDP) ++ extcon_set_state_sync(rphy->edev, ++ EXTCON_USB, ++ vbus_attach); ++ } ++ } ++ break; ++ case OTG_STATE_B_PERIPHERAL: ++ if (!vbus_attach) { ++ dev_dbg(&rport->phy->dev, "usb disconnect\n"); ++ rphy->chg_state = USB_CHG_STATE_UNDEFINED; ++ rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; ++ rport->state = OTG_STATE_B_IDLE; ++ delay = 0; ++ rockchip_usb2phy_power_off(rport->phy); ++ } ++ sch_work = true; ++ break; ++ case OTG_STATE_A_HOST: ++ if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) { ++ dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); ++ rport->state = OTG_STATE_B_IDLE; ++ rockchip_usb2phy_power_off(rport->phy); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (sch_work) ++ schedule_delayed_work(&rport->otg_sm_work, delay); ++} ++ ++static const char *chg_to_string(enum power_supply_type chg_type) ++{ ++ switch (chg_type) { ++ case POWER_SUPPLY_TYPE_USB: ++ return "USB_SDP_CHARGER"; ++ case POWER_SUPPLY_TYPE_USB_DCP: ++ return "USB_DCP_CHARGER"; ++ case POWER_SUPPLY_TYPE_USB_CDP: ++ return "USB_CDP_CHARGER"; ++ default: ++ return "INVALID_CHARGER"; ++ } ++} ++ ++static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, ++ bool en) ++{ ++ property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); ++ property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en); ++} ++ ++static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, ++ bool en) ++{ ++ property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en); ++ property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en); ++} ++ ++static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, ++ bool en) ++{ ++ property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en); ++ property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en); ++} ++ ++#define CHG_DCD_POLL_TIME (100 * HZ / 1000) ++#define CHG_DCD_MAX_RETRIES 6 ++#define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) ++#define CHG_SECONDARY_DET_TIME (40 * HZ / 1000) ++static void rockchip_chg_detect_work(struct work_struct *work) ++{ ++ struct rockchip_usb2phy_port *rport = ++ container_of(work, struct rockchip_usb2phy_port, chg_work.work); ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); ++ bool is_dcd, tmout, vout; ++ unsigned long delay; ++ ++ dev_dbg(&rport->phy->dev, "chg detection work state = %d\n", ++ rphy->chg_state); ++ switch (rphy->chg_state) { ++ case USB_CHG_STATE_UNDEFINED: ++ if (!rport->suspended) ++ rockchip_usb2phy_power_off(rport->phy); ++ /* put the controller in non-driving mode */ ++ property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false); ++ /* Start DCD processing stage 1 */ ++ rockchip_chg_enable_dcd(rphy, true); ++ rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; ++ rphy->dcd_retries = 0; ++ delay = CHG_DCD_POLL_TIME; ++ break; ++ case USB_CHG_STATE_WAIT_FOR_DCD: ++ /* get data contact detection status */ ++ is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det); ++ tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES; ++ /* stage 2 */ ++ if (is_dcd || tmout) { ++ /* stage 4 */ ++ /* Turn off DCD circuitry */ ++ rockchip_chg_enable_dcd(rphy, false); ++ /* Voltage Source on DP, Probe on DM */ ++ rockchip_chg_enable_primary_det(rphy, true); ++ delay = CHG_PRIMARY_DET_TIME; ++ rphy->chg_state = USB_CHG_STATE_DCD_DONE; ++ } else { ++ /* stage 3 */ ++ delay = CHG_DCD_POLL_TIME; ++ } ++ break; ++ case USB_CHG_STATE_DCD_DONE: ++ vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det); ++ rockchip_chg_enable_primary_det(rphy, false); ++ if (vout) { ++ /* Voltage Source on DM, Probe on DP */ ++ rockchip_chg_enable_secondary_det(rphy, true); ++ delay = CHG_SECONDARY_DET_TIME; ++ rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE; ++ } else { ++ if (rphy->dcd_retries == CHG_DCD_MAX_RETRIES) { ++ /* floating charger found */ ++ rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; ++ rphy->chg_state = USB_CHG_STATE_DETECTED; ++ delay = 0; ++ } else { ++ rphy->chg_type = POWER_SUPPLY_TYPE_USB; ++ rphy->chg_state = USB_CHG_STATE_DETECTED; ++ delay = 0; ++ } ++ } ++ break; ++ case USB_CHG_STATE_PRIMARY_DONE: ++ vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det); ++ /* Turn off voltage source */ ++ rockchip_chg_enable_secondary_det(rphy, false); ++ if (vout) ++ rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; ++ else ++ rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; ++ /* fall through */ ++ case USB_CHG_STATE_SECONDARY_DONE: ++ rphy->chg_state = USB_CHG_STATE_DETECTED; ++ delay = 0; ++ /* fall through */ ++ case USB_CHG_STATE_DETECTED: ++ /* put the controller in normal mode */ ++ property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true); ++ rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); ++ dev_info(&rport->phy->dev, "charger = %s\n", ++ chg_to_string(rphy->chg_type)); ++ return; ++ default: ++ return; ++ } ++ ++ schedule_delayed_work(&rport->chg_work, delay); ++} ++ ++/* ++ * The function manage host-phy port state and suspend/resume phy port ++ * to save power. ++ * ++ * we rely on utmi_linestate and utmi_hostdisconnect to identify whether ++ * devices is disconnect or not. Besides, we do not need care it is FS/LS ++ * disconnected or HS disconnected, actually, we just only need get the ++ * device is disconnected at last through rearm the delayed work, ++ * to suspend the phy port in _PHY_STATE_DISCONNECT_ case. ++ * ++ * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke ++ * some clk related APIs, so do not invoke it from interrupt context directly. ++ */ ++static void rockchip_usb2phy_sm_work(struct work_struct *work) ++{ ++ struct rockchip_usb2phy_port *rport = ++ container_of(work, struct rockchip_usb2phy_port, sm_work.work); ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); ++ unsigned int sh = rport->port_cfg->utmi_hstdet.bitend - ++ rport->port_cfg->utmi_hstdet.bitstart + 1; ++ unsigned int ul, uhd, state; ++ unsigned int ul_mask, uhd_mask; ++ int ret; ++ ++ mutex_lock(&rport->mutex); ++ ++ ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul); ++ if (ret < 0) ++ goto next_schedule; ++ ++ ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, ++ &uhd); ++ if (ret < 0) ++ goto next_schedule; ++ ++ uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, ++ rport->port_cfg->utmi_hstdet.bitstart); ++ ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, ++ rport->port_cfg->utmi_ls.bitstart); ++ ++ /* stitch on utmi_ls and utmi_hstdet as phy state */ ++ state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | ++ (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); ++ ++ switch (state) { ++ case PHY_STATE_HS_ONLINE: ++ dev_dbg(&rport->phy->dev, "HS online\n"); ++ break; ++ case PHY_STATE_FS_LS_ONLINE: ++ /* ++ * For FS/LS device, the online state share with connect state ++ * from utmi_ls and utmi_hstdet register, so we distinguish ++ * them via suspended flag. ++ * ++ * Plus, there are two cases, one is D- Line pull-up, and D+ ++ * line pull-down, the state is 4; another is D+ line pull-up, ++ * and D- line pull-down, the state is 2. ++ */ ++ if (!rport->suspended) { ++ /* D- line pull-up, D+ line pull-down */ ++ dev_dbg(&rport->phy->dev, "FS/LS online\n"); ++ break; ++ } ++ /* fall through */ ++ case PHY_STATE_CONNECT: ++ if (rport->suspended) { ++ dev_dbg(&rport->phy->dev, "Connected\n"); ++ rockchip_usb2phy_power_on(rport->phy); ++ rport->suspended = false; ++ } else { ++ /* D+ line pull-up, D- line pull-down */ ++ dev_dbg(&rport->phy->dev, "FS/LS online\n"); ++ } ++ break; ++ case PHY_STATE_DISCONNECT: ++ if (!rport->suspended) { ++ dev_dbg(&rport->phy->dev, "Disconnected\n"); ++ rockchip_usb2phy_power_off(rport->phy); ++ rport->suspended = true; ++ } ++ ++ /* ++ * activate the linestate detection to get the next device ++ * plug-in irq. ++ */ ++ property_enable(rphy, &rport->port_cfg->ls_det_clr, true); ++ property_enable(rphy, &rport->port_cfg->ls_det_en, true); ++ ++ /* ++ * we don't need to rearm the delayed work when the phy port ++ * is suspended. ++ */ ++ mutex_unlock(&rport->mutex); ++ return; ++ default: ++ dev_dbg(&rport->phy->dev, "unknown phy state\n"); ++ break; ++ } ++ ++next_schedule: ++ mutex_unlock(&rport->mutex); ++ schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); ++} ++ ++static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) ++{ ++ struct rockchip_usb2phy_port *rport = data; ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); ++ ++ if (!property_enabled(rphy, &rport->port_cfg->ls_det_st)) ++ return IRQ_NONE; ++ ++ mutex_lock(&rport->mutex); ++ ++ /* disable linestate detect irq and clear its status */ ++ property_enable(rphy, &rport->port_cfg->ls_det_en, false); ++ property_enable(rphy, &rport->port_cfg->ls_det_clr, true); ++ ++ mutex_unlock(&rport->mutex); ++ ++ /* ++ * In this case for host phy port, a new device is plugged in, ++ * meanwhile, if the phy port is suspended, we need rearm the work to ++ * resume it and mange its states; otherwise, we do nothing about that. ++ */ ++ if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST) ++ rockchip_usb2phy_sm_work(&rport->sm_work.work); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) ++{ ++ struct rockchip_usb2phy_port *rport = data; ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); ++ ++ if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st)) ++ return IRQ_NONE; ++ ++ mutex_lock(&rport->mutex); ++ ++ /* clear bvalid detect irq pending status */ ++ property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true); ++ ++ mutex_unlock(&rport->mutex); ++ ++ rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); ++ ++ return IRQ_HANDLED; ++} ++ ++static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, ++ struct rockchip_usb2phy_port *rport, ++ struct device_node *child_np) ++{ ++ int ret; ++ ++ rport->port_id = USB2PHY_PORT_HOST; ++ rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST]; ++ rport->suspended = true; ++ ++ mutex_init(&rport->mutex); ++ INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work); ++ ++ rport->ls_irq = of_irq_get_byname(child_np, "linestate"); ++ if (rport->ls_irq < 0) { ++ dev_err(rphy->dev, "no linestate irq provided\n"); ++ return rport->ls_irq; ++ } ++ ++ ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, ++ rockchip_usb2phy_linestate_irq, ++ IRQF_ONESHOT, ++ "rockchip_usb2phy", rport); ++ if (ret) { ++ dev_err(rphy->dev, "failed to request linestate irq handle\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int rockchip_otg_event(struct notifier_block *nb, ++ unsigned long event, void *ptr) ++{ ++ struct rockchip_usb2phy_port *rport = ++ container_of(nb, struct rockchip_usb2phy_port, event_nb); ++ ++ schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY); ++ ++ return NOTIFY_DONE; ++} ++ ++static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, ++ struct rockchip_usb2phy_port *rport, ++ struct device_node *child_np) ++{ ++ int ret; ++ ++ rport->port_id = USB2PHY_PORT_OTG; ++ rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; ++ rport->state = OTG_STATE_UNDEFINED; ++ ++ /* ++ * set suspended flag to true, but actually don't ++ * put phy in suspend mode, it aims to enable usb ++ * phy and clock in power_on() called by usb controller ++ * driver during probe. ++ */ ++ rport->suspended = true; ++ rport->vbus_attached = false; ++ ++ mutex_init(&rport->mutex); ++ ++ rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); ++ if (rport->mode == USB_DR_MODE_HOST) { ++ ret = 0; ++ goto out; ++ } ++ ++ INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); ++ INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); ++ ++ rport->utmi_avalid = ++ of_property_read_bool(child_np, "rockchip,utmi-avalid"); ++ ++ rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); ++ if (rport->bvalid_irq < 0) { ++ dev_err(rphy->dev, "no vbus valid irq provided\n"); ++ ret = rport->bvalid_irq; ++ goto out; ++ } ++ ++ ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL, ++ rockchip_usb2phy_bvalid_irq, ++ IRQF_ONESHOT, ++ "rockchip_usb2phy_bvalid", rport); ++ if (ret) { ++ dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n"); ++ goto out; ++ } ++ ++ if (!IS_ERR(rphy->edev)) { ++ rport->event_nb.notifier_call = rockchip_otg_event; ++ ++ ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, ++ &rport->event_nb); ++ if (ret) ++ dev_err(rphy->dev, "register USB HOST notifier failed\n"); ++ } ++ ++out: ++ return ret; ++} ++ ++static int rockchip_usb2phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *child_np; ++ struct phy_provider *provider; ++ struct rockchip_usb2phy *rphy; ++ const struct rockchip_usb2phy_cfg *phy_cfgs; ++ const struct of_device_id *match; ++ unsigned int reg; ++ int index, ret; ++ ++ rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL); ++ if (!rphy) ++ return -ENOMEM; ++ ++ match = of_match_device(dev->driver->of_match_table, dev); ++ if (!match || !match->data) { ++ dev_err(dev, "phy configs are not assigned!\n"); ++ return -EINVAL; ++ } ++ ++ if (!dev->parent || !dev->parent->of_node) ++ return -EINVAL; ++ ++ rphy->grf = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(rphy->grf)) ++ return PTR_ERR(rphy->grf); ++ ++ if (of_property_read_u32(np, "reg", ®)) { ++ dev_err(dev, "the reg property is not assigned in %s node\n", ++ np->name); ++ return -EINVAL; ++ } ++ ++ rphy->dev = dev; ++ phy_cfgs = match->data; ++ rphy->chg_state = USB_CHG_STATE_UNDEFINED; ++ rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; ++ platform_set_drvdata(pdev, rphy); ++ ++ ret = rockchip_usb2phy_extcon_register(rphy); ++ if (ret) ++ return ret; ++ ++ /* find out a proper config which can be matched with dt. */ ++ index = 0; ++ while (phy_cfgs[index].reg) { ++ if (phy_cfgs[index].reg == reg) { ++ rphy->phy_cfg = &phy_cfgs[index]; ++ break; ++ } ++ ++ ++index; ++ } ++ ++ if (!rphy->phy_cfg) { ++ dev_err(dev, "no phy-config can be matched with %s node\n", ++ np->name); ++ return -EINVAL; ++ } ++ ++ rphy->clk = of_clk_get_by_name(np, "phyclk"); ++ if (!IS_ERR(rphy->clk)) { ++ clk_prepare_enable(rphy->clk); ++ } else { ++ dev_info(&pdev->dev, "no phyclk specified\n"); ++ rphy->clk = NULL; ++ } ++ ++ ret = rockchip_usb2phy_clk480m_register(rphy); ++ if (ret) { ++ dev_err(dev, "failed to register 480m output clock\n"); ++ goto disable_clks; ++ } ++ ++ index = 0; ++ for_each_available_child_of_node(np, child_np) { ++ struct rockchip_usb2phy_port *rport = &rphy->ports[index]; ++ struct phy *phy; ++ ++ /* This driver aims to support both otg-port and host-port */ ++ if (of_node_cmp(child_np->name, "host-port") && ++ of_node_cmp(child_np->name, "otg-port")) ++ goto next_child; ++ ++ phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create phy\n"); ++ ret = PTR_ERR(phy); ++ goto put_child; ++ } ++ ++ rport->phy = phy; ++ phy_set_drvdata(rport->phy, rport); ++ ++ /* initialize otg/host port separately */ ++ if (!of_node_cmp(child_np->name, "host-port")) { ++ ret = rockchip_usb2phy_host_port_init(rphy, rport, ++ child_np); ++ if (ret) ++ goto put_child; ++ } else { ++ ret = rockchip_usb2phy_otg_port_init(rphy, rport, ++ child_np); ++ if (ret) ++ goto put_child; ++ } ++ ++next_child: ++ /* to prevent out of boundary */ ++ if (++index >= rphy->phy_cfg->num_ports) ++ break; ++ } ++ ++ provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(provider); ++ ++put_child: ++ of_node_put(child_np); ++disable_clks: ++ if (rphy->clk) { ++ clk_disable_unprepare(rphy->clk); ++ clk_put(rphy->clk); ++ } ++ return ret; ++} ++ ++static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { ++ { ++ .reg = 0x100, ++ .num_ports = 2, ++ .clkout_ctl = { 0x108, 4, 4, 1, 0 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_OTG] = { ++ .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, ++ .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, ++ .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, ++ .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 }, ++ .ls_det_en = { 0x0110, 0, 0, 0, 1 }, ++ .ls_det_st = { 0x0114, 0, 0, 0, 1 }, ++ .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, ++ .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, ++ .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, ++ .utmi_ls = { 0x0120, 5, 4, 0, 1 }, ++ }, ++ [USB2PHY_PORT_HOST] = { ++ .phy_sus = { 0x104, 15, 0, 0, 0x1d1 }, ++ .ls_det_en = { 0x110, 1, 1, 0, 1 }, ++ .ls_det_st = { 0x114, 1, 1, 0, 1 }, ++ .ls_det_clr = { 0x118, 1, 1, 0, 1 }, ++ .utmi_ls = { 0x120, 17, 16, 0, 1 }, ++ .utmi_hstdet = { 0x120, 19, 19, 0, 1 } ++ } ++ }, ++ .chg_det = { ++ .opmode = { 0x0100, 3, 0, 5, 1 }, ++ .cp_det = { 0x0120, 24, 24, 0, 1 }, ++ .dcp_det = { 0x0120, 23, 23, 0, 1 }, ++ .dp_det = { 0x0120, 25, 25, 0, 1 }, ++ .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, ++ .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, ++ .idp_src_en = { 0x0108, 9, 9, 0, 1 }, ++ .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, ++ .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, ++ .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, ++ }, ++ }, ++ { /* sentinel */ } ++}; ++ ++static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = { ++ { ++ .reg = 0x700, ++ .num_ports = 2, ++ .clkout_ctl = { 0x0724, 15, 15, 1, 0 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_HOST] = { ++ .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, ++ .ls_det_en = { 0x0680, 4, 4, 0, 1 }, ++ .ls_det_st = { 0x0690, 4, 4, 0, 1 }, ++ .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, ++ .utmi_ls = { 0x049c, 14, 13, 0, 1 }, ++ .utmi_hstdet = { 0x049c, 12, 12, 0, 1 } ++ } ++ }, ++ }, ++ { /* sentinel */ } ++}; ++ ++static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { ++ { ++ .reg = 0xe450, ++ .num_ports = 2, ++ .clkout_ctl = { 0xe450, 4, 4, 1, 0 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_OTG] = { ++ .phy_sus = { 0xe454, 1, 0, 2, 1 }, ++ .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, ++ .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, ++ .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, ++ .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, ++ .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, ++ }, ++ [USB2PHY_PORT_HOST] = { ++ .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, ++ .ls_det_en = { 0xe3c0, 6, 6, 0, 1 }, ++ .ls_det_st = { 0xe3e0, 6, 6, 0, 1 }, ++ .ls_det_clr = { 0xe3d0, 6, 6, 0, 1 }, ++ .utmi_ls = { 0xe2ac, 22, 21, 0, 1 }, ++ .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 } ++ } ++ }, ++ .chg_det = { ++ .opmode = { 0xe454, 3, 0, 5, 1 }, ++ .cp_det = { 0xe2ac, 2, 2, 0, 1 }, ++ .dcp_det = { 0xe2ac, 1, 1, 0, 1 }, ++ .dp_det = { 0xe2ac, 0, 0, 0, 1 }, ++ .idm_sink_en = { 0xe450, 8, 8, 0, 1 }, ++ .idp_sink_en = { 0xe450, 7, 7, 0, 1 }, ++ .idp_src_en = { 0xe450, 9, 9, 0, 1 }, ++ .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 }, ++ .vdm_src_en = { 0xe450, 12, 12, 0, 1 }, ++ .vdp_src_en = { 0xe450, 11, 11, 0, 1 }, ++ }, ++ }, ++ { ++ .reg = 0xe460, ++ .num_ports = 2, ++ .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_OTG] = { ++ .phy_sus = { 0xe464, 1, 0, 2, 1 }, ++ .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, ++ .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, ++ .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, ++ .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, ++ .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, ++ }, ++ [USB2PHY_PORT_HOST] = { ++ .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, ++ .ls_det_en = { 0xe3c0, 11, 11, 0, 1 }, ++ .ls_det_st = { 0xe3e0, 11, 11, 0, 1 }, ++ .ls_det_clr = { 0xe3d0, 11, 11, 0, 1 }, ++ .utmi_ls = { 0xe2ac, 26, 25, 0, 1 }, ++ .utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 } ++ } ++ }, ++ }, ++ { /* sentinel */ } ++}; ++ ++static const struct of_device_id rockchip_usb2phy_dt_match[] = { ++ { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, ++ { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, ++ { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); ++ ++static struct platform_driver rockchip_usb2phy_driver = { ++ .probe = rockchip_usb2phy_probe, ++ .driver = { ++ .name = "rockchip-usb2phy", ++ .of_match_table = rockchip_usb2phy_dt_match, ++ }, ++}; ++module_platform_driver(rockchip_usb2phy_driver); ++ ++MODULE_AUTHOR("Frank Wang "); ++MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/rockchip/phy-rockchip-pcie.c b/drivers/phy/rockchip/phy-rockchip-pcie.c +new file mode 100644 +index 000000000000..6904633cad68 +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-pcie.c +@@ -0,0 +1,346 @@ ++/* ++ * Rockchip PCIe PHY driver ++ * ++ * Copyright (C) 2016 Shawn Lin ++ * Copyright (C) 2016 ROCKCHIP, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * The higher 16-bit of this register is used for write protection ++ * only if BIT(x + 16) set to 1 the BIT(x) can be written. ++ */ ++#define HIWORD_UPDATE(val, mask, shift) \ ++ ((val) << (shift) | (mask) << ((shift) + 16)) ++ ++#define PHY_MAX_LANE_NUM 4 ++#define PHY_CFG_DATA_SHIFT 7 ++#define PHY_CFG_ADDR_SHIFT 1 ++#define PHY_CFG_DATA_MASK 0xf ++#define PHY_CFG_ADDR_MASK 0x3f ++#define PHY_CFG_RD_MASK 0x3ff ++#define PHY_CFG_WR_ENABLE 1 ++#define PHY_CFG_WR_DISABLE 1 ++#define PHY_CFG_WR_SHIFT 0 ++#define PHY_CFG_WR_MASK 1 ++#define PHY_CFG_PLL_LOCK 0x10 ++#define PHY_CFG_CLK_TEST 0x10 ++#define PHY_CFG_CLK_SCC 0x12 ++#define PHY_CFG_SEPE_RATE BIT(3) ++#define PHY_CFG_PLL_100M BIT(3) ++#define PHY_PLL_LOCKED BIT(9) ++#define PHY_PLL_OUTPUT BIT(10) ++#define PHY_LANE_A_STATUS 0x30 ++#define PHY_LANE_B_STATUS 0x31 ++#define PHY_LANE_C_STATUS 0x32 ++#define PHY_LANE_D_STATUS 0x33 ++#define PHY_LANE_RX_DET_SHIFT 11 ++#define PHY_LANE_RX_DET_TH 0x1 ++#define PHY_LANE_IDLE_OFF 0x1 ++#define PHY_LANE_IDLE_MASK 0x1 ++#define PHY_LANE_IDLE_A_SHIFT 3 ++#define PHY_LANE_IDLE_B_SHIFT 4 ++#define PHY_LANE_IDLE_C_SHIFT 5 ++#define PHY_LANE_IDLE_D_SHIFT 6 ++ ++struct rockchip_pcie_data { ++ unsigned int pcie_conf; ++ unsigned int pcie_status; ++ unsigned int pcie_laneoff; ++}; ++ ++struct rockchip_pcie_phy { ++ struct rockchip_pcie_data *phy_data; ++ struct regmap *reg_base; ++ struct reset_control *phy_rst; ++ struct clk *clk_pciephy_ref; ++}; ++ ++static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy, ++ u32 addr, u32 data) ++{ ++ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, ++ HIWORD_UPDATE(data, ++ PHY_CFG_DATA_MASK, ++ PHY_CFG_DATA_SHIFT) | ++ HIWORD_UPDATE(addr, ++ PHY_CFG_ADDR_MASK, ++ PHY_CFG_ADDR_SHIFT)); ++ udelay(1); ++ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, ++ HIWORD_UPDATE(PHY_CFG_WR_ENABLE, ++ PHY_CFG_WR_MASK, ++ PHY_CFG_WR_SHIFT)); ++ udelay(1); ++ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, ++ HIWORD_UPDATE(PHY_CFG_WR_DISABLE, ++ PHY_CFG_WR_MASK, ++ PHY_CFG_WR_SHIFT)); ++} ++ ++static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy, ++ u32 addr) ++{ ++ u32 val; ++ ++ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, ++ HIWORD_UPDATE(addr, ++ PHY_CFG_RD_MASK, ++ PHY_CFG_ADDR_SHIFT)); ++ regmap_read(rk_phy->reg_base, ++ rk_phy->phy_data->pcie_status, ++ &val); ++ return val; ++} ++ ++static int rockchip_pcie_phy_power_off(struct phy *phy) ++{ ++ struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); ++ int err = 0; ++ ++ err = reset_control_assert(rk_phy->phy_rst); ++ if (err) { ++ dev_err(&phy->dev, "assert phy_rst err %d\n", err); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int rockchip_pcie_phy_power_on(struct phy *phy) ++{ ++ struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); ++ int err = 0; ++ u32 status; ++ unsigned long timeout; ++ ++ err = reset_control_deassert(rk_phy->phy_rst); ++ if (err) { ++ dev_err(&phy->dev, "deassert phy_rst err %d\n", err); ++ return err; ++ } ++ ++ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, ++ HIWORD_UPDATE(PHY_CFG_PLL_LOCK, ++ PHY_CFG_ADDR_MASK, ++ PHY_CFG_ADDR_SHIFT)); ++ ++ /* ++ * No documented timeout value for phy operation below, ++ * so we make it large enough here. And we use loop-break ++ * method which should not be harmful. ++ */ ++ timeout = jiffies + msecs_to_jiffies(1000); ++ ++ err = -EINVAL; ++ while (time_before(jiffies, timeout)) { ++ regmap_read(rk_phy->reg_base, ++ rk_phy->phy_data->pcie_status, ++ &status); ++ if (status & PHY_PLL_LOCKED) { ++ dev_dbg(&phy->dev, "pll locked!\n"); ++ err = 0; ++ break; ++ } ++ msleep(20); ++ } ++ ++ if (err) { ++ dev_err(&phy->dev, "pll lock timeout!\n"); ++ goto err_pll_lock; ++ } ++ ++ phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE); ++ phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M); ++ ++ err = -ETIMEDOUT; ++ while (time_before(jiffies, timeout)) { ++ regmap_read(rk_phy->reg_base, ++ rk_phy->phy_data->pcie_status, ++ &status); ++ if (!(status & PHY_PLL_OUTPUT)) { ++ dev_dbg(&phy->dev, "pll output enable done!\n"); ++ err = 0; ++ break; ++ } ++ msleep(20); ++ } ++ ++ if (err) { ++ dev_err(&phy->dev, "pll output enable timeout!\n"); ++ goto err_pll_lock; ++ } ++ ++ regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, ++ HIWORD_UPDATE(PHY_CFG_PLL_LOCK, ++ PHY_CFG_ADDR_MASK, ++ PHY_CFG_ADDR_SHIFT)); ++ err = -EINVAL; ++ while (time_before(jiffies, timeout)) { ++ regmap_read(rk_phy->reg_base, ++ rk_phy->phy_data->pcie_status, ++ &status); ++ if (status & PHY_PLL_LOCKED) { ++ dev_dbg(&phy->dev, "pll relocked!\n"); ++ err = 0; ++ break; ++ } ++ msleep(20); ++ } ++ ++ if (err) { ++ dev_err(&phy->dev, "pll relock timeout!\n"); ++ goto err_pll_lock; ++ } ++ ++ return 0; ++ ++err_pll_lock: ++ reset_control_assert(rk_phy->phy_rst); ++ return err; ++} ++ ++static int rockchip_pcie_phy_init(struct phy *phy) ++{ ++ struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); ++ int err = 0; ++ ++ err = clk_prepare_enable(rk_phy->clk_pciephy_ref); ++ if (err) { ++ dev_err(&phy->dev, "Fail to enable pcie ref clock.\n"); ++ goto err_refclk; ++ } ++ ++ err = reset_control_assert(rk_phy->phy_rst); ++ if (err) { ++ dev_err(&phy->dev, "assert phy_rst err %d\n", err); ++ goto err_reset; ++ } ++ ++ return err; ++ ++err_reset: ++ clk_disable_unprepare(rk_phy->clk_pciephy_ref); ++err_refclk: ++ return err; ++} ++ ++static int rockchip_pcie_phy_exit(struct phy *phy) ++{ ++ struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); ++ ++ clk_disable_unprepare(rk_phy->clk_pciephy_ref); ++ ++ return 0; ++} ++ ++static const struct phy_ops ops = { ++ .init = rockchip_pcie_phy_init, ++ .exit = rockchip_pcie_phy_exit, ++ .power_on = rockchip_pcie_phy_power_on, ++ .power_off = rockchip_pcie_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct rockchip_pcie_data rk3399_pcie_data = { ++ .pcie_conf = 0xe220, ++ .pcie_status = 0xe2a4, ++ .pcie_laneoff = 0xe214, ++}; ++ ++static const struct of_device_id rockchip_pcie_phy_dt_ids[] = { ++ { ++ .compatible = "rockchip,rk3399-pcie-phy", ++ .data = &rk3399_pcie_data, ++ }, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids); ++ ++static int rockchip_pcie_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rockchip_pcie_phy *rk_phy; ++ struct phy *generic_phy; ++ struct phy_provider *phy_provider; ++ struct regmap *grf; ++ const struct of_device_id *of_id; ++ ++ grf = syscon_node_to_regmap(dev->parent->of_node); ++ if (IS_ERR(grf)) { ++ dev_err(dev, "Cannot find GRF syscon\n"); ++ return PTR_ERR(grf); ++ } ++ ++ rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); ++ if (!rk_phy) ++ return -ENOMEM; ++ ++ of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev); ++ if (!of_id) ++ return -EINVAL; ++ ++ rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data; ++ rk_phy->reg_base = grf; ++ ++ rk_phy->phy_rst = devm_reset_control_get(dev, "phy"); ++ if (IS_ERR(rk_phy->phy_rst)) { ++ if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER) ++ dev_err(dev, ++ "missing phy property for reset controller\n"); ++ return PTR_ERR(rk_phy->phy_rst); ++ } ++ ++ rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk"); ++ if (IS_ERR(rk_phy->clk_pciephy_ref)) { ++ dev_err(dev, "refclk not found.\n"); ++ return PTR_ERR(rk_phy->clk_pciephy_ref); ++ } ++ ++ generic_phy = devm_phy_create(dev, dev->of_node, &ops); ++ if (IS_ERR(generic_phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ return PTR_ERR(generic_phy); ++ } ++ ++ phy_set_drvdata(generic_phy, rk_phy); ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static struct platform_driver rockchip_pcie_driver = { ++ .probe = rockchip_pcie_phy_probe, ++ .driver = { ++ .name = "rockchip-pcie-phy", ++ .of_match_table = rockchip_pcie_phy_dt_ids, ++ }, ++}; ++ ++module_platform_driver(rockchip_pcie_driver); ++ ++MODULE_AUTHOR("Shawn Lin "); ++MODULE_DESCRIPTION("Rockchip PCIe PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +new file mode 100644 +index 000000000000..7cfb0f8995de +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -0,0 +1,1023 @@ ++/* ++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd ++ * Author: Chris Zhong ++ * Kever Yang ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * The ROCKCHIP Type-C PHY has two PLL clocks. The first PLL clock ++ * is used for USB3, the second PLL clock is used for DP. This Type-C PHY has ++ * 3 working modes: USB3 only mode, DP only mode, and USB3+DP mode. ++ * At USB3 only mode, both PLL clocks need to be initialized, this allows the ++ * PHY to switch mode between USB3 and USB3+DP, without disconnecting the USB ++ * device. ++ * In The DP only mode, only the DP PLL needs to be powered on, and the 4 lanes ++ * are all used for DP. ++ * ++ * This driver gets extcon cable state and property, then decides which mode to ++ * select: ++ * ++ * 1. USB3 only mode: ++ * EXTCON_USB or EXTCON_USB_HOST state is true, and ++ * EXTCON_PROP_USB_SS property is true. ++ * EXTCON_DISP_DP state is false. ++ * ++ * 2. DP only mode: ++ * EXTCON_DISP_DP state is true, and ++ * EXTCON_PROP_USB_SS property is false. ++ * If EXTCON_USB_HOST state is true, it is DP + USB2 mode, since the USB2 phy ++ * is a separate phy, so this case is still DP only mode. ++ * ++ * 3. USB3+DP mode: ++ * EXTCON_USB_HOST and EXTCON_DISP_DP are both true, and ++ * EXTCON_PROP_USB_SS property is true. ++ * ++ * This Type-C PHY driver supports normal and flip orientation. The orientation ++ * is reported by the EXTCON_PROP_USB_TYPEC_POLARITY property: true is flip ++ * orientation, false is normal orientation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define CMN_SSM_BANDGAP (0x21 << 2) ++#define CMN_SSM_BIAS (0x22 << 2) ++#define CMN_PLLSM0_PLLEN (0x29 << 2) ++#define CMN_PLLSM0_PLLPRE (0x2a << 2) ++#define CMN_PLLSM0_PLLVREF (0x2b << 2) ++#define CMN_PLLSM0_PLLLOCK (0x2c << 2) ++#define CMN_PLLSM1_PLLEN (0x31 << 2) ++#define CMN_PLLSM1_PLLPRE (0x32 << 2) ++#define CMN_PLLSM1_PLLVREF (0x33 << 2) ++#define CMN_PLLSM1_PLLLOCK (0x34 << 2) ++#define CMN_PLLSM1_USER_DEF_CTRL (0x37 << 2) ++#define CMN_ICAL_OVRD (0xc1 << 2) ++#define CMN_PLL0_VCOCAL_OVRD (0x83 << 2) ++#define CMN_PLL0_VCOCAL_INIT (0x84 << 2) ++#define CMN_PLL0_VCOCAL_ITER (0x85 << 2) ++#define CMN_PLL0_LOCK_REFCNT_START (0x90 << 2) ++#define CMN_PLL0_LOCK_PLLCNT_START (0x92 << 2) ++#define CMN_PLL0_LOCK_PLLCNT_THR (0x93 << 2) ++#define CMN_PLL0_INTDIV (0x94 << 2) ++#define CMN_PLL0_FRACDIV (0x95 << 2) ++#define CMN_PLL0_HIGH_THR (0x96 << 2) ++#define CMN_PLL0_DSM_DIAG (0x97 << 2) ++#define CMN_PLL0_SS_CTRL1 (0x98 << 2) ++#define CMN_PLL0_SS_CTRL2 (0x99 << 2) ++#define CMN_PLL1_VCOCAL_START (0xa1 << 2) ++#define CMN_PLL1_VCOCAL_OVRD (0xa3 << 2) ++#define CMN_PLL1_VCOCAL_INIT (0xa4 << 2) ++#define CMN_PLL1_VCOCAL_ITER (0xa5 << 2) ++#define CMN_PLL1_LOCK_REFCNT_START (0xb0 << 2) ++#define CMN_PLL1_LOCK_PLLCNT_START (0xb2 << 2) ++#define CMN_PLL1_LOCK_PLLCNT_THR (0xb3 << 2) ++#define CMN_PLL1_INTDIV (0xb4 << 2) ++#define CMN_PLL1_FRACDIV (0xb5 << 2) ++#define CMN_PLL1_HIGH_THR (0xb6 << 2) ++#define CMN_PLL1_DSM_DIAG (0xb7 << 2) ++#define CMN_PLL1_SS_CTRL1 (0xb8 << 2) ++#define CMN_PLL1_SS_CTRL2 (0xb9 << 2) ++#define CMN_RXCAL_OVRD (0xd1 << 2) ++#define CMN_TXPUCAL_CTRL (0xe0 << 2) ++#define CMN_TXPUCAL_OVRD (0xe1 << 2) ++#define CMN_TXPDCAL_OVRD (0xf1 << 2) ++#define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2) ++#define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2) ++#define CMN_DIAG_PLL0_OVRD (0x1c2 << 2) ++#define CMN_DIAG_PLL0_V2I_TUNE (0x1c5 << 2) ++#define CMN_DIAG_PLL0_CP_TUNE (0x1c6 << 2) ++#define CMN_DIAG_PLL0_LF_PROG (0x1c7 << 2) ++#define CMN_DIAG_PLL1_FBH_OVRD (0x1d0 << 2) ++#define CMN_DIAG_PLL1_FBL_OVRD (0x1d1 << 2) ++#define CMN_DIAG_PLL1_OVRD (0x1d2 << 2) ++#define CMN_DIAG_PLL1_V2I_TUNE (0x1d5 << 2) ++#define CMN_DIAG_PLL1_CP_TUNE (0x1d6 << 2) ++#define CMN_DIAG_PLL1_LF_PROG (0x1d7 << 2) ++#define CMN_DIAG_PLL1_PTATIS_TUNE1 (0x1d8 << 2) ++#define CMN_DIAG_PLL1_PTATIS_TUNE2 (0x1d9 << 2) ++#define CMN_DIAG_PLL1_INCLK_CTRL (0x1da << 2) ++#define CMN_DIAG_HSCLK_SEL (0x1e0 << 2) ++ ++#define XCVR_PSM_RCTRL(n) ((0x4001 | ((n) << 9)) << 2) ++#define XCVR_PSM_CAL_TMR(n) ((0x4002 | ((n) << 9)) << 2) ++#define XCVR_PSM_A0IN_TMR(n) ((0x4003 | ((n) << 9)) << 2) ++#define TX_TXCC_CAL_SCLR_MULT(n) ((0x4047 | ((n) << 9)) << 2) ++#define TX_TXCC_CPOST_MULT_00(n) ((0x404c | ((n) << 9)) << 2) ++#define TX_TXCC_CPOST_MULT_01(n) ((0x404d | ((n) << 9)) << 2) ++#define TX_TXCC_CPOST_MULT_10(n) ((0x404e | ((n) << 9)) << 2) ++#define TX_TXCC_CPOST_MULT_11(n) ((0x404f | ((n) << 9)) << 2) ++#define TX_TXCC_MGNFS_MULT_000(n) ((0x4050 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNFS_MULT_001(n) ((0x4051 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNFS_MULT_010(n) ((0x4052 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNFS_MULT_011(n) ((0x4053 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNFS_MULT_100(n) ((0x4054 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2) ++#define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2) ++#define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2) ++#define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2) ++#define TX_PSC_A0(n) ((0x4100 | ((n) << 9)) << 2) ++#define TX_PSC_A1(n) ((0x4101 | ((n) << 9)) << 2) ++#define TX_PSC_A2(n) ((0x4102 | ((n) << 9)) << 2) ++#define TX_PSC_A3(n) ((0x4103 | ((n) << 9)) << 2) ++#define TX_RCVDET_CTRL(n) ((0x4120 | ((n) << 9)) << 2) ++#define TX_RCVDET_EN_TMR(n) ((0x4122 | ((n) << 9)) << 2) ++#define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2) ++#define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2) ++#define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2) ++#define TX_ANA_CTRL_REG_1 (0x5020 << 2) ++#define TX_ANA_CTRL_REG_2 (0x5021 << 2) ++#define TXDA_COEFF_CALC_CTRL (0x5022 << 2) ++#define TX_DIG_CTRL_REG_2 (0x5024 << 2) ++#define TXDA_CYA_AUXDA_CYA (0x5025 << 2) ++#define TX_ANA_CTRL_REG_3 (0x5026 << 2) ++#define TX_ANA_CTRL_REG_4 (0x5027 << 2) ++#define TX_ANA_CTRL_REG_5 (0x5029 << 2) ++ ++#define RX_PSC_A0(n) ((0x8000 | ((n) << 9)) << 2) ++#define RX_PSC_A1(n) ((0x8001 | ((n) << 9)) << 2) ++#define RX_PSC_A2(n) ((0x8002 | ((n) << 9)) << 2) ++#define RX_PSC_A3(n) ((0x8003 | ((n) << 9)) << 2) ++#define RX_PSC_CAL(n) ((0x8006 | ((n) << 9)) << 2) ++#define RX_PSC_RDY(n) ((0x8007 | ((n) << 9)) << 2) ++#define RX_IQPI_ILL_CAL_OVRD (0x8023 << 2) ++#define RX_EPI_ILL_CAL_OVRD (0x8033 << 2) ++#define RX_SDCAL0_OVRD (0x8041 << 2) ++#define RX_SDCAL1_OVRD (0x8049 << 2) ++#define RX_SLC_INIT (0x806d << 2) ++#define RX_SLC_RUN (0x806e << 2) ++#define RX_CDRLF_CNFG2 (0x8081 << 2) ++#define RX_SIGDET_HL_FILT_TMR(n) ((0x8090 | ((n) << 9)) << 2) ++#define RX_SLC_IOP0_OVRD (0x8101 << 2) ++#define RX_SLC_IOP1_OVRD (0x8105 << 2) ++#define RX_SLC_QOP0_OVRD (0x8109 << 2) ++#define RX_SLC_QOP1_OVRD (0x810d << 2) ++#define RX_SLC_EOP0_OVRD (0x8111 << 2) ++#define RX_SLC_EOP1_OVRD (0x8115 << 2) ++#define RX_SLC_ION0_OVRD (0x8119 << 2) ++#define RX_SLC_ION1_OVRD (0x811d << 2) ++#define RX_SLC_QON0_OVRD (0x8121 << 2) ++#define RX_SLC_QON1_OVRD (0x8125 << 2) ++#define RX_SLC_EON0_OVRD (0x8129 << 2) ++#define RX_SLC_EON1_OVRD (0x812d << 2) ++#define RX_SLC_IEP0_OVRD (0x8131 << 2) ++#define RX_SLC_IEP1_OVRD (0x8135 << 2) ++#define RX_SLC_QEP0_OVRD (0x8139 << 2) ++#define RX_SLC_QEP1_OVRD (0x813d << 2) ++#define RX_SLC_EEP0_OVRD (0x8141 << 2) ++#define RX_SLC_EEP1_OVRD (0x8145 << 2) ++#define RX_SLC_IEN0_OVRD (0x8149 << 2) ++#define RX_SLC_IEN1_OVRD (0x814d << 2) ++#define RX_SLC_QEN0_OVRD (0x8151 << 2) ++#define RX_SLC_QEN1_OVRD (0x8155 << 2) ++#define RX_SLC_EEN0_OVRD (0x8159 << 2) ++#define RX_SLC_EEN1_OVRD (0x815d << 2) ++#define RX_REE_CTRL_DATA_MASK(n) ((0x81bb | ((n) << 9)) << 2) ++#define RX_DIAG_SIGDET_TUNE(n) ((0x81dc | ((n) << 9)) << 2) ++#define RX_DIAG_SC2C_DELAY (0x81e1 << 2) ++ ++#define PMA_LANE_CFG (0xc000 << 2) ++#define PIPE_CMN_CTRL1 (0xc001 << 2) ++#define PIPE_CMN_CTRL2 (0xc002 << 2) ++#define PIPE_COM_LOCK_CFG1 (0xc003 << 2) ++#define PIPE_COM_LOCK_CFG2 (0xc004 << 2) ++#define PIPE_RCV_DET_INH (0xc005 << 2) ++#define DP_MODE_CTL (0xc008 << 2) ++#define DP_CLK_CTL (0xc009 << 2) ++#define STS (0xc00F << 2) ++#define PHY_ISO_CMN_CTRL (0xc010 << 2) ++#define PHY_DP_TX_CTL (0xc408 << 2) ++#define PMA_CMN_CTRL1 (0xc800 << 2) ++#define PHY_PMA_ISO_CMN_CTRL (0xc810 << 2) ++#define PHY_ISOLATION_CTRL (0xc81f << 2) ++#define PHY_PMA_ISO_XCVR_CTRL(n) ((0xcc11 | ((n) << 6)) << 2) ++#define PHY_PMA_ISO_LINK_MODE(n) ((0xcc12 | ((n) << 6)) << 2) ++#define PHY_PMA_ISO_PWRST_CTRL(n) ((0xcc13 | ((n) << 6)) << 2) ++#define PHY_PMA_ISO_TX_DATA_LO(n) ((0xcc14 | ((n) << 6)) << 2) ++#define PHY_PMA_ISO_TX_DATA_HI(n) ((0xcc15 | ((n) << 6)) << 2) ++#define PHY_PMA_ISO_RX_DATA_LO(n) ((0xcc16 | ((n) << 6)) << 2) ++#define PHY_PMA_ISO_RX_DATA_HI(n) ((0xcc17 | ((n) << 6)) << 2) ++#define TX_BIST_CTRL(n) ((0x4140 | ((n) << 9)) << 2) ++#define TX_BIST_UDDWR(n) ((0x4141 | ((n) << 9)) << 2) ++ ++/* ++ * Selects which PLL clock will be driven on the analog high speed ++ * clock 0: PLL 0 div 1 ++ * clock 1: PLL 1 div 2 ++ */ ++#define CLK_PLL_CONFIG 0X30 ++#define CLK_PLL_MASK 0x33 ++ ++#define CMN_READY BIT(0) ++ ++#define DP_PLL_CLOCK_ENABLE BIT(2) ++#define DP_PLL_ENABLE BIT(0) ++#define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) ++#define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) ++#define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) ++ ++#define DP_MODE_A0 BIT(4) ++#define DP_MODE_A2 BIT(6) ++#define DP_MODE_ENTER_A0 0xc101 ++#define DP_MODE_ENTER_A2 0xc104 ++ ++#define PHY_MODE_SET_TIMEOUT 100000 ++ ++#define PIN_ASSIGN_C_E 0x51d9 ++#define PIN_ASSIGN_D_F 0x5100 ++ ++#define MODE_DISCONNECT 0 ++#define MODE_UFP_USB BIT(0) ++#define MODE_DFP_USB BIT(1) ++#define MODE_DFP_DP BIT(2) ++ ++struct usb3phy_reg { ++ u32 offset; ++ u32 enable_bit; ++ u32 write_enable; ++}; ++ ++struct rockchip_usb3phy_port_cfg { ++ struct usb3phy_reg typec_conn_dir; ++ struct usb3phy_reg usb3tousb2_en; ++ struct usb3phy_reg external_psm; ++ struct usb3phy_reg pipe_status; ++}; ++ ++struct rockchip_typec_phy { ++ struct device *dev; ++ void __iomem *base; ++ struct extcon_dev *extcon; ++ struct regmap *grf_regs; ++ struct clk *clk_core; ++ struct clk *clk_ref; ++ struct reset_control *uphy_rst; ++ struct reset_control *pipe_rst; ++ struct reset_control *tcphy_rst; ++ struct rockchip_usb3phy_port_cfg port_cfgs; ++ /* mutex to protect access to individual PHYs */ ++ struct mutex lock; ++ ++ bool flip; ++ u8 mode; ++}; ++ ++struct phy_reg { ++ u16 value; ++ u32 addr; ++}; ++ ++struct phy_reg usb3_pll_cfg[] = { ++ { 0xf0, CMN_PLL0_VCOCAL_INIT }, ++ { 0x18, CMN_PLL0_VCOCAL_ITER }, ++ { 0xd0, CMN_PLL0_INTDIV }, ++ { 0x4a4a, CMN_PLL0_FRACDIV }, ++ { 0x34, CMN_PLL0_HIGH_THR }, ++ { 0x1ee, CMN_PLL0_SS_CTRL1 }, ++ { 0x7f03, CMN_PLL0_SS_CTRL2 }, ++ { 0x20, CMN_PLL0_DSM_DIAG }, ++ { 0, CMN_DIAG_PLL0_OVRD }, ++ { 0, CMN_DIAG_PLL0_FBH_OVRD }, ++ { 0, CMN_DIAG_PLL0_FBL_OVRD }, ++ { 0x7, CMN_DIAG_PLL0_V2I_TUNE }, ++ { 0x45, CMN_DIAG_PLL0_CP_TUNE }, ++ { 0x8, CMN_DIAG_PLL0_LF_PROG }, ++}; ++ ++struct phy_reg dp_pll_cfg[] = { ++ { 0xf0, CMN_PLL1_VCOCAL_INIT }, ++ { 0x18, CMN_PLL1_VCOCAL_ITER }, ++ { 0x30b9, CMN_PLL1_VCOCAL_START }, ++ { 0x21c, CMN_PLL1_INTDIV }, ++ { 0, CMN_PLL1_FRACDIV }, ++ { 0x5, CMN_PLL1_HIGH_THR }, ++ { 0x35, CMN_PLL1_SS_CTRL1 }, ++ { 0x7f1e, CMN_PLL1_SS_CTRL2 }, ++ { 0x20, CMN_PLL1_DSM_DIAG }, ++ { 0, CMN_PLLSM1_USER_DEF_CTRL }, ++ { 0, CMN_DIAG_PLL1_OVRD }, ++ { 0, CMN_DIAG_PLL1_FBH_OVRD }, ++ { 0, CMN_DIAG_PLL1_FBL_OVRD }, ++ { 0x6, CMN_DIAG_PLL1_V2I_TUNE }, ++ { 0x45, CMN_DIAG_PLL1_CP_TUNE }, ++ { 0x8, CMN_DIAG_PLL1_LF_PROG }, ++ { 0x100, CMN_DIAG_PLL1_PTATIS_TUNE1 }, ++ { 0x7, CMN_DIAG_PLL1_PTATIS_TUNE2 }, ++ { 0x4, CMN_DIAG_PLL1_INCLK_CTRL }, ++}; ++ ++static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy) ++{ ++ u32 i, rdata; ++ ++ /* ++ * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent ++ * cmn_psm_clk_dig_div = 2, set the clk division to 2 ++ */ ++ writel(0x830, tcphy->base + PMA_CMN_CTRL1); ++ for (i = 0; i < 4; i++) { ++ /* ++ * The following PHY configuration assumes a 24 MHz reference ++ * clock. ++ */ ++ writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i)); ++ writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i)); ++ writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i)); ++ } ++ ++ rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL); ++ rdata &= ~CLK_PLL_MASK; ++ rdata |= CLK_PLL_CONFIG; ++ writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL); ++} ++ ++static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy) ++{ ++ u32 i; ++ ++ /* load the configuration of PLL0 */ ++ for (i = 0; i < ARRAY_SIZE(usb3_pll_cfg); i++) ++ writel(usb3_pll_cfg[i].value, ++ tcphy->base + usb3_pll_cfg[i].addr); ++} ++ ++static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy) ++{ ++ u32 i; ++ ++ /* set the default mode to RBR */ ++ writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR, ++ tcphy->base + DP_CLK_CTL); ++ ++ /* load the configuration of PLL1 */ ++ for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++) ++ writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr); ++} ++ ++static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) ++{ ++ writel(0x7799, tcphy->base + TX_PSC_A0(lane)); ++ writel(0x7798, tcphy->base + TX_PSC_A1(lane)); ++ writel(0x5098, tcphy->base + TX_PSC_A2(lane)); ++ writel(0x5098, tcphy->base + TX_PSC_A3(lane)); ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane)); ++ writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane)); ++} ++ ++static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) ++{ ++ writel(0xa6fd, tcphy->base + RX_PSC_A0(lane)); ++ writel(0xa6fd, tcphy->base + RX_PSC_A1(lane)); ++ writel(0xa410, tcphy->base + RX_PSC_A2(lane)); ++ writel(0x2410, tcphy->base + RX_PSC_A3(lane)); ++ writel(0x23ff, tcphy->base + RX_PSC_CAL(lane)); ++ writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane)); ++ writel(0x03e7, tcphy->base + RX_REE_CTRL_DATA_MASK(lane)); ++ writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane)); ++ writel(0x2010, tcphy->base + RX_PSC_RDY(lane)); ++ writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane)); ++} ++ ++static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) ++{ ++ u16 rdata; ++ ++ writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane)); ++ writel(0x6799, tcphy->base + TX_PSC_A0(lane)); ++ writel(0x6798, tcphy->base + TX_PSC_A1(lane)); ++ writel(0x98, tcphy->base + TX_PSC_A2(lane)); ++ writel(0x98, tcphy->base + TX_PSC_A3(lane)); ++ ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane)); ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane)); ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane)); ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane)); ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane)); ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane)); ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane)); ++ writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane)); ++ writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane)); ++ writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane)); ++ writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane)); ++ writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane)); ++ ++ writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane)); ++ writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane)); ++ ++ rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane)); ++ rdata = (rdata & 0x8fff) | 0x6000; ++ writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane)); ++} ++ ++static inline int property_enable(struct rockchip_typec_phy *tcphy, ++ const struct usb3phy_reg *reg, bool en) ++{ ++ u32 mask = 1 << reg->write_enable; ++ u32 val = en << reg->enable_bit; ++ ++ return regmap_write(tcphy->grf_regs, reg->offset, val | mask); ++} ++ ++static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) ++{ ++ u16 rdata, rdata2, val; ++ ++ /* disable txda_cal_latch_en for rewrite the calibration values */ ++ rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); ++ val = rdata & 0xdfff; ++ writel(val, tcphy->base + TX_ANA_CTRL_REG_1); ++ ++ /* ++ * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and ++ * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it ++ * works. ++ */ ++ rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2); ++ rdata = rdata & 0xffc0; ++ ++ rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL); ++ rdata2 = rdata2 & 0x3f; ++ ++ val = rdata | rdata2; ++ writel(val, tcphy->base + TX_DIG_CTRL_REG_2); ++ usleep_range(1000, 1050); ++ ++ /* ++ * Enable signal for latch that sample and holds calibration values. ++ * Activate this signal for 1 clock cycle to sample new calibration ++ * values. ++ */ ++ rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); ++ val = rdata | 0x2000; ++ writel(val, tcphy->base + TX_ANA_CTRL_REG_1); ++ usleep_range(150, 200); ++ ++ /* set TX Voltage Level and TX Deemphasis to 0 */ ++ writel(0, tcphy->base + PHY_DP_TX_CTL); ++ /* re-enable decap */ ++ writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2); ++ writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2); ++ writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1); ++ writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1); ++ ++ writel(0, tcphy->base + TX_ANA_CTRL_REG_5); ++ ++ /* ++ * Programs txda_drv_ldo_prog[15:0], Sets driver LDO ++ * voltage 16'h1001 for DP-AUX-TX and RX ++ */ ++ writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4); ++ ++ /* re-enables Bandgap reference for LDO */ ++ writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1); ++ writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1); ++ ++ /* ++ * re-enables the transmitter pre-driver, driver data selection MUX, ++ * and receiver detect circuits. ++ */ ++ writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2); ++ writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2); ++ ++ /* ++ * BIT 12: Controls auxda_polarity, which selects the polarity of the ++ * xcvr: ++ * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull ++ * down aux_m) ++ * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down ++ * aux_p) ++ */ ++ val = 0xa078; ++ if (!tcphy->flip) ++ val |= BIT(12); ++ writel(val, tcphy->base + TX_ANA_CTRL_REG_1); ++ ++ writel(0, tcphy->base + TX_ANA_CTRL_REG_3); ++ writel(0, tcphy->base + TX_ANA_CTRL_REG_4); ++ writel(0, tcphy->base + TX_ANA_CTRL_REG_5); ++ ++ /* ++ * Controls low_power_swing_en, set the voltage swing of the driver ++ * to 400mv. The values below are peak to peak (differential) values. ++ */ ++ writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL); ++ writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA); ++ ++ /* Controls tx_high_z_tm_en */ ++ val = readl(tcphy->base + TX_DIG_CTRL_REG_2); ++ val |= BIT(15); ++ writel(val, tcphy->base + TX_DIG_CTRL_REG_2); ++} ++ ++static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) ++{ ++ struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; ++ int ret, i; ++ u32 val; ++ ++ ret = clk_prepare_enable(tcphy->clk_core); ++ if (ret) { ++ dev_err(tcphy->dev, "Failed to prepare_enable core clock\n"); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(tcphy->clk_ref); ++ if (ret) { ++ dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n"); ++ goto err_clk_core; ++ } ++ ++ reset_control_deassert(tcphy->tcphy_rst); ++ ++ property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip); ++ ++ tcphy_cfg_24m(tcphy); ++ ++ if (mode == MODE_DFP_DP) { ++ tcphy_cfg_dp_pll(tcphy); ++ for (i = 0; i < 4; i++) ++ tcphy_dp_cfg_lane(tcphy, i); ++ ++ writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG); ++ } else { ++ tcphy_cfg_usb3_pll(tcphy); ++ tcphy_cfg_dp_pll(tcphy); ++ if (tcphy->flip) { ++ tcphy_tx_usb3_cfg_lane(tcphy, 3); ++ tcphy_rx_usb3_cfg_lane(tcphy, 2); ++ tcphy_dp_cfg_lane(tcphy, 0); ++ tcphy_dp_cfg_lane(tcphy, 1); ++ } else { ++ tcphy_tx_usb3_cfg_lane(tcphy, 0); ++ tcphy_rx_usb3_cfg_lane(tcphy, 1); ++ tcphy_dp_cfg_lane(tcphy, 2); ++ tcphy_dp_cfg_lane(tcphy, 3); ++ } ++ ++ writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG); ++ } ++ ++ writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); ++ ++ reset_control_deassert(tcphy->uphy_rst); ++ ++ ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1, ++ val, val & CMN_READY, 10, ++ PHY_MODE_SET_TIMEOUT); ++ if (ret < 0) { ++ dev_err(tcphy->dev, "wait pma ready timeout\n"); ++ ret = -ETIMEDOUT; ++ goto err_wait_pma; ++ } ++ ++ reset_control_deassert(tcphy->pipe_rst); ++ ++ return 0; ++ ++err_wait_pma: ++ reset_control_assert(tcphy->uphy_rst); ++ reset_control_assert(tcphy->tcphy_rst); ++ clk_disable_unprepare(tcphy->clk_ref); ++err_clk_core: ++ clk_disable_unprepare(tcphy->clk_core); ++ return ret; ++} ++ ++static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy) ++{ ++ reset_control_assert(tcphy->tcphy_rst); ++ reset_control_assert(tcphy->uphy_rst); ++ reset_control_assert(tcphy->pipe_rst); ++ clk_disable_unprepare(tcphy->clk_core); ++ clk_disable_unprepare(tcphy->clk_ref); ++} ++ ++static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) ++{ ++ struct extcon_dev *edev = tcphy->extcon; ++ union extcon_property_value property; ++ unsigned int id; ++ bool dfp, ufp, dp; ++ u8 mode; ++ int ret; ++ ++ ufp = extcon_get_state(edev, EXTCON_USB); ++ dfp = extcon_get_state(edev, EXTCON_USB_HOST); ++ dp = extcon_get_state(edev, EXTCON_DISP_DP); ++ ++ mode = MODE_DFP_USB; ++ id = EXTCON_USB_HOST; ++ ++ if (ufp) { ++ mode = MODE_UFP_USB; ++ id = EXTCON_USB; ++ } else if (dp) { ++ mode = MODE_DFP_DP; ++ id = EXTCON_DISP_DP; ++ ++ ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS, ++ &property); ++ if (ret) { ++ dev_err(tcphy->dev, "get superspeed property failed\n"); ++ return ret; ++ } ++ ++ if (property.intval) ++ mode |= MODE_DFP_USB; ++ } ++ ++ ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY, ++ &property); ++ if (ret) { ++ dev_err(tcphy->dev, "get polarity property failed\n"); ++ return ret; ++ } ++ ++ tcphy->flip = property.intval ? 1 : 0; ++ ++ return mode; ++} ++ ++static int rockchip_usb3_phy_power_on(struct phy *phy) ++{ ++ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); ++ struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; ++ const struct usb3phy_reg *reg = &cfg->pipe_status; ++ int timeout, new_mode, ret = 0; ++ u32 val; ++ ++ mutex_lock(&tcphy->lock); ++ ++ new_mode = tcphy_get_mode(tcphy); ++ if (new_mode < 0) { ++ ret = new_mode; ++ goto unlock_ret; ++ } ++ ++ /* DP-only mode; fall back to USB2 */ ++ if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) ++ goto unlock_ret; ++ ++ if (tcphy->mode == new_mode) ++ goto unlock_ret; ++ ++ if (tcphy->mode == MODE_DISCONNECT) ++ tcphy_phy_init(tcphy, new_mode); ++ ++ /* wait TCPHY for pipe ready */ ++ for (timeout = 0; timeout < 100; timeout++) { ++ regmap_read(tcphy->grf_regs, reg->offset, &val); ++ if (!(val & BIT(reg->enable_bit))) { ++ tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB); ++ goto unlock_ret; ++ } ++ usleep_range(10, 20); ++ } ++ ++ if (tcphy->mode == MODE_DISCONNECT) ++ tcphy_phy_deinit(tcphy); ++ ++ ret = -ETIMEDOUT; ++ ++unlock_ret: ++ mutex_unlock(&tcphy->lock); ++ return ret; ++} ++ ++static int rockchip_usb3_phy_power_off(struct phy *phy) ++{ ++ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); ++ ++ mutex_lock(&tcphy->lock); ++ ++ if (tcphy->mode == MODE_DISCONNECT) ++ goto unlock; ++ ++ tcphy->mode &= ~(MODE_UFP_USB | MODE_DFP_USB); ++ if (tcphy->mode == MODE_DISCONNECT) ++ tcphy_phy_deinit(tcphy); ++ ++unlock: ++ mutex_unlock(&tcphy->lock); ++ return 0; ++} ++ ++static const struct phy_ops rockchip_usb3_phy_ops = { ++ .power_on = rockchip_usb3_phy_power_on, ++ .power_off = rockchip_usb3_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int rockchip_dp_phy_power_on(struct phy *phy) ++{ ++ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); ++ int new_mode, ret = 0; ++ u32 val; ++ ++ mutex_lock(&tcphy->lock); ++ ++ new_mode = tcphy_get_mode(tcphy); ++ if (new_mode < 0) { ++ ret = new_mode; ++ goto unlock_ret; ++ } ++ ++ if (!(new_mode & MODE_DFP_DP)) { ++ ret = -ENODEV; ++ goto unlock_ret; ++ } ++ ++ if (tcphy->mode == new_mode) ++ goto unlock_ret; ++ ++ /* ++ * If the PHY has been power on, but the mode is not DP only mode, ++ * re-init the PHY for setting all of 4 lanes to DP. ++ */ ++ if (new_mode == MODE_DFP_DP && tcphy->mode != MODE_DISCONNECT) { ++ tcphy_phy_deinit(tcphy); ++ tcphy_phy_init(tcphy, new_mode); ++ } else if (tcphy->mode == MODE_DISCONNECT) { ++ tcphy_phy_init(tcphy, new_mode); ++ } ++ ++ ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, ++ val, val & DP_MODE_A2, 1000, ++ PHY_MODE_SET_TIMEOUT); ++ if (ret < 0) { ++ dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n"); ++ goto power_on_finish; ++ } ++ ++ tcphy_dp_aux_calibration(tcphy); ++ ++ writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL); ++ ++ ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, ++ val, val & DP_MODE_A0, 1000, ++ PHY_MODE_SET_TIMEOUT); ++ if (ret < 0) { ++ writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); ++ dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n"); ++ goto power_on_finish; ++ } ++ ++ tcphy->mode |= MODE_DFP_DP; ++ ++power_on_finish: ++ if (tcphy->mode == MODE_DISCONNECT) ++ tcphy_phy_deinit(tcphy); ++unlock_ret: ++ mutex_unlock(&tcphy->lock); ++ return ret; ++} ++ ++static int rockchip_dp_phy_power_off(struct phy *phy) ++{ ++ struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); ++ ++ mutex_lock(&tcphy->lock); ++ ++ if (tcphy->mode == MODE_DISCONNECT) ++ goto unlock; ++ ++ tcphy->mode &= ~MODE_DFP_DP; ++ ++ writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); ++ ++ if (tcphy->mode == MODE_DISCONNECT) ++ tcphy_phy_deinit(tcphy); ++ ++unlock: ++ mutex_unlock(&tcphy->lock); ++ return 0; ++} ++ ++static const struct phy_ops rockchip_dp_phy_ops = { ++ .power_on = rockchip_dp_phy_power_on, ++ .power_off = rockchip_dp_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int tcphy_get_param(struct device *dev, ++ struct usb3phy_reg *reg, ++ const char *name) ++{ ++ u32 buffer[3]; ++ int ret; ++ ++ ret = of_property_read_u32_array(dev->of_node, name, buffer, 3); ++ if (ret) { ++ dev_err(dev, "Can not parse %s\n", name); ++ return ret; ++ } ++ ++ reg->offset = buffer[0]; ++ reg->enable_bit = buffer[1]; ++ reg->write_enable = buffer[2]; ++ return 0; ++} ++ ++static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy, ++ struct device *dev) ++{ ++ struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; ++ int ret; ++ ++ ret = tcphy_get_param(dev, &cfg->typec_conn_dir, ++ "rockchip,typec-conn-dir"); ++ if (ret) ++ return ret; ++ ++ ret = tcphy_get_param(dev, &cfg->usb3tousb2_en, ++ "rockchip,usb3tousb2-en"); ++ if (ret) ++ return ret; ++ ++ ret = tcphy_get_param(dev, &cfg->external_psm, ++ "rockchip,external-psm"); ++ if (ret) ++ return ret; ++ ++ ret = tcphy_get_param(dev, &cfg->pipe_status, ++ "rockchip,pipe-status"); ++ if (ret) ++ return ret; ++ ++ tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "rockchip,grf"); ++ if (IS_ERR(tcphy->grf_regs)) { ++ dev_err(dev, "could not find grf dt node\n"); ++ return PTR_ERR(tcphy->grf_regs); ++ } ++ ++ tcphy->clk_core = devm_clk_get(dev, "tcpdcore"); ++ if (IS_ERR(tcphy->clk_core)) { ++ dev_err(dev, "could not get uphy core clock\n"); ++ return PTR_ERR(tcphy->clk_core); ++ } ++ ++ tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref"); ++ if (IS_ERR(tcphy->clk_ref)) { ++ dev_err(dev, "could not get uphy ref clock\n"); ++ return PTR_ERR(tcphy->clk_ref); ++ } ++ ++ tcphy->uphy_rst = devm_reset_control_get(dev, "uphy"); ++ if (IS_ERR(tcphy->uphy_rst)) { ++ dev_err(dev, "no uphy_rst reset control found\n"); ++ return PTR_ERR(tcphy->uphy_rst); ++ } ++ ++ tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe"); ++ if (IS_ERR(tcphy->pipe_rst)) { ++ dev_err(dev, "no pipe_rst reset control found\n"); ++ return PTR_ERR(tcphy->pipe_rst); ++ } ++ ++ tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy"); ++ if (IS_ERR(tcphy->tcphy_rst)) { ++ dev_err(dev, "no tcphy_rst reset control found\n"); ++ return PTR_ERR(tcphy->tcphy_rst); ++ } ++ ++ return 0; ++} ++ ++static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) ++{ ++ struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; ++ ++ reset_control_assert(tcphy->tcphy_rst); ++ reset_control_assert(tcphy->uphy_rst); ++ reset_control_assert(tcphy->pipe_rst); ++ ++ /* select external psm clock */ ++ property_enable(tcphy, &cfg->external_psm, 1); ++ property_enable(tcphy, &cfg->usb3tousb2_en, 0); ++ ++ tcphy->mode = MODE_DISCONNECT; ++} ++ ++static int rockchip_typec_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct device_node *child_np; ++ struct rockchip_typec_phy *tcphy; ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ int ret; ++ ++ tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL); ++ if (!tcphy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ tcphy->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(tcphy->base)) ++ return PTR_ERR(tcphy->base); ++ ++ ret = tcphy_parse_dt(tcphy, dev); ++ if (ret) ++ return ret; ++ ++ tcphy->dev = dev; ++ platform_set_drvdata(pdev, tcphy); ++ mutex_init(&tcphy->lock); ++ ++ typec_phy_pre_init(tcphy); ++ ++ tcphy->extcon = extcon_get_edev_by_phandle(dev, 0); ++ if (IS_ERR(tcphy->extcon)) { ++ if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER) ++ dev_err(dev, "Invalid or missing extcon\n"); ++ return PTR_ERR(tcphy->extcon); ++ } ++ ++ pm_runtime_enable(dev); ++ ++ for_each_available_child_of_node(np, child_np) { ++ struct phy *phy; ++ ++ if (!of_node_cmp(child_np->name, "dp-port")) ++ phy = devm_phy_create(dev, child_np, ++ &rockchip_dp_phy_ops); ++ else if (!of_node_cmp(child_np->name, "usb3-port")) ++ phy = devm_phy_create(dev, child_np, ++ &rockchip_usb3_phy_ops); ++ else ++ continue; ++ ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create phy: %s\n", ++ child_np->name); ++ return PTR_ERR(phy); ++ } ++ ++ phy_set_drvdata(phy, tcphy); ++ } ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ dev_err(dev, "Failed to register phy provider\n"); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static int rockchip_typec_phy_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id rockchip_typec_phy_dt_ids[] = { ++ { .compatible = "rockchip,rk3399-typec-phy" }, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids); ++ ++static struct platform_driver rockchip_typec_phy_driver = { ++ .probe = rockchip_typec_phy_probe, ++ .remove = rockchip_typec_phy_remove, ++ .driver = { ++ .name = "rockchip-typec-phy", ++ .of_match_table = rockchip_typec_phy_dt_ids, ++ }, ++}; ++ ++module_platform_driver(rockchip_typec_phy_driver); ++ ++MODULE_AUTHOR("Chris Zhong "); ++MODULE_AUTHOR("Kever Yang "); ++MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/rockchip/phy-rockchip-usb.c b/drivers/phy/rockchip/phy-rockchip-usb.c +new file mode 100644 +index 000000000000..3378eeb7a562 +--- /dev/null ++++ b/drivers/phy/rockchip/phy-rockchip-usb.c +@@ -0,0 +1,540 @@ ++/* ++ * Rockchip usb PHY driver ++ * ++ * Copyright (C) 2014 Yunzhi Li ++ * Copyright (C) 2014 ROCKCHIP, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int enable_usb_uart; ++ ++#define HIWORD_UPDATE(val, mask) \ ++ ((val) | (mask) << 16) ++ ++#define UOC_CON0_SIDDQ BIT(13) ++ ++struct rockchip_usb_phys { ++ int reg; ++ const char *pll_name; ++}; ++ ++struct rockchip_usb_phy_base; ++struct rockchip_usb_phy_pdata { ++ struct rockchip_usb_phys *phys; ++ int (*init_usb_uart)(struct regmap *grf); ++ int usb_uart_phy; ++}; ++ ++struct rockchip_usb_phy_base { ++ struct device *dev; ++ struct regmap *reg_base; ++ const struct rockchip_usb_phy_pdata *pdata; ++}; ++ ++struct rockchip_usb_phy { ++ struct rockchip_usb_phy_base *base; ++ struct device_node *np; ++ unsigned int reg_offset; ++ struct clk *clk; ++ struct clk *clk480m; ++ struct clk_hw clk480m_hw; ++ struct phy *phy; ++ bool uart_enabled; ++ struct reset_control *reset; ++ struct regulator *vbus; ++}; ++ ++static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, ++ bool siddq) ++{ ++ u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ); ++ ++ return regmap_write(phy->base->reg_base, phy->reg_offset, val); ++} ++ ++static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ return 480000000; ++} ++ ++static void rockchip_usb_phy480m_disable(struct clk_hw *hw) ++{ ++ struct rockchip_usb_phy *phy = container_of(hw, ++ struct rockchip_usb_phy, ++ clk480m_hw); ++ ++ if (phy->vbus) ++ regulator_disable(phy->vbus); ++ ++ /* Power down usb phy analog blocks by set siddq 1 */ ++ rockchip_usb_phy_power(phy, 1); ++} ++ ++static int rockchip_usb_phy480m_enable(struct clk_hw *hw) ++{ ++ struct rockchip_usb_phy *phy = container_of(hw, ++ struct rockchip_usb_phy, ++ clk480m_hw); ++ ++ /* Power up usb phy analog blocks by set siddq 0 */ ++ return rockchip_usb_phy_power(phy, 0); ++} ++ ++static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw) ++{ ++ struct rockchip_usb_phy *phy = container_of(hw, ++ struct rockchip_usb_phy, ++ clk480m_hw); ++ int ret; ++ u32 val; ++ ++ ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val); ++ if (ret < 0) ++ return ret; ++ ++ return (val & UOC_CON0_SIDDQ) ? 0 : 1; ++} ++ ++static const struct clk_ops rockchip_usb_phy480m_ops = { ++ .enable = rockchip_usb_phy480m_enable, ++ .disable = rockchip_usb_phy480m_disable, ++ .is_enabled = rockchip_usb_phy480m_is_enabled, ++ .recalc_rate = rockchip_usb_phy480m_recalc_rate, ++}; ++ ++static int rockchip_usb_phy_power_off(struct phy *_phy) ++{ ++ struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); ++ ++ if (phy->uart_enabled) ++ return -EBUSY; ++ ++ clk_disable_unprepare(phy->clk480m); ++ ++ return 0; ++} ++ ++static int rockchip_usb_phy_power_on(struct phy *_phy) ++{ ++ struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); ++ ++ if (phy->uart_enabled) ++ return -EBUSY; ++ ++ if (phy->vbus) { ++ int ret; ++ ++ ret = regulator_enable(phy->vbus); ++ if (ret) ++ return ret; ++ } ++ ++ return clk_prepare_enable(phy->clk480m); ++} ++ ++static int rockchip_usb_phy_reset(struct phy *_phy) ++{ ++ struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); ++ ++ if (phy->reset) { ++ reset_control_assert(phy->reset); ++ udelay(10); ++ reset_control_deassert(phy->reset); ++ } ++ ++ return 0; ++} ++ ++static const struct phy_ops ops = { ++ .power_on = rockchip_usb_phy_power_on, ++ .power_off = rockchip_usb_phy_power_off, ++ .reset = rockchip_usb_phy_reset, ++ .owner = THIS_MODULE, ++}; ++ ++static void rockchip_usb_phy_action(void *data) ++{ ++ struct rockchip_usb_phy *rk_phy = data; ++ ++ if (!rk_phy->uart_enabled) { ++ of_clk_del_provider(rk_phy->np); ++ clk_unregister(rk_phy->clk480m); ++ } ++ ++ if (rk_phy->clk) ++ clk_put(rk_phy->clk); ++} ++ ++static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, ++ struct device_node *child) ++{ ++ struct rockchip_usb_phy *rk_phy; ++ unsigned int reg_offset; ++ const char *clk_name; ++ struct clk_init_data init; ++ int err, i; ++ ++ rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL); ++ if (!rk_phy) ++ return -ENOMEM; ++ ++ rk_phy->base = base; ++ rk_phy->np = child; ++ ++ if (of_property_read_u32(child, "reg", ®_offset)) { ++ dev_err(base->dev, "missing reg property in node %s\n", ++ child->name); ++ return -EINVAL; ++ } ++ ++ rk_phy->reset = of_reset_control_get(child, "phy-reset"); ++ if (IS_ERR(rk_phy->reset)) ++ rk_phy->reset = NULL; ++ ++ rk_phy->reg_offset = reg_offset; ++ ++ rk_phy->clk = of_clk_get_by_name(child, "phyclk"); ++ if (IS_ERR(rk_phy->clk)) ++ rk_phy->clk = NULL; ++ ++ i = 0; ++ init.name = NULL; ++ while (base->pdata->phys[i].reg) { ++ if (base->pdata->phys[i].reg == reg_offset) { ++ init.name = base->pdata->phys[i].pll_name; ++ break; ++ } ++ i++; ++ } ++ ++ if (!init.name) { ++ dev_err(base->dev, "phy data not found\n"); ++ return -EINVAL; ++ } ++ ++ if (enable_usb_uart && base->pdata->usb_uart_phy == i) { ++ dev_dbg(base->dev, "phy%d used as uart output\n", i); ++ rk_phy->uart_enabled = true; ++ } else { ++ if (rk_phy->clk) { ++ clk_name = __clk_get_name(rk_phy->clk); ++ init.flags = 0; ++ init.parent_names = &clk_name; ++ init.num_parents = 1; ++ } else { ++ init.flags = 0; ++ init.parent_names = NULL; ++ init.num_parents = 0; ++ } ++ ++ init.ops = &rockchip_usb_phy480m_ops; ++ rk_phy->clk480m_hw.init = &init; ++ ++ rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); ++ if (IS_ERR(rk_phy->clk480m)) { ++ err = PTR_ERR(rk_phy->clk480m); ++ goto err_clk; ++ } ++ ++ err = of_clk_add_provider(child, of_clk_src_simple_get, ++ rk_phy->clk480m); ++ if (err < 0) ++ goto err_clk_prov; ++ } ++ ++ err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action, ++ rk_phy); ++ if (err) ++ return err; ++ ++ rk_phy->phy = devm_phy_create(base->dev, child, &ops); ++ if (IS_ERR(rk_phy->phy)) { ++ dev_err(base->dev, "failed to create PHY\n"); ++ return PTR_ERR(rk_phy->phy); ++ } ++ phy_set_drvdata(rk_phy->phy, rk_phy); ++ ++ rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus"); ++ if (IS_ERR(rk_phy->vbus)) { ++ if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER) ++ return PTR_ERR(rk_phy->vbus); ++ rk_phy->vbus = NULL; ++ } ++ ++ /* ++ * When acting as uart-pipe, just keep clock on otherwise ++ * only power up usb phy when it use, so disable it when init ++ */ ++ if (rk_phy->uart_enabled) ++ return clk_prepare_enable(rk_phy->clk); ++ else ++ return rockchip_usb_phy_power(rk_phy, 1); ++ ++err_clk_prov: ++ if (!rk_phy->uart_enabled) ++ clk_unregister(rk_phy->clk480m); ++err_clk: ++ if (rk_phy->clk) ++ clk_put(rk_phy->clk); ++ return err; ++} ++ ++static const struct rockchip_usb_phy_pdata rk3066a_pdata = { ++ .phys = (struct rockchip_usb_phys[]){ ++ { .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" }, ++ { .reg = 0x188, .pll_name = "sclk_otgphy1_480m" }, ++ { /* sentinel */ } ++ }, ++}; ++ ++static const struct rockchip_usb_phy_pdata rk3188_pdata = { ++ .phys = (struct rockchip_usb_phys[]){ ++ { .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" }, ++ { .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" }, ++ { /* sentinel */ } ++ }, ++}; ++ ++#define RK3288_UOC0_CON0 0x320 ++#define RK3288_UOC0_CON0_COMMON_ON_N BIT(0) ++#define RK3288_UOC0_CON0_DISABLE BIT(4) ++ ++#define RK3288_UOC0_CON2 0x328 ++#define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2) ++ ++#define RK3288_UOC0_CON3 0x32c ++#define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0) ++#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1) ++#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1) ++#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3) ++#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3) ++#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5) ++#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6) ++#define RK3288_UOC0_CON3_BYPASSSEL BIT(7) ++ ++/* ++ * Enable the bypass of uart2 data through the otg usb phy. ++ * Original description in the TRM. ++ * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1. ++ * 2. Disable the pull-up resistance on the D+ line by setting ++ * OPMODE0[1:0] to 2’b01. ++ * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend ++ * mode, set COMMONONN to 1’b1. ++ * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0. ++ * 5. Set BYPASSSEL0 to 1’b1. ++ * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0. ++ * To receive data, monitor FSVPLUS0. ++ * ++ * The actual code in the vendor kernel does some things differently. ++ */ ++static int __init rk3288_init_usb_uart(struct regmap *grf) ++{ ++ u32 val; ++ int ret; ++ ++ /* ++ * COMMON_ON and DISABLE settings are described in the TRM, ++ * but were not present in the original code. ++ * Also disable the analog phy components to save power. ++ */ ++ val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N ++ | RK3288_UOC0_CON0_DISABLE ++ | UOC_CON0_SIDDQ, ++ RK3288_UOC0_CON0_COMMON_ON_N ++ | RK3288_UOC0_CON0_DISABLE ++ | UOC_CON0_SIDDQ); ++ ret = regmap_write(grf, RK3288_UOC0_CON0, val); ++ if (ret) ++ return ret; ++ ++ val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL, ++ RK3288_UOC0_CON2_SOFT_CON_SEL); ++ ret = regmap_write(grf, RK3288_UOC0_CON2, val); ++ if (ret) ++ return ret; ++ ++ val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING ++ | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC ++ | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED, ++ RK3288_UOC0_CON3_UTMI_SUSPENDN ++ | RK3288_UOC0_CON3_UTMI_OPMODE_MASK ++ | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK ++ | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED); ++ ret = regmap_write(grf, RK3288_UOC0_CON3, val); ++ if (ret) ++ return ret; ++ ++ val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL ++ | RK3288_UOC0_CON3_BYPASSDMEN, ++ RK3288_UOC0_CON3_BYPASSSEL ++ | RK3288_UOC0_CON3_BYPASSDMEN); ++ ret = regmap_write(grf, RK3288_UOC0_CON3, val); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static const struct rockchip_usb_phy_pdata rk3288_pdata = { ++ .phys = (struct rockchip_usb_phys[]){ ++ { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, ++ { .reg = 0x334, .pll_name = "sclk_otgphy1_480m" }, ++ { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, ++ { /* sentinel */ } ++ }, ++ .init_usb_uart = rk3288_init_usb_uart, ++ .usb_uart_phy = 0, ++}; ++ ++static int rockchip_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rockchip_usb_phy_base *phy_base; ++ struct phy_provider *phy_provider; ++ const struct of_device_id *match; ++ struct device_node *child; ++ int err; ++ ++ phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL); ++ if (!phy_base) ++ return -ENOMEM; ++ ++ match = of_match_device(dev->driver->of_match_table, dev); ++ if (!match || !match->data) { ++ dev_err(dev, "missing phy data\n"); ++ return -EINVAL; ++ } ++ ++ phy_base->pdata = match->data; ++ ++ phy_base->dev = dev; ++ phy_base->reg_base = ERR_PTR(-ENODEV); ++ if (dev->parent && dev->parent->of_node) ++ phy_base->reg_base = syscon_node_to_regmap( ++ dev->parent->of_node); ++ if (IS_ERR(phy_base->reg_base)) ++ phy_base->reg_base = syscon_regmap_lookup_by_phandle( ++ dev->of_node, "rockchip,grf"); ++ if (IS_ERR(phy_base->reg_base)) { ++ dev_err(&pdev->dev, "Missing rockchip,grf property\n"); ++ return PTR_ERR(phy_base->reg_base); ++ } ++ ++ for_each_available_child_of_node(dev->of_node, child) { ++ err = rockchip_usb_phy_init(phy_base, child); ++ if (err) { ++ of_node_put(child); ++ return err; ++ } ++ } ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id rockchip_usb_phy_dt_ids[] = { ++ { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata }, ++ { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata }, ++ { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata }, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids); ++ ++static struct platform_driver rockchip_usb_driver = { ++ .probe = rockchip_usb_phy_probe, ++ .driver = { ++ .name = "rockchip-usb-phy", ++ .of_match_table = rockchip_usb_phy_dt_ids, ++ }, ++}; ++ ++module_platform_driver(rockchip_usb_driver); ++ ++#ifndef MODULE ++static int __init rockchip_init_usb_uart(void) ++{ ++ const struct of_device_id *match; ++ const struct rockchip_usb_phy_pdata *data; ++ struct device_node *np; ++ struct regmap *grf; ++ int ret; ++ ++ if (!enable_usb_uart) ++ return 0; ++ ++ np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids, ++ &match); ++ if (!np) { ++ pr_err("%s: failed to find usbphy node\n", __func__); ++ return -ENOTSUPP; ++ } ++ ++ pr_debug("%s: using settings for %s\n", __func__, match->compatible); ++ data = match->data; ++ ++ if (!data->init_usb_uart) { ++ pr_err("%s: usb-uart not available on %s\n", ++ __func__, match->compatible); ++ return -ENOTSUPP; ++ } ++ ++ grf = ERR_PTR(-ENODEV); ++ if (np->parent) ++ grf = syscon_node_to_regmap(np->parent); ++ if (IS_ERR(grf)) ++ grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); ++ if (IS_ERR(grf)) { ++ pr_err("%s: Missing rockchip,grf property, %lu\n", ++ __func__, PTR_ERR(grf)); ++ return PTR_ERR(grf); ++ } ++ ++ ret = data->init_usb_uart(grf); ++ if (ret) { ++ pr_err("%s: could not init usb_uart, %d\n", __func__, ret); ++ enable_usb_uart = 0; ++ return ret; ++ } ++ ++ return 0; ++} ++early_initcall(rockchip_init_usb_uart); ++ ++static int __init rockchip_usb_uart(char *buf) ++{ ++ enable_usb_uart = true; ++ return 0; ++} ++early_param("rockchip.usb_uart", rockchip_usb_uart); ++#endif ++ ++MODULE_AUTHOR("Yunzhi Li "); ++MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig +new file mode 100644 +index 000000000000..b7e0645a7bd9 +--- /dev/null ++++ b/drivers/phy/samsung/Kconfig +@@ -0,0 +1,95 @@ ++# ++# Phy drivers for Samsung platforms ++# ++config PHY_EXYNOS_DP_VIDEO ++ tristate "EXYNOS SoC series Display Port PHY driver" ++ depends on OF ++ depends on ARCH_EXYNOS || COMPILE_TEST ++ default ARCH_EXYNOS ++ select GENERIC_PHY ++ help ++ Support for Display Port PHY found on Samsung EXYNOS SoCs. ++ ++config PHY_EXYNOS_MIPI_VIDEO ++ tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" ++ depends on HAS_IOMEM ++ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST ++ select GENERIC_PHY ++ default y if ARCH_S5PV210 || ARCH_EXYNOS ++ help ++ Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P ++ and EXYNOS SoCs. ++ ++config PHY_EXYNOS_PCIE ++ bool "Exynos PCIe PHY driver" ++ depends on OF && (ARCH_EXYNOS || COMPILE_TEST) ++ select GENERIC_PHY ++ help ++ Enable PCIe PHY support for Exynos SoC series. ++ This driver provides PHY interface for Exynos PCIe controller. ++ ++config PHY_SAMSUNG_USB2 ++ tristate "Samsung USB 2.0 PHY driver" ++ depends on HAS_IOMEM ++ depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 ++ select GENERIC_PHY ++ select MFD_SYSCON ++ default ARCH_EXYNOS ++ help ++ Enable this to support the Samsung USB 2.0 PHY driver for Samsung ++ SoCs. This driver provides the interface for USB 2.0 PHY. Support ++ for particular PHYs will be enabled based on the SoC type in addition ++ to this driver. ++ ++config PHY_EXYNOS4210_USB2 ++ bool ++ depends on PHY_SAMSUNG_USB2 ++ default CPU_EXYNOS4210 ++ ++config PHY_EXYNOS4X12_USB2 ++ bool ++ depends on PHY_SAMSUNG_USB2 ++ default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412 ++ ++config PHY_EXYNOS5250_USB2 ++ bool ++ depends on PHY_SAMSUNG_USB2 ++ default SOC_EXYNOS5250 || SOC_EXYNOS5420 ++ ++config PHY_S5PV210_USB2 ++ bool "Support for S5PV210" ++ depends on PHY_SAMSUNG_USB2 ++ depends on ARCH_S5PV210 ++ help ++ Enable USB PHY support for S5PV210. This option requires that Samsung ++ USB 2.0 PHY driver is enabled and means that support for this ++ particular SoC is compiled in the driver. In case of S5PV210 two phys ++ are available - device and host. ++ ++config PHY_EXYNOS5_USBDRD ++ tristate "Exynos5 SoC series USB DRD PHY driver" ++ depends on ARCH_EXYNOS && OF ++ depends on HAS_IOMEM ++ depends on USB_DWC3_EXYNOS ++ select GENERIC_PHY ++ select MFD_SYSCON ++ default y ++ help ++ Enable USB DRD PHY support for Exynos 5 SoC series. ++ This driver provides PHY interface for USB 3.0 DRD controller ++ present on Exynos5 SoC series. ++ ++config PHY_EXYNOS5250_SATA ++ tristate "Exynos5250 Sata SerDes/PHY driver" ++ depends on SOC_EXYNOS5250 ++ depends on HAS_IOMEM ++ depends on OF ++ select GENERIC_PHY ++ select I2C ++ select I2C_S3C2410 ++ select MFD_SYSCON ++ help ++ Enable this to support SATA SerDes/Phy found on Samsung's ++ Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s, ++ SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host ++ port to accept one SATA device. +diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile +new file mode 100644 +index 000000000000..20d7f2424772 +--- /dev/null ++++ b/drivers/phy/samsung/Makefile +@@ -0,0 +1,11 @@ ++obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o ++obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o ++obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o ++obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o ++phy-exynos-usb2-y += phy-samsung-usb2.o ++phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o ++phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o ++phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o ++phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o ++obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o ++obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o +diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c +new file mode 100644 +index 000000000000..bb3279dbf88c +--- /dev/null ++++ b/drivers/phy/samsung/phy-exynos-dp-video.c +@@ -0,0 +1,122 @@ ++/* ++ * Samsung EXYNOS SoC series Display Port PHY driver ++ * ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Author: Jingoo Han ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct exynos_dp_video_phy_drvdata { ++ u32 phy_ctrl_offset; ++}; ++ ++struct exynos_dp_video_phy { ++ struct regmap *regs; ++ const struct exynos_dp_video_phy_drvdata *drvdata; ++}; ++ ++static int exynos_dp_video_phy_power_on(struct phy *phy) ++{ ++ struct exynos_dp_video_phy *state = phy_get_drvdata(phy); ++ ++ /* Disable power isolation on DP-PHY */ ++ return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, ++ EXYNOS4_PHY_ENABLE, EXYNOS4_PHY_ENABLE); ++} ++ ++static int exynos_dp_video_phy_power_off(struct phy *phy) ++{ ++ struct exynos_dp_video_phy *state = phy_get_drvdata(phy); ++ ++ /* Enable power isolation on DP-PHY */ ++ return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, ++ EXYNOS4_PHY_ENABLE, 0); ++} ++ ++static const struct phy_ops exynos_dp_video_phy_ops = { ++ .power_on = exynos_dp_video_phy_power_on, ++ .power_off = exynos_dp_video_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct exynos_dp_video_phy_drvdata exynos5250_dp_video_phy = { ++ .phy_ctrl_offset = EXYNOS5_DPTX_PHY_CONTROL, ++}; ++ ++static const struct exynos_dp_video_phy_drvdata exynos5420_dp_video_phy = { ++ .phy_ctrl_offset = EXYNOS5420_DPTX_PHY_CONTROL, ++}; ++ ++static const struct of_device_id exynos_dp_video_phy_of_match[] = { ++ { ++ .compatible = "samsung,exynos5250-dp-video-phy", ++ .data = &exynos5250_dp_video_phy, ++ }, { ++ .compatible = "samsung,exynos5420-dp-video-phy", ++ .data = &exynos5420_dp_video_phy, ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match); ++ ++static int exynos_dp_video_phy_probe(struct platform_device *pdev) ++{ ++ struct exynos_dp_video_phy *state; ++ struct device *dev = &pdev->dev; ++ const struct of_device_id *match; ++ struct phy_provider *phy_provider; ++ struct phy *phy; ++ ++ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ state->regs = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "samsung,pmu-syscon"); ++ if (IS_ERR(state->regs)) { ++ dev_err(dev, "Failed to lookup PMU regmap\n"); ++ return PTR_ERR(state->regs); ++ } ++ ++ match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node); ++ state->drvdata = match->data; ++ ++ phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create Display Port PHY\n"); ++ return PTR_ERR(phy); ++ } ++ phy_set_drvdata(phy, state); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static struct platform_driver exynos_dp_video_phy_driver = { ++ .probe = exynos_dp_video_phy_probe, ++ .driver = { ++ .name = "exynos-dp-video-phy", ++ .of_match_table = exynos_dp_video_phy_of_match, ++ } ++}; ++module_platform_driver(exynos_dp_video_phy_driver); ++ ++MODULE_AUTHOR("Jingoo Han "); ++MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c +new file mode 100644 +index 000000000000..c198886f80a3 +--- /dev/null ++++ b/drivers/phy/samsung/phy-exynos-mipi-video.c +@@ -0,0 +1,377 @@ ++/* ++ * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver ++ * ++ * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. ++ * Author: Sylwester Nawrocki ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++enum exynos_mipi_phy_id { ++ EXYNOS_MIPI_PHY_ID_NONE = -1, ++ EXYNOS_MIPI_PHY_ID_CSIS0, ++ EXYNOS_MIPI_PHY_ID_DSIM0, ++ EXYNOS_MIPI_PHY_ID_CSIS1, ++ EXYNOS_MIPI_PHY_ID_DSIM1, ++ EXYNOS_MIPI_PHY_ID_CSIS2, ++ EXYNOS_MIPI_PHYS_NUM ++}; ++ ++enum exynos_mipi_phy_regmap_id { ++ EXYNOS_MIPI_REGMAP_PMU, ++ EXYNOS_MIPI_REGMAP_DISP, ++ EXYNOS_MIPI_REGMAP_CAM0, ++ EXYNOS_MIPI_REGMAP_CAM1, ++ EXYNOS_MIPI_REGMAPS_NUM ++}; ++ ++struct mipi_phy_device_desc { ++ int num_phys; ++ int num_regmaps; ++ const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM]; ++ struct exynos_mipi_phy_desc { ++ enum exynos_mipi_phy_id coupled_phy_id; ++ u32 enable_val; ++ unsigned int enable_reg; ++ enum exynos_mipi_phy_regmap_id enable_map; ++ u32 resetn_val; ++ unsigned int resetn_reg; ++ enum exynos_mipi_phy_regmap_id resetn_map; ++ } phys[EXYNOS_MIPI_PHYS_NUM]; ++}; ++ ++static const struct mipi_phy_device_desc s5pv210_mipi_phy = { ++ .num_regmaps = 1, ++ .regmap_names = {"syscon"}, ++ .num_phys = 4, ++ .phys = { ++ { ++ /* EXYNOS_MIPI_PHY_ID_CSIS0 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, ++ .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_DSIM0 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, ++ .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_CSIS1 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, ++ .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_DSIM1 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, ++ .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, ++ }, ++}; ++ ++static const struct mipi_phy_device_desc exynos5420_mipi_phy = { ++ .num_regmaps = 1, ++ .regmap_names = {"syscon"}, ++ .num_phys = 5, ++ .phys = { ++ { ++ /* EXYNOS_MIPI_PHY_ID_CSIS0 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, ++ .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_DSIM0 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, ++ .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_CSIS1 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, ++ .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_DSIM1 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, ++ .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_CSIS2 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, ++ .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), ++ .resetn_map = EXYNOS_MIPI_REGMAP_PMU, ++ }, ++ }, ++}; ++ ++#define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C ++#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014 ++#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020 ++ ++static const struct mipi_phy_device_desc exynos5433_mipi_phy = { ++ .num_regmaps = 4, ++ .regmap_names = { ++ "samsung,pmu-syscon", ++ "samsung,disp-sysreg", ++ "samsung,cam0-sysreg", ++ "samsung,cam1-sysreg" ++ }, ++ .num_phys = 5, ++ .phys = { ++ { ++ /* EXYNOS_MIPI_PHY_ID_CSIS0 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = BIT(0), ++ .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, ++ .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_DSIM0 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = BIT(0), ++ .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, ++ .resetn_map = EXYNOS_MIPI_REGMAP_DISP, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_CSIS1 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = BIT(1), ++ .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, ++ .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_DSIM1 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = BIT(1), ++ .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, ++ .resetn_map = EXYNOS_MIPI_REGMAP_DISP, ++ }, { ++ /* EXYNOS_MIPI_PHY_ID_CSIS2 */ ++ .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, ++ .enable_val = EXYNOS4_PHY_ENABLE, ++ .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(2), ++ .enable_map = EXYNOS_MIPI_REGMAP_PMU, ++ .resetn_val = BIT(0), ++ .resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON, ++ .resetn_map = EXYNOS_MIPI_REGMAP_CAM1, ++ }, ++ }, ++}; ++ ++struct exynos_mipi_video_phy { ++ struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM]; ++ int num_phys; ++ struct video_phy_desc { ++ struct phy *phy; ++ unsigned int index; ++ const struct exynos_mipi_phy_desc *data; ++ } phys[EXYNOS_MIPI_PHYS_NUM]; ++ spinlock_t slock; ++}; ++ ++static int __set_phy_state(const struct exynos_mipi_phy_desc *data, ++ struct exynos_mipi_video_phy *state, unsigned int on) ++{ ++ u32 val; ++ ++ spin_lock(&state->slock); ++ ++ /* disable in PMU sysreg */ ++ if (!on && data->coupled_phy_id >= 0 && ++ state->phys[data->coupled_phy_id].phy->power_count == 0) { ++ regmap_read(state->regmaps[data->enable_map], data->enable_reg, ++ &val); ++ val &= ~data->enable_val; ++ regmap_write(state->regmaps[data->enable_map], data->enable_reg, ++ val); ++ } ++ ++ /* PHY reset */ ++ regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val); ++ val = on ? (val | data->resetn_val) : (val & ~data->resetn_val); ++ regmap_write(state->regmaps[data->resetn_map], data->resetn_reg, val); ++ ++ /* enable in PMU sysreg */ ++ if (on) { ++ regmap_read(state->regmaps[data->enable_map], data->enable_reg, ++ &val); ++ val |= data->enable_val; ++ regmap_write(state->regmaps[data->enable_map], data->enable_reg, ++ val); ++ } ++ ++ spin_unlock(&state->slock); ++ ++ return 0; ++} ++ ++#define to_mipi_video_phy(desc) \ ++ container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]) ++ ++static int exynos_mipi_video_phy_power_on(struct phy *phy) ++{ ++ struct video_phy_desc *phy_desc = phy_get_drvdata(phy); ++ struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); ++ ++ return __set_phy_state(phy_desc->data, state, 1); ++} ++ ++static int exynos_mipi_video_phy_power_off(struct phy *phy) ++{ ++ struct video_phy_desc *phy_desc = phy_get_drvdata(phy); ++ struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); ++ ++ return __set_phy_state(phy_desc->data, state, 0); ++} ++ ++static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct exynos_mipi_video_phy *state = dev_get_drvdata(dev); ++ ++ if (WARN_ON(args->args[0] >= state->num_phys)) ++ return ERR_PTR(-ENODEV); ++ ++ return state->phys[args->args[0]].phy; ++} ++ ++static const struct phy_ops exynos_mipi_video_phy_ops = { ++ .power_on = exynos_mipi_video_phy_power_on, ++ .power_off = exynos_mipi_video_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int exynos_mipi_video_phy_probe(struct platform_device *pdev) ++{ ++ const struct mipi_phy_device_desc *phy_dev; ++ struct exynos_mipi_video_phy *state; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct phy_provider *phy_provider; ++ unsigned int i; ++ ++ phy_dev = of_device_get_match_data(dev); ++ if (!phy_dev) ++ return -ENODEV; ++ ++ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ for (i = 0; i < phy_dev->num_regmaps; i++) { ++ state->regmaps[i] = syscon_regmap_lookup_by_phandle(np, ++ phy_dev->regmap_names[i]); ++ if (IS_ERR(state->regmaps[i])) ++ return PTR_ERR(state->regmaps[i]); ++ } ++ state->num_phys = phy_dev->num_phys; ++ spin_lock_init(&state->slock); ++ ++ dev_set_drvdata(dev, state); ++ ++ for (i = 0; i < state->num_phys; i++) { ++ struct phy *phy = devm_phy_create(dev, NULL, ++ &exynos_mipi_video_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create PHY %d\n", i); ++ return PTR_ERR(phy); ++ } ++ ++ state->phys[i].phy = phy; ++ state->phys[i].index = i; ++ state->phys[i].data = &phy_dev->phys[i]; ++ phy_set_drvdata(phy, &state->phys[i]); ++ } ++ ++ phy_provider = devm_of_phy_provider_register(dev, ++ exynos_mipi_video_phy_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static const struct of_device_id exynos_mipi_video_phy_of_match[] = { ++ { ++ .compatible = "samsung,s5pv210-mipi-video-phy", ++ .data = &s5pv210_mipi_phy, ++ }, { ++ .compatible = "samsung,exynos5420-mipi-video-phy", ++ .data = &exynos5420_mipi_phy, ++ }, { ++ .compatible = "samsung,exynos5433-mipi-video-phy", ++ .data = &exynos5433_mipi_phy, ++ }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match); ++ ++static struct platform_driver exynos_mipi_video_phy_driver = { ++ .probe = exynos_mipi_video_phy_probe, ++ .driver = { ++ .of_match_table = exynos_mipi_video_phy_of_match, ++ .name = "exynos-mipi-video-phy", ++ } ++}; ++module_platform_driver(exynos_mipi_video_phy_driver); ++ ++MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver"); ++MODULE_AUTHOR("Sylwester Nawrocki "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/samsung/phy-exynos-pcie.c b/drivers/phy/samsung/phy-exynos-pcie.c +new file mode 100644 +index 000000000000..a89c12faff39 +--- /dev/null ++++ b/drivers/phy/samsung/phy-exynos-pcie.c +@@ -0,0 +1,281 @@ ++/* ++ * Samsung EXYNOS SoC series PCIe PHY driver ++ * ++ * Phy provider for PCIe controller on Exynos SoC series ++ * ++ * Copyright (C) 2017 Samsung Electronics Co., Ltd. ++ * Jaehoon Chung ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* PCIe Purple registers */ ++#define PCIE_PHY_GLOBAL_RESET 0x000 ++#define PCIE_PHY_COMMON_RESET 0x004 ++#define PCIE_PHY_CMN_REG 0x008 ++#define PCIE_PHY_MAC_RESET 0x00c ++#define PCIE_PHY_PLL_LOCKED 0x010 ++#define PCIE_PHY_TRSVREG_RESET 0x020 ++#define PCIE_PHY_TRSV_RESET 0x024 ++ ++/* PCIe PHY registers */ ++#define PCIE_PHY_IMPEDANCE 0x004 ++#define PCIE_PHY_PLL_DIV_0 0x008 ++#define PCIE_PHY_PLL_BIAS 0x00c ++#define PCIE_PHY_DCC_FEEDBACK 0x014 ++#define PCIE_PHY_PLL_DIV_1 0x05c ++#define PCIE_PHY_COMMON_POWER 0x064 ++#define PCIE_PHY_COMMON_PD_CMN BIT(3) ++#define PCIE_PHY_TRSV0_EMP_LVL 0x084 ++#define PCIE_PHY_TRSV0_DRV_LVL 0x088 ++#define PCIE_PHY_TRSV0_RXCDR 0x0ac ++#define PCIE_PHY_TRSV0_POWER 0x0c4 ++#define PCIE_PHY_TRSV0_PD_TSV BIT(7) ++#define PCIE_PHY_TRSV0_LVCC 0x0dc ++#define PCIE_PHY_TRSV1_EMP_LVL 0x144 ++#define PCIE_PHY_TRSV1_RXCDR 0x16c ++#define PCIE_PHY_TRSV1_POWER 0x184 ++#define PCIE_PHY_TRSV1_PD_TSV BIT(7) ++#define PCIE_PHY_TRSV1_LVCC 0x19c ++#define PCIE_PHY_TRSV2_EMP_LVL 0x204 ++#define PCIE_PHY_TRSV2_RXCDR 0x22c ++#define PCIE_PHY_TRSV2_POWER 0x244 ++#define PCIE_PHY_TRSV2_PD_TSV BIT(7) ++#define PCIE_PHY_TRSV2_LVCC 0x25c ++#define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 ++#define PCIE_PHY_TRSV3_RXCDR 0x2ec ++#define PCIE_PHY_TRSV3_POWER 0x304 ++#define PCIE_PHY_TRSV3_PD_TSV BIT(7) ++#define PCIE_PHY_TRSV3_LVCC 0x31c ++ ++struct exynos_pcie_phy_data { ++ const struct phy_ops *ops; ++}; ++ ++/* For Exynos pcie phy */ ++struct exynos_pcie_phy { ++ const struct exynos_pcie_phy_data *drv_data; ++ void __iomem *phy_base; ++ void __iomem *blk_base; /* For exynos5440 */ ++}; ++ ++static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset) ++{ ++ writel(val, base + offset); ++} ++ ++static u32 exynos_pcie_phy_readl(void __iomem *base, u32 offset) ++{ ++ return readl(base + offset); ++} ++ ++/* For Exynos5440 specific functions */ ++static int exynos5440_pcie_phy_init(struct phy *phy) ++{ ++ struct exynos_pcie_phy *ep = phy_get_drvdata(phy); ++ ++ /* DCC feedback control off */ ++ exynos_pcie_phy_writel(ep->phy_base, 0x29, PCIE_PHY_DCC_FEEDBACK); ++ ++ /* set TX/RX impedance */ ++ exynos_pcie_phy_writel(ep->phy_base, 0xd5, PCIE_PHY_IMPEDANCE); ++ ++ /* set 50Mhz PHY clock */ ++ exynos_pcie_phy_writel(ep->phy_base, 0x14, PCIE_PHY_PLL_DIV_0); ++ exynos_pcie_phy_writel(ep->phy_base, 0x12, PCIE_PHY_PLL_DIV_1); ++ ++ /* set TX Differential output for lane 0 */ ++ exynos_pcie_phy_writel(ep->phy_base, 0x7f, PCIE_PHY_TRSV0_DRV_LVL); ++ ++ /* set TX Pre-emphasis Level Control for lane 0 to minimum */ ++ exynos_pcie_phy_writel(ep->phy_base, 0x0, PCIE_PHY_TRSV0_EMP_LVL); ++ ++ /* set RX clock and data recovery bandwidth */ ++ exynos_pcie_phy_writel(ep->phy_base, 0xe7, PCIE_PHY_PLL_BIAS); ++ exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV0_RXCDR); ++ exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV1_RXCDR); ++ exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV2_RXCDR); ++ exynos_pcie_phy_writel(ep->phy_base, 0x82, PCIE_PHY_TRSV3_RXCDR); ++ ++ /* change TX Pre-emphasis Level Control for lanes */ ++ exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV0_EMP_LVL); ++ exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV1_EMP_LVL); ++ exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV2_EMP_LVL); ++ exynos_pcie_phy_writel(ep->phy_base, 0x39, PCIE_PHY_TRSV3_EMP_LVL); ++ ++ /* set LVCC */ ++ exynos_pcie_phy_writel(ep->phy_base, 0x20, PCIE_PHY_TRSV0_LVCC); ++ exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV1_LVCC); ++ exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV2_LVCC); ++ exynos_pcie_phy_writel(ep->phy_base, 0xa0, PCIE_PHY_TRSV3_LVCC); ++ ++ /* pulse for common reset */ ++ exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_COMMON_RESET); ++ udelay(500); ++ exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); ++ ++ return 0; ++} ++ ++static int exynos5440_pcie_phy_power_on(struct phy *phy) ++{ ++ struct exynos_pcie_phy *ep = phy_get_drvdata(phy); ++ u32 val; ++ ++ exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_COMMON_RESET); ++ exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_CMN_REG); ++ exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSVREG_RESET); ++ exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_TRSV_RESET); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); ++ val &= ~PCIE_PHY_COMMON_PD_CMN; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); ++ val &= ~PCIE_PHY_TRSV0_PD_TSV; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); ++ val &= ~PCIE_PHY_TRSV1_PD_TSV; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); ++ val &= ~PCIE_PHY_TRSV2_PD_TSV; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); ++ val &= ~PCIE_PHY_TRSV3_PD_TSV; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); ++ ++ return 0; ++} ++ ++static int exynos5440_pcie_phy_power_off(struct phy *phy) ++{ ++ struct exynos_pcie_phy *ep = phy_get_drvdata(phy); ++ u32 val; ++ ++ if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val, ++ (val != 0), 1, 500)) { ++ dev_err(&phy->dev, "PLL Locked: 0x%x\n", val); ++ return -ETIMEDOUT; ++ } ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_COMMON_POWER); ++ val |= PCIE_PHY_COMMON_PD_CMN; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_COMMON_POWER); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV0_POWER); ++ val |= PCIE_PHY_TRSV0_PD_TSV; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV0_POWER); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV1_POWER); ++ val |= PCIE_PHY_TRSV1_PD_TSV; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV1_POWER); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV2_POWER); ++ val |= PCIE_PHY_TRSV2_PD_TSV; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV2_POWER); ++ ++ val = exynos_pcie_phy_readl(ep->phy_base, PCIE_PHY_TRSV3_POWER); ++ val |= PCIE_PHY_TRSV3_PD_TSV; ++ exynos_pcie_phy_writel(ep->phy_base, val, PCIE_PHY_TRSV3_POWER); ++ ++ return 0; ++} ++ ++static int exynos5440_pcie_phy_reset(struct phy *phy) ++{ ++ struct exynos_pcie_phy *ep = phy_get_drvdata(phy); ++ ++ exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_MAC_RESET); ++ exynos_pcie_phy_writel(ep->blk_base, 1, PCIE_PHY_GLOBAL_RESET); ++ exynos_pcie_phy_writel(ep->blk_base, 0, PCIE_PHY_GLOBAL_RESET); ++ ++ return 0; ++} ++ ++static const struct phy_ops exynos5440_phy_ops = { ++ .init = exynos5440_pcie_phy_init, ++ .power_on = exynos5440_pcie_phy_power_on, ++ .power_off = exynos5440_pcie_phy_power_off, ++ .reset = exynos5440_pcie_phy_reset, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct exynos_pcie_phy_data exynos5440_pcie_phy_data = { ++ .ops = &exynos5440_phy_ops, ++}; ++ ++static const struct of_device_id exynos_pcie_phy_match[] = { ++ { ++ .compatible = "samsung,exynos5440-pcie-phy", ++ .data = &exynos5440_pcie_phy_data, ++ }, ++ {}, ++}; ++ ++static int exynos_pcie_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct exynos_pcie_phy *exynos_phy; ++ struct phy *generic_phy; ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ const struct exynos_pcie_phy_data *drv_data; ++ ++ drv_data = of_device_get_match_data(dev); ++ if (!drv_data) ++ return -ENODEV; ++ ++ exynos_phy = devm_kzalloc(dev, sizeof(*exynos_phy), GFP_KERNEL); ++ if (!exynos_phy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ exynos_phy->phy_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(exynos_phy->phy_base)) ++ return PTR_ERR(exynos_phy->phy_base); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ exynos_phy->blk_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(exynos_phy->blk_base)) ++ return PTR_ERR(exynos_phy->blk_base); ++ ++ exynos_phy->drv_data = drv_data; ++ ++ generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops); ++ if (IS_ERR(generic_phy)) { ++ dev_err(dev, "failed to create PHY\n"); ++ return PTR_ERR(generic_phy); ++ } ++ ++ phy_set_drvdata(generic_phy, exynos_phy); ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static struct platform_driver exynos_pcie_phy_driver = { ++ .probe = exynos_pcie_phy_probe, ++ .driver = { ++ .of_match_table = exynos_pcie_phy_match, ++ .name = "exynos_pcie_phy", ++ } ++}; ++ ++builtin_platform_driver(exynos_pcie_phy_driver); +diff --git a/drivers/phy/samsung/phy-exynos4210-usb2.c b/drivers/phy/samsung/phy-exynos4210-usb2.c +new file mode 100644 +index 000000000000..1f50e1004828 +--- /dev/null ++++ b/drivers/phy/samsung/phy-exynos4210-usb2.c +@@ -0,0 +1,260 @@ ++/* ++ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support ++ * ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Author: Kamil Debski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "phy-samsung-usb2.h" ++ ++/* Exynos USB PHY registers */ ++ ++/* PHY power control */ ++#define EXYNOS_4210_UPHYPWR 0x0 ++ ++#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0) ++#define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3) ++#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4) ++#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5) ++#define EXYNOS_4210_UPHYPWR_PHY0 ( \ ++ EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \ ++ EXYNOS_4210_UPHYPWR_PHY0_PWR | \ ++ EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \ ++ EXYNOS_4210_UPHYPWR_PHY0_SLEEP) ++ ++#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6) ++#define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7) ++#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8) ++#define EXYNOS_4210_UPHYPWR_PHY1 ( \ ++ EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \ ++ EXYNOS_4210_UPHYPWR_PHY1_PWR | \ ++ EXYNOS_4210_UPHYPWR_PHY1_SLEEP) ++ ++#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9) ++#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10) ++#define EXYNOS_4210_UPHYPWR_HSIC0 ( \ ++ EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \ ++ EXYNOS_4210_UPHYPWR_HSIC0_SLEEP) ++ ++#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11) ++#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12) ++#define EXYNOS_4210_UPHYPWR_HSIC1 ( \ ++ EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \ ++ EXYNOS_4210_UPHYPWR_HSIC1_SLEEP) ++ ++/* PHY clock control */ ++#define EXYNOS_4210_UPHYCLK 0x4 ++ ++#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) ++#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0 ++#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) ++#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) ++#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) ++ ++#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2) ++#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4) ++#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7) ++ ++/* PHY reset control */ ++#define EXYNOS_4210_UPHYRST 0x8 ++ ++#define EXYNOS_4210_URSTCON_PHY0 BIT(0) ++#define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1) ++#define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2) ++#define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3) ++#define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4) ++#define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5) ++#define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6) ++#define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7) ++#define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8) ++#define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9) ++ ++/* Isolation, configured in the power management unit */ ++#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704 ++#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0) ++#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708 ++#define EXYNOS_4210_USB_ISOL_HOST BIT(0) ++ ++/* USBYPHY1 Floating prevention */ ++#define EXYNOS_4210_UPHY1CON 0x34 ++#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1 ++ ++/* Mode switching SUB Device <-> Host */ ++#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c ++#define EXYNOS_4210_MODE_SWITCH_MASK 1 ++#define EXYNOS_4210_MODE_SWITCH_DEVICE 0 ++#define EXYNOS_4210_MODE_SWITCH_HOST 1 ++ ++enum exynos4210_phy_id { ++ EXYNOS4210_DEVICE, ++ EXYNOS4210_HOST, ++ EXYNOS4210_HSIC0, ++ EXYNOS4210_HSIC1, ++ EXYNOS4210_NUM_PHYS, ++}; ++ ++/* ++ * exynos4210_rate_to_clk() converts the supplied clock rate to the value that ++ * can be written to the phy register. ++ */ ++static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) ++{ ++ switch (rate) { ++ case 12 * MHZ: ++ *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ; ++ break; ++ case 24 * MHZ: ++ *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ; ++ break; ++ case 48 * MHZ: ++ *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 offset; ++ u32 mask; ++ ++ switch (inst->cfg->id) { ++ case EXYNOS4210_DEVICE: ++ offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET; ++ mask = EXYNOS_4210_USB_ISOL_DEVICE; ++ break; ++ case EXYNOS4210_HOST: ++ offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET; ++ mask = EXYNOS_4210_USB_ISOL_HOST; ++ break; ++ default: ++ return; ++ } ++ ++ regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); ++} ++ ++static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 rstbits = 0; ++ u32 phypwr = 0; ++ u32 rst; ++ u32 pwr; ++ u32 clk; ++ ++ switch (inst->cfg->id) { ++ case EXYNOS4210_DEVICE: ++ phypwr = EXYNOS_4210_UPHYPWR_PHY0; ++ rstbits = EXYNOS_4210_URSTCON_PHY0; ++ break; ++ case EXYNOS4210_HOST: ++ phypwr = EXYNOS_4210_UPHYPWR_PHY1; ++ rstbits = EXYNOS_4210_URSTCON_PHY1_ALL | ++ EXYNOS_4210_URSTCON_PHY1_P0 | ++ EXYNOS_4210_URSTCON_PHY1_P1P2 | ++ EXYNOS_4210_URSTCON_HOST_LINK_ALL | ++ EXYNOS_4210_URSTCON_HOST_LINK_P0; ++ writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON); ++ break; ++ case EXYNOS4210_HSIC0: ++ phypwr = EXYNOS_4210_UPHYPWR_HSIC0; ++ rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | ++ EXYNOS_4210_URSTCON_HOST_LINK_P1; ++ break; ++ case EXYNOS4210_HSIC1: ++ phypwr = EXYNOS_4210_UPHYPWR_HSIC1; ++ rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | ++ EXYNOS_4210_URSTCON_HOST_LINK_P2; ++ break; ++ } ++ ++ if (on) { ++ clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK); ++ clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK; ++ clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET; ++ writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK); ++ ++ pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); ++ pwr &= ~phypwr; ++ writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); ++ ++ rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST); ++ rst |= rstbits; ++ writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); ++ udelay(10); ++ rst &= ~rstbits; ++ writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); ++ /* The following delay is necessary for the reset sequence to be ++ * completed */ ++ udelay(80); ++ } else { ++ pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); ++ pwr |= phypwr; ++ writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); ++ } ++} ++ ++static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst) ++{ ++ /* Order of initialisation is important - first power then isolation */ ++ exynos4210_phy_pwr(inst, 1); ++ exynos4210_isol(inst, 0); ++ ++ return 0; ++} ++ ++static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst) ++{ ++ exynos4210_isol(inst, 1); ++ exynos4210_phy_pwr(inst, 0); ++ ++ return 0; ++} ++ ++ ++static const struct samsung_usb2_common_phy exynos4210_phys[] = { ++ { ++ .label = "device", ++ .id = EXYNOS4210_DEVICE, ++ .power_on = exynos4210_power_on, ++ .power_off = exynos4210_power_off, ++ }, ++ { ++ .label = "host", ++ .id = EXYNOS4210_HOST, ++ .power_on = exynos4210_power_on, ++ .power_off = exynos4210_power_off, ++ }, ++ { ++ .label = "hsic0", ++ .id = EXYNOS4210_HSIC0, ++ .power_on = exynos4210_power_on, ++ .power_off = exynos4210_power_off, ++ }, ++ { ++ .label = "hsic1", ++ .id = EXYNOS4210_HSIC1, ++ .power_on = exynos4210_power_on, ++ .power_off = exynos4210_power_off, ++ }, ++}; ++ ++const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { ++ .has_mode_switch = 0, ++ .num_phys = EXYNOS4210_NUM_PHYS, ++ .phys = exynos4210_phys, ++ .rate_to_clk = exynos4210_rate_to_clk, ++}; +diff --git a/drivers/phy/samsung/phy-exynos4x12-usb2.c b/drivers/phy/samsung/phy-exynos4x12-usb2.c +new file mode 100644 +index 000000000000..7f27a91acf87 +--- /dev/null ++++ b/drivers/phy/samsung/phy-exynos4x12-usb2.c +@@ -0,0 +1,378 @@ ++/* ++ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support ++ * ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Author: Kamil Debski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "phy-samsung-usb2.h" ++ ++/* Exynos USB PHY registers */ ++ ++/* PHY power control */ ++#define EXYNOS_4x12_UPHYPWR 0x0 ++ ++#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0) ++#define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3) ++#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4) ++#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5) ++#define EXYNOS_4x12_UPHYPWR_PHY0 ( \ ++ EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \ ++ EXYNOS_4x12_UPHYPWR_PHY0_PWR | \ ++ EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \ ++ EXYNOS_4x12_UPHYPWR_PHY0_SLEEP) ++ ++#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6) ++#define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7) ++#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8) ++#define EXYNOS_4x12_UPHYPWR_PHY1 ( \ ++ EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \ ++ EXYNOS_4x12_UPHYPWR_PHY1_PWR | \ ++ EXYNOS_4x12_UPHYPWR_PHY1_SLEEP) ++ ++#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9) ++#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10) ++#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11) ++#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \ ++ EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \ ++ EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \ ++ EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP) ++ ++#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12) ++#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13) ++#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14) ++#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \ ++ EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \ ++ EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \ ++ EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP) ++ ++/* PHY clock control */ ++#define EXYNOS_4x12_UPHYCLK 0x4 ++ ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0) ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0 ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0) ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0) ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0) ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0) ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) ++#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0) ++ ++#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8) ++ ++#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) ++#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) ++#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) ++ ++#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10) ++#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10 ++#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10) ++#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10) ++#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10) ++#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10) ++#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10) ++ ++/* PHY reset control */ ++#define EXYNOS_4x12_UPHYRST 0x8 ++ ++#define EXYNOS_4x12_URSTCON_PHY0 BIT(0) ++#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) ++#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) ++#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) ++/* The following bit defines are presented in the ++ * order taken from the Exynos4412 reference manual. ++ * ++ * During experiments with the hardware and debugging ++ * it was determined that the hardware behaves contrary ++ * to the manual. ++ * ++ * The following bit values were chaned accordingly to the ++ * results of real hardware experiments. ++ */ ++#define EXYNOS_4x12_URSTCON_PHY1 BIT(4) ++#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) ++#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) ++#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) ++#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) ++#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) ++#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) ++ ++/* Isolation, configured in the power management unit */ ++#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 ++#define EXYNOS_4x12_USB_ISOL_OTG BIT(0) ++#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708 ++#define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0) ++#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c ++#define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0) ++ ++/* Mode switching SUB Device <-> Host */ ++#define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c ++#define EXYNOS_4x12_MODE_SWITCH_MASK 1 ++#define EXYNOS_4x12_MODE_SWITCH_DEVICE 0 ++#define EXYNOS_4x12_MODE_SWITCH_HOST 1 ++ ++enum exynos4x12_phy_id { ++ EXYNOS4x12_DEVICE, ++ EXYNOS4x12_HOST, ++ EXYNOS4x12_HSIC0, ++ EXYNOS4x12_HSIC1, ++ EXYNOS4x12_NUM_PHYS, ++}; ++ ++/* ++ * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that ++ * can be written to the phy register. ++ */ ++static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) ++{ ++ /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */ ++ ++ switch (rate) { ++ case 9600 * KHZ: ++ *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6; ++ break; ++ case 10 * MHZ: ++ *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ; ++ break; ++ case 12 * MHZ: ++ *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ; ++ break; ++ case 19200 * KHZ: ++ *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2; ++ break; ++ case 20 * MHZ: ++ *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ; ++ break; ++ case 24 * MHZ: ++ *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ; ++ break; ++ case 50 * MHZ: ++ *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 offset; ++ u32 mask; ++ ++ switch (inst->cfg->id) { ++ case EXYNOS4x12_DEVICE: ++ case EXYNOS4x12_HOST: ++ offset = EXYNOS_4x12_USB_ISOL_OFFSET; ++ mask = EXYNOS_4x12_USB_ISOL_OTG; ++ break; ++ case EXYNOS4x12_HSIC0: ++ offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET; ++ mask = EXYNOS_4x12_USB_ISOL_HSIC0; ++ break; ++ case EXYNOS4x12_HSIC1: ++ offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET; ++ mask = EXYNOS_4x12_USB_ISOL_HSIC1; ++ break; ++ default: ++ return; ++ } ++ ++ regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); ++} ++ ++static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 clk; ++ ++ clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); ++ clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; ++ ++ if (drv->cfg->has_refclk_sel) ++ clk = EXYNOS_3250_UPHYCLK_REFCLKSEL; ++ ++ clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; ++ clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; ++ writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); ++} ++ ++static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 rstbits = 0; ++ u32 phypwr = 0; ++ u32 rst; ++ u32 pwr; ++ ++ switch (inst->cfg->id) { ++ case EXYNOS4x12_DEVICE: ++ phypwr = EXYNOS_4x12_UPHYPWR_PHY0; ++ rstbits = EXYNOS_4x12_URSTCON_PHY0; ++ break; ++ case EXYNOS4x12_HOST: ++ phypwr = EXYNOS_4x12_UPHYPWR_PHY1; ++ rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | ++ EXYNOS_4x12_URSTCON_PHY1 | ++ EXYNOS_4x12_URSTCON_HOST_LINK_P0; ++ break; ++ case EXYNOS4x12_HSIC0: ++ phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; ++ rstbits = EXYNOS_4x12_URSTCON_HSIC0 | ++ EXYNOS_4x12_URSTCON_HOST_LINK_P1; ++ break; ++ case EXYNOS4x12_HSIC1: ++ phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; ++ rstbits = EXYNOS_4x12_URSTCON_HSIC1 | ++ EXYNOS_4x12_URSTCON_HOST_LINK_P1; ++ break; ++ } ++ ++ if (on) { ++ pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); ++ pwr &= ~phypwr; ++ writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); ++ ++ rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST); ++ rst |= rstbits; ++ writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); ++ udelay(10); ++ rst &= ~rstbits; ++ writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); ++ /* The following delay is necessary for the reset sequence to be ++ * completed */ ++ udelay(80); ++ } else { ++ pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); ++ pwr |= phypwr; ++ writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); ++ } ++} ++ ++static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) ++{ ++ if (inst->int_cnt++ > 0) ++ return; ++ ++ exynos4x12_setup_clk(inst); ++ exynos4x12_isol(inst, 0); ++ exynos4x12_phy_pwr(inst, 1); ++} ++ ++static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ ++ if (inst->ext_cnt++ > 0) ++ return 0; ++ ++ if (inst->cfg->id == EXYNOS4x12_HOST) { ++ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, ++ EXYNOS_4x12_MODE_SWITCH_MASK, ++ EXYNOS_4x12_MODE_SWITCH_HOST); ++ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); ++ } ++ ++ if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) ++ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, ++ EXYNOS_4x12_MODE_SWITCH_MASK, ++ EXYNOS_4x12_MODE_SWITCH_DEVICE); ++ ++ if (inst->cfg->id == EXYNOS4x12_HSIC0 || ++ inst->cfg->id == EXYNOS4x12_HSIC1) { ++ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); ++ exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); ++ } ++ ++ exynos4x12_power_on_int(inst); ++ ++ return 0; ++} ++ ++static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) ++{ ++ if (inst->int_cnt-- > 1) ++ return; ++ ++ exynos4x12_isol(inst, 1); ++ exynos4x12_phy_pwr(inst, 0); ++} ++ ++static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ ++ if (inst->ext_cnt-- > 1) ++ return 0; ++ ++ if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) ++ regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, ++ EXYNOS_4x12_MODE_SWITCH_MASK, ++ EXYNOS_4x12_MODE_SWITCH_HOST); ++ ++ if (inst->cfg->id == EXYNOS4x12_HOST) ++ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); ++ ++ if (inst->cfg->id == EXYNOS4x12_HSIC0 || ++ inst->cfg->id == EXYNOS4x12_HSIC1) { ++ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); ++ exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); ++ } ++ ++ exynos4x12_power_off_int(inst); ++ ++ return 0; ++} ++ ++ ++static const struct samsung_usb2_common_phy exynos4x12_phys[] = { ++ { ++ .label = "device", ++ .id = EXYNOS4x12_DEVICE, ++ .power_on = exynos4x12_power_on, ++ .power_off = exynos4x12_power_off, ++ }, ++ { ++ .label = "host", ++ .id = EXYNOS4x12_HOST, ++ .power_on = exynos4x12_power_on, ++ .power_off = exynos4x12_power_off, ++ }, ++ { ++ .label = "hsic0", ++ .id = EXYNOS4x12_HSIC0, ++ .power_on = exynos4x12_power_on, ++ .power_off = exynos4x12_power_off, ++ }, ++ { ++ .label = "hsic1", ++ .id = EXYNOS4x12_HSIC1, ++ .power_on = exynos4x12_power_on, ++ .power_off = exynos4x12_power_off, ++ }, ++}; ++ ++const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { ++ .has_refclk_sel = 1, ++ .num_phys = 1, ++ .phys = exynos4x12_phys, ++ .rate_to_clk = exynos4x12_rate_to_clk, ++}; ++ ++const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { ++ .has_mode_switch = 1, ++ .num_phys = EXYNOS4x12_NUM_PHYS, ++ .phys = exynos4x12_phys, ++ .rate_to_clk = exynos4x12_rate_to_clk, ++}; +diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c +new file mode 100644 +index 000000000000..7c41daa2c625 +--- /dev/null ++++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c +@@ -0,0 +1,782 @@ ++/* ++ * Samsung EXYNOS5 SoC series USB DRD PHY driver ++ * ++ * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series ++ * ++ * Copyright (C) 2014 Samsung Electronics Co., Ltd. ++ * Author: Vivek Gautam ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Exynos USB PHY registers */ ++#define EXYNOS5_FSEL_9MHZ6 0x0 ++#define EXYNOS5_FSEL_10MHZ 0x1 ++#define EXYNOS5_FSEL_12MHZ 0x2 ++#define EXYNOS5_FSEL_19MHZ2 0x3 ++#define EXYNOS5_FSEL_20MHZ 0x4 ++#define EXYNOS5_FSEL_24MHZ 0x5 ++#define EXYNOS5_FSEL_50MHZ 0x7 ++ ++/* EXYNOS5: USB 3.0 DRD PHY registers */ ++#define EXYNOS5_DRD_LINKSYSTEM 0x04 ++ ++#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) ++#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) ++#define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) ++ ++#define EXYNOS5_DRD_PHYUTMI 0x08 ++ ++#define PHYUTMI_OTGDISABLE BIT(6) ++#define PHYUTMI_FORCESUSPEND BIT(1) ++#define PHYUTMI_FORCESLEEP BIT(0) ++ ++#define EXYNOS5_DRD_PHYPIPE 0x0c ++ ++#define EXYNOS5_DRD_PHYCLKRST 0x10 ++ ++#define PHYCLKRST_EN_UTMISUSPEND BIT(31) ++ ++#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) ++#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) ++ ++#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) ++#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) ++ ++#define PHYCLKRST_SSC_EN BIT(20) ++#define PHYCLKRST_REF_SSP_EN BIT(19) ++#define PHYCLKRST_REF_CLKDIV2 BIT(18) ++ ++#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) ++#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) ++#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x32 << 11) ++#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) ++#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) ++#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) ++ ++#define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) ++#define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) ++#define PHYCLKRST_FSEL(_x) ((_x) << 5) ++#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) ++#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) ++#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) ++#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) ++ ++#define PHYCLKRST_RETENABLEN BIT(4) ++ ++#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) ++#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) ++#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) ++ ++#define PHYCLKRST_PORTRESET BIT(1) ++#define PHYCLKRST_COMMONONN BIT(0) ++ ++#define EXYNOS5_DRD_PHYREG0 0x14 ++#define EXYNOS5_DRD_PHYREG1 0x18 ++ ++#define EXYNOS5_DRD_PHYPARAM0 0x1c ++ ++#define PHYPARAM0_REF_USE_PAD BIT(31) ++#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) ++#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) ++ ++#define EXYNOS5_DRD_PHYPARAM1 0x20 ++ ++#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) ++#define PHYPARAM1_PCS_TXDEEMPH (0x1c) ++ ++#define EXYNOS5_DRD_PHYTERM 0x24 ++ ++#define EXYNOS5_DRD_PHYTEST 0x28 ++ ++#define PHYTEST_POWERDOWN_SSP BIT(3) ++#define PHYTEST_POWERDOWN_HSP BIT(2) ++ ++#define EXYNOS5_DRD_PHYADP 0x2c ++ ++#define EXYNOS5_DRD_PHYUTMICLKSEL 0x30 ++ ++#define PHYUTMICLKSEL_UTMI_CLKSEL BIT(2) ++ ++#define EXYNOS5_DRD_PHYRESUME 0x34 ++#define EXYNOS5_DRD_LINKPORT 0x44 ++ ++#define KHZ 1000 ++#define MHZ (KHZ * KHZ) ++ ++enum exynos5_usbdrd_phy_id { ++ EXYNOS5_DRDPHY_UTMI, ++ EXYNOS5_DRDPHY_PIPE3, ++ EXYNOS5_DRDPHYS_NUM, ++}; ++ ++struct phy_usb_instance; ++struct exynos5_usbdrd_phy; ++ ++struct exynos5_usbdrd_phy_config { ++ u32 id; ++ void (*phy_isol)(struct phy_usb_instance *inst, u32 on); ++ void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd); ++ unsigned int (*set_refclk)(struct phy_usb_instance *inst); ++}; ++ ++struct exynos5_usbdrd_phy_drvdata { ++ const struct exynos5_usbdrd_phy_config *phy_cfg; ++ u32 pmu_offset_usbdrd0_phy; ++ u32 pmu_offset_usbdrd1_phy; ++ bool has_common_clk_gate; ++}; ++ ++/** ++ * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY ++ * @dev: pointer to device instance of this platform device ++ * @reg_phy: usb phy controller register memory base ++ * @clk: phy clock for register access ++ * @pipeclk: clock for pipe3 phy ++ * @utmiclk: clock for utmi+ phy ++ * @itpclk: clock for ITP generation ++ * @drv_data: pointer to SoC level driver data structure ++ * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY ++ * instances each with its 'phy' and 'phy_cfg'. ++ * @extrefclk: frequency select settings when using 'separate ++ * reference clocks' for SS and HS operations ++ * @ref_clk: reference clock to PHY block from which PHY's ++ * operational clocks are derived ++ * vbus: VBUS regulator for phy ++ * vbus_boost: Boost regulator for VBUS present on few Exynos boards ++ */ ++struct exynos5_usbdrd_phy { ++ struct device *dev; ++ void __iomem *reg_phy; ++ struct clk *clk; ++ struct clk *pipeclk; ++ struct clk *utmiclk; ++ struct clk *itpclk; ++ const struct exynos5_usbdrd_phy_drvdata *drv_data; ++ struct phy_usb_instance { ++ struct phy *phy; ++ u32 index; ++ struct regmap *reg_pmu; ++ u32 pmu_offset; ++ const struct exynos5_usbdrd_phy_config *phy_cfg; ++ } phys[EXYNOS5_DRDPHYS_NUM]; ++ u32 extrefclk; ++ struct clk *ref_clk; ++ struct regulator *vbus; ++ struct regulator *vbus_boost; ++}; ++ ++static inline ++struct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst) ++{ ++ return container_of((inst), struct exynos5_usbdrd_phy, ++ phys[(inst)->index]); ++} ++ ++/* ++ * exynos5_rate_to_clk() converts the supplied clock rate to the value that ++ * can be written to the phy register. ++ */ ++static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg) ++{ ++ /* EXYNOS5_FSEL_MASK */ ++ ++ switch (rate) { ++ case 9600 * KHZ: ++ *reg = EXYNOS5_FSEL_9MHZ6; ++ break; ++ case 10 * MHZ: ++ *reg = EXYNOS5_FSEL_10MHZ; ++ break; ++ case 12 * MHZ: ++ *reg = EXYNOS5_FSEL_12MHZ; ++ break; ++ case 19200 * KHZ: ++ *reg = EXYNOS5_FSEL_19MHZ2; ++ break; ++ case 20 * MHZ: ++ *reg = EXYNOS5_FSEL_20MHZ; ++ break; ++ case 24 * MHZ: ++ *reg = EXYNOS5_FSEL_24MHZ; ++ break; ++ case 50 * MHZ: ++ *reg = EXYNOS5_FSEL_50MHZ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst, ++ unsigned int on) ++{ ++ unsigned int val; ++ ++ if (!inst->reg_pmu) ++ return; ++ ++ val = on ? 0 : EXYNOS4_PHY_ENABLE; ++ ++ regmap_update_bits(inst->reg_pmu, inst->pmu_offset, ++ EXYNOS4_PHY_ENABLE, val); ++} ++ ++/* ++ * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock ++ * from clock core. Further sets multiplier values and spread spectrum ++ * clock settings for SuperSpeed operations. ++ */ ++static unsigned int ++exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst) ++{ ++ u32 reg; ++ struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); ++ ++ /* restore any previous reference clock settings */ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); ++ ++ /* Use EXTREFCLK as ref clock */ ++ reg &= ~PHYCLKRST_REFCLKSEL_MASK; ++ reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; ++ ++ /* FSEL settings corresponding to reference clock */ ++ reg &= ~PHYCLKRST_FSEL_PIPE_MASK | ++ PHYCLKRST_MPLL_MULTIPLIER_MASK | ++ PHYCLKRST_SSC_REFCLKSEL_MASK; ++ switch (phy_drd->extrefclk) { ++ case EXYNOS5_FSEL_50MHZ: ++ reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | ++ PHYCLKRST_SSC_REFCLKSEL(0x00)); ++ break; ++ case EXYNOS5_FSEL_24MHZ: ++ reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | ++ PHYCLKRST_SSC_REFCLKSEL(0x88)); ++ break; ++ case EXYNOS5_FSEL_20MHZ: ++ reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | ++ PHYCLKRST_SSC_REFCLKSEL(0x00)); ++ break; ++ case EXYNOS5_FSEL_19MHZ2: ++ reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | ++ PHYCLKRST_SSC_REFCLKSEL(0x88)); ++ break; ++ default: ++ dev_dbg(phy_drd->dev, "unsupported ref clk\n"); ++ break; ++ } ++ ++ return reg; ++} ++ ++/* ++ * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock ++ * from clock core. Further sets the FSEL values for HighSpeed operations. ++ */ ++static unsigned int ++exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst) ++{ ++ u32 reg; ++ struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); ++ ++ /* restore any previous reference clock settings */ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); ++ ++ reg &= ~PHYCLKRST_REFCLKSEL_MASK; ++ reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; ++ ++ reg &= ~PHYCLKRST_FSEL_UTMI_MASK | ++ PHYCLKRST_MPLL_MULTIPLIER_MASK | ++ PHYCLKRST_SSC_REFCLKSEL_MASK; ++ reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); ++ ++ return reg; ++} ++ ++static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd) ++{ ++ u32 reg; ++ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); ++ /* Set Tx De-Emphasis level */ ++ reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; ++ reg |= PHYPARAM1_PCS_TXDEEMPH; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); ++ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); ++ reg &= ~PHYTEST_POWERDOWN_SSP; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); ++} ++ ++static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) ++{ ++ u32 reg; ++ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); ++ /* Set Loss-of-Signal Detector sensitivity */ ++ reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK; ++ reg |= PHYPARAM0_REF_LOSLEVEL; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); ++ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); ++ /* Set Tx De-Emphasis level */ ++ reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; ++ reg |= PHYPARAM1_PCS_TXDEEMPH; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); ++ ++ /* UTMI Power Control */ ++ writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); ++ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); ++ reg &= ~PHYTEST_POWERDOWN_HSP; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); ++} ++ ++static int exynos5_usbdrd_phy_init(struct phy *phy) ++{ ++ int ret; ++ u32 reg; ++ struct phy_usb_instance *inst = phy_get_drvdata(phy); ++ struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); ++ ++ ret = clk_prepare_enable(phy_drd->clk); ++ if (ret) ++ return ret; ++ ++ /* Reset USB 3.0 PHY */ ++ writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); ++ writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME); ++ ++ /* ++ * Setting the Frame length Adj value[6:1] to default 0x20 ++ * See xHCI 1.0 spec, 5.2.4 ++ */ ++ reg = LINKSYSTEM_XHCI_VERSION_CONTROL | ++ LINKSYSTEM_FLADJ(0x20); ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); ++ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); ++ /* Select PHY CLK source */ ++ reg &= ~PHYPARAM0_REF_USE_PAD; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); ++ ++ /* This bit must be set for both HS and SS operations */ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); ++ reg |= PHYUTMICLKSEL_UTMI_CLKSEL; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); ++ ++ /* UTMI or PIPE3 specific init */ ++ inst->phy_cfg->phy_init(phy_drd); ++ ++ /* reference clock settings */ ++ reg = inst->phy_cfg->set_refclk(inst); ++ ++ /* Digital power supply in normal operating mode */ ++ reg |= PHYCLKRST_RETENABLEN | ++ /* Enable ref clock for SS function */ ++ PHYCLKRST_REF_SSP_EN | ++ /* Enable spread spectrum */ ++ PHYCLKRST_SSC_EN | ++ /* Power down HS Bias and PLL blocks in suspend mode */ ++ PHYCLKRST_COMMONONN | ++ /* Reset the port */ ++ PHYCLKRST_PORTRESET; ++ ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); ++ ++ udelay(10); ++ ++ reg &= ~PHYCLKRST_PORTRESET; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); ++ ++ clk_disable_unprepare(phy_drd->clk); ++ ++ return 0; ++} ++ ++static int exynos5_usbdrd_phy_exit(struct phy *phy) ++{ ++ int ret; ++ u32 reg; ++ struct phy_usb_instance *inst = phy_get_drvdata(phy); ++ struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); ++ ++ ret = clk_prepare_enable(phy_drd->clk); ++ if (ret) ++ return ret; ++ ++ reg = PHYUTMI_OTGDISABLE | ++ PHYUTMI_FORCESUSPEND | ++ PHYUTMI_FORCESLEEP; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); ++ ++ /* Resetting the PHYCLKRST enable bits to reduce leakage current */ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); ++ reg &= ~(PHYCLKRST_REF_SSP_EN | ++ PHYCLKRST_SSC_EN | ++ PHYCLKRST_COMMONONN); ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); ++ ++ /* Control PHYTEST to remove leakage current */ ++ reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); ++ reg |= PHYTEST_POWERDOWN_SSP | ++ PHYTEST_POWERDOWN_HSP; ++ writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); ++ ++ clk_disable_unprepare(phy_drd->clk); ++ ++ return 0; ++} ++ ++static int exynos5_usbdrd_phy_power_on(struct phy *phy) ++{ ++ int ret; ++ struct phy_usb_instance *inst = phy_get_drvdata(phy); ++ struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); ++ ++ dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); ++ ++ clk_prepare_enable(phy_drd->ref_clk); ++ if (!phy_drd->drv_data->has_common_clk_gate) { ++ clk_prepare_enable(phy_drd->pipeclk); ++ clk_prepare_enable(phy_drd->utmiclk); ++ clk_prepare_enable(phy_drd->itpclk); ++ } ++ ++ /* Enable VBUS supply */ ++ if (phy_drd->vbus_boost) { ++ ret = regulator_enable(phy_drd->vbus_boost); ++ if (ret) { ++ dev_err(phy_drd->dev, ++ "Failed to enable VBUS boost supply\n"); ++ goto fail_vbus; ++ } ++ } ++ ++ if (phy_drd->vbus) { ++ ret = regulator_enable(phy_drd->vbus); ++ if (ret) { ++ dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); ++ goto fail_vbus_boost; ++ } ++ } ++ ++ /* Power-on PHY*/ ++ inst->phy_cfg->phy_isol(inst, 0); ++ ++ return 0; ++ ++fail_vbus_boost: ++ if (phy_drd->vbus_boost) ++ regulator_disable(phy_drd->vbus_boost); ++ ++fail_vbus: ++ clk_disable_unprepare(phy_drd->ref_clk); ++ if (!phy_drd->drv_data->has_common_clk_gate) { ++ clk_disable_unprepare(phy_drd->itpclk); ++ clk_disable_unprepare(phy_drd->utmiclk); ++ clk_disable_unprepare(phy_drd->pipeclk); ++ } ++ ++ return ret; ++} ++ ++static int exynos5_usbdrd_phy_power_off(struct phy *phy) ++{ ++ struct phy_usb_instance *inst = phy_get_drvdata(phy); ++ struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); ++ ++ dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); ++ ++ /* Power-off the PHY */ ++ inst->phy_cfg->phy_isol(inst, 1); ++ ++ /* Disable VBUS supply */ ++ if (phy_drd->vbus) ++ regulator_disable(phy_drd->vbus); ++ if (phy_drd->vbus_boost) ++ regulator_disable(phy_drd->vbus_boost); ++ ++ clk_disable_unprepare(phy_drd->ref_clk); ++ if (!phy_drd->drv_data->has_common_clk_gate) { ++ clk_disable_unprepare(phy_drd->itpclk); ++ clk_disable_unprepare(phy_drd->pipeclk); ++ clk_disable_unprepare(phy_drd->utmiclk); ++ } ++ ++ return 0; ++} ++ ++static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev); ++ ++ if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM)) ++ return ERR_PTR(-ENODEV); ++ ++ return phy_drd->phys[args->args[0]].phy; ++} ++ ++static const struct phy_ops exynos5_usbdrd_phy_ops = { ++ .init = exynos5_usbdrd_phy_init, ++ .exit = exynos5_usbdrd_phy_exit, ++ .power_on = exynos5_usbdrd_phy_power_on, ++ .power_off = exynos5_usbdrd_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) ++{ ++ unsigned long ref_rate; ++ int ret; ++ ++ phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); ++ if (IS_ERR(phy_drd->clk)) { ++ dev_err(phy_drd->dev, "Failed to get phy clock\n"); ++ return PTR_ERR(phy_drd->clk); ++ } ++ ++ phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); ++ if (IS_ERR(phy_drd->ref_clk)) { ++ dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); ++ return PTR_ERR(phy_drd->ref_clk); ++ } ++ ref_rate = clk_get_rate(phy_drd->ref_clk); ++ ++ ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); ++ if (ret) { ++ dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", ++ ref_rate); ++ return ret; ++ } ++ ++ if (!phy_drd->drv_data->has_common_clk_gate) { ++ phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); ++ if (IS_ERR(phy_drd->pipeclk)) { ++ dev_info(phy_drd->dev, ++ "PIPE3 phy operational clock not specified\n"); ++ phy_drd->pipeclk = NULL; ++ } ++ ++ phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); ++ if (IS_ERR(phy_drd->utmiclk)) { ++ dev_info(phy_drd->dev, ++ "UTMI phy operational clock not specified\n"); ++ phy_drd->utmiclk = NULL; ++ } ++ ++ phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); ++ if (IS_ERR(phy_drd->itpclk)) { ++ dev_info(phy_drd->dev, ++ "ITP clock from main OSC not specified\n"); ++ phy_drd->itpclk = NULL; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { ++ { ++ .id = EXYNOS5_DRDPHY_UTMI, ++ .phy_isol = exynos5_usbdrd_phy_isol, ++ .phy_init = exynos5_usbdrd_utmi_init, ++ .set_refclk = exynos5_usbdrd_utmi_set_refclk, ++ }, ++ { ++ .id = EXYNOS5_DRDPHY_PIPE3, ++ .phy_isol = exynos5_usbdrd_phy_isol, ++ .phy_init = exynos5_usbdrd_pipe3_init, ++ .set_refclk = exynos5_usbdrd_pipe3_set_refclk, ++ }, ++}; ++ ++static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { ++ .phy_cfg = phy_cfg_exynos5, ++ .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, ++ .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, ++ .has_common_clk_gate = true, ++}; ++ ++static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { ++ .phy_cfg = phy_cfg_exynos5, ++ .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, ++ .has_common_clk_gate = true, ++}; ++ ++static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = { ++ .phy_cfg = phy_cfg_exynos5, ++ .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, ++ .pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL, ++ .has_common_clk_gate = false, ++}; ++ ++static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { ++ .phy_cfg = phy_cfg_exynos5, ++ .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, ++ .has_common_clk_gate = false, ++}; ++ ++static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { ++ { ++ .compatible = "samsung,exynos5250-usbdrd-phy", ++ .data = &exynos5250_usbdrd_phy ++ }, { ++ .compatible = "samsung,exynos5420-usbdrd-phy", ++ .data = &exynos5420_usbdrd_phy ++ }, { ++ .compatible = "samsung,exynos5433-usbdrd-phy", ++ .data = &exynos5433_usbdrd_phy ++ }, { ++ .compatible = "samsung,exynos7-usbdrd-phy", ++ .data = &exynos7_usbdrd_phy ++ }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match); ++ ++static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct exynos5_usbdrd_phy *phy_drd; ++ struct phy_provider *phy_provider; ++ struct resource *res; ++ const struct of_device_id *match; ++ const struct exynos5_usbdrd_phy_drvdata *drv_data; ++ struct regmap *reg_pmu; ++ u32 pmu_offset; ++ int i, ret; ++ int channel; ++ ++ phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL); ++ if (!phy_drd) ++ return -ENOMEM; ++ ++ dev_set_drvdata(dev, phy_drd); ++ phy_drd->dev = dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy_drd->reg_phy = devm_ioremap_resource(dev, res); ++ if (IS_ERR(phy_drd->reg_phy)) ++ return PTR_ERR(phy_drd->reg_phy); ++ ++ match = of_match_node(exynos5_usbdrd_phy_of_match, pdev->dev.of_node); ++ ++ drv_data = match->data; ++ phy_drd->drv_data = drv_data; ++ ++ ret = exynos5_usbdrd_phy_clk_handle(phy_drd); ++ if (ret) { ++ dev_err(dev, "Failed to initialize clocks\n"); ++ return ret; ++ } ++ ++ 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); ++ } ++ ++ /* ++ * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with ++ * each having separate power control registers. ++ * 'channel' facilitates to set such registers. ++ */ ++ channel = of_alias_get_id(node, "usbdrdphy"); ++ if (channel < 0) ++ dev_dbg(dev, "Not a multi-controller usbdrd phy\n"); ++ ++ switch (channel) { ++ case 1: ++ pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy; ++ break; ++ case 0: ++ default: ++ pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy; ++ break; ++ } ++ ++ /* Get Vbus regulators */ ++ phy_drd->vbus = devm_regulator_get(dev, "vbus"); ++ if (IS_ERR(phy_drd->vbus)) { ++ ret = PTR_ERR(phy_drd->vbus); ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ ++ dev_warn(dev, "Failed to get VBUS supply regulator\n"); ++ phy_drd->vbus = NULL; ++ } ++ ++ phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); ++ if (IS_ERR(phy_drd->vbus_boost)) { ++ ret = PTR_ERR(phy_drd->vbus_boost); ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ ++ dev_warn(dev, "Failed to get VBUS boost supply regulator\n"); ++ phy_drd->vbus_boost = NULL; ++ } ++ ++ dev_vdbg(dev, "Creating usbdrd_phy phy\n"); ++ ++ for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { ++ struct phy *phy = devm_phy_create(dev, NULL, ++ &exynos5_usbdrd_phy_ops); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "Failed to create usbdrd_phy phy\n"); ++ return PTR_ERR(phy); ++ } ++ ++ phy_drd->phys[i].phy = phy; ++ phy_drd->phys[i].index = i; ++ phy_drd->phys[i].reg_pmu = reg_pmu; ++ phy_drd->phys[i].pmu_offset = pmu_offset; ++ phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; ++ phy_set_drvdata(phy, &phy_drd->phys[i]); ++ } ++ ++ 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); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver exynos5_usb3drd_phy = { ++ .probe = exynos5_usbdrd_phy_probe, ++ .driver = { ++ .of_match_table = exynos5_usbdrd_phy_of_match, ++ .name = "exynos5_usb3drd_phy", ++ } ++}; ++ ++module_platform_driver(exynos5_usb3drd_phy); ++MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver"); ++MODULE_AUTHOR("Vivek Gautam "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:exynos5_usb3drd_phy"); +diff --git a/drivers/phy/samsung/phy-exynos5250-sata.c b/drivers/phy/samsung/phy-exynos5250-sata.c +new file mode 100644 +index 000000000000..60e13afcd9b8 +--- /dev/null ++++ b/drivers/phy/samsung/phy-exynos5250-sata.c +@@ -0,0 +1,250 @@ ++/* ++ * Samsung SATA SerDes(PHY) driver ++ * ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Authors: Girish K S ++ * Yuvaraj Kumar C D ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SATAPHY_CONTROL_OFFSET 0x0724 ++#define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0) ++#define EXYNOS5_SATA_RESET 0x4 ++#define RESET_GLOBAL_RST_N BIT(0) ++#define RESET_CMN_RST_N BIT(1) ++#define RESET_CMN_BLOCK_RST_N BIT(2) ++#define RESET_CMN_I2C_RST_N BIT(3) ++#define RESET_TX_RX_PIPE_RST_N BIT(4) ++#define RESET_TX_RX_BLOCK_RST_N BIT(5) ++#define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7)) ++#define LINK_RESET 0xf0000 ++#define EXYNOS5_SATA_MODE0 0x10 ++#define SATA_SPD_GEN3 BIT(1) ++#define EXYNOS5_SATA_CTRL0 0x14 ++#define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9) ++#define CTRL0_P0_PHY_CALIBRATED BIT(8) ++#define EXYNOS5_SATA_PHSATA_CTRLM 0xe0 ++#define PHCTRLM_REF_RATE BIT(1) ++#define PHCTRLM_HIGH_SPEED BIT(0) ++#define EXYNOS5_SATA_PHSATA_STATM 0xf0 ++#define PHSTATM_PLL_LOCKED BIT(0) ++ ++#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000)) ++ ++struct exynos_sata_phy { ++ struct phy *phy; ++ struct clk *phyclk; ++ void __iomem *regs; ++ struct regmap *pmureg; ++ struct i2c_client *client; ++}; ++ ++static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit, ++ u32 status) ++{ ++ unsigned long timeout = jiffies + PHY_PLL_TIMEOUT; ++ ++ while (time_before(jiffies, timeout)) { ++ if ((readl(base + reg) & checkbit) == status) ++ return 0; ++ } ++ ++ return -EFAULT; ++} ++ ++static int exynos_sata_phy_power_on(struct phy *phy) ++{ ++ struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); ++ ++ return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, ++ EXYNOS5_SATAPHY_PMU_ENABLE, true); ++ ++} ++ ++static int exynos_sata_phy_power_off(struct phy *phy) ++{ ++ struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); ++ ++ return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, ++ EXYNOS5_SATAPHY_PMU_ENABLE, false); ++ ++} ++ ++static int exynos_sata_phy_init(struct phy *phy) ++{ ++ u32 val = 0; ++ int ret = 0; ++ u8 buf[] = { 0x3a, 0x0b }; ++ struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); ++ ++ ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, ++ EXYNOS5_SATAPHY_PMU_ENABLE, true); ++ if (ret != 0) ++ dev_err(&sata_phy->phy->dev, "phy init failed\n"); ++ ++ writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); ++ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); ++ val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N ++ | RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N ++ | RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); ++ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); ++ val |= LINK_RESET; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); ++ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); ++ val |= RESET_CMN_RST_N; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); ++ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); ++ val &= ~PHCTRLM_REF_RATE; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); ++ ++ /* High speed enable for Gen3 */ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); ++ val |= PHCTRLM_HIGH_SPEED; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); ++ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0); ++ val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0); ++ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0); ++ val |= SATA_SPD_GEN3; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0); ++ ++ ret = i2c_master_send(sata_phy->client, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ /* release cmu reset */ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); ++ val &= ~RESET_CMN_RST_N; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); ++ ++ val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); ++ val |= RESET_CMN_RST_N; ++ writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); ++ ++ ret = wait_for_reg_status(sata_phy->regs, ++ EXYNOS5_SATA_PHSATA_STATM, ++ PHSTATM_PLL_LOCKED, 1); ++ if (ret < 0) ++ dev_err(&sata_phy->phy->dev, ++ "PHY PLL locking failed\n"); ++ return ret; ++} ++ ++static const struct phy_ops exynos_sata_phy_ops = { ++ .init = exynos_sata_phy_init, ++ .power_on = exynos_sata_phy_power_on, ++ .power_off = exynos_sata_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int exynos_sata_phy_probe(struct platform_device *pdev) ++{ ++ struct exynos_sata_phy *sata_phy; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ struct phy_provider *phy_provider; ++ struct device_node *node; ++ int ret = 0; ++ ++ sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL); ++ if (!sata_phy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ sata_phy->regs = devm_ioremap_resource(dev, res); ++ if (IS_ERR(sata_phy->regs)) ++ return PTR_ERR(sata_phy->regs); ++ ++ sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "samsung,syscon-phandle"); ++ if (IS_ERR(sata_phy->pmureg)) { ++ dev_err(dev, "syscon regmap lookup failed.\n"); ++ return PTR_ERR(sata_phy->pmureg); ++ } ++ ++ node = of_parse_phandle(dev->of_node, ++ "samsung,exynos-sataphy-i2c-phandle", 0); ++ if (!node) ++ return -EINVAL; ++ ++ sata_phy->client = of_find_i2c_device_by_node(node); ++ if (!sata_phy->client) ++ return -EPROBE_DEFER; ++ ++ dev_set_drvdata(dev, sata_phy); ++ ++ sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl"); ++ if (IS_ERR(sata_phy->phyclk)) { ++ dev_err(dev, "failed to get clk for PHY\n"); ++ return PTR_ERR(sata_phy->phyclk); ++ } ++ ++ ret = clk_prepare_enable(sata_phy->phyclk); ++ if (ret < 0) { ++ dev_err(dev, "failed to enable source clk\n"); ++ return ret; ++ } ++ ++ sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops); ++ if (IS_ERR(sata_phy->phy)) { ++ clk_disable_unprepare(sata_phy->phyclk); ++ dev_err(dev, "failed to create PHY\n"); ++ return PTR_ERR(sata_phy->phy); ++ } ++ ++ phy_set_drvdata(sata_phy->phy, sata_phy); ++ ++ phy_provider = devm_of_phy_provider_register(dev, ++ of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ clk_disable_unprepare(sata_phy->phyclk); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id exynos_sata_phy_of_match[] = { ++ { .compatible = "samsung,exynos5250-sata-phy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match); ++ ++static struct platform_driver exynos_sata_phy_driver = { ++ .probe = exynos_sata_phy_probe, ++ .driver = { ++ .of_match_table = exynos_sata_phy_of_match, ++ .name = "samsung,sata-phy", ++ } ++}; ++module_platform_driver(exynos_sata_phy_driver); ++ ++MODULE_DESCRIPTION("Samsung SerDes PHY driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Girish K S "); ++MODULE_AUTHOR("Yuvaraj C D "); +diff --git a/drivers/phy/samsung/phy-exynos5250-usb2.c b/drivers/phy/samsung/phy-exynos5250-usb2.c +new file mode 100644 +index 000000000000..aad806272305 +--- /dev/null ++++ b/drivers/phy/samsung/phy-exynos5250-usb2.c +@@ -0,0 +1,401 @@ ++/* ++ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support ++ * ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Author: Kamil Debski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "phy-samsung-usb2.h" ++ ++/* Exynos USB PHY registers */ ++#define EXYNOS_5250_REFCLKSEL_CRYSTAL 0x0 ++#define EXYNOS_5250_REFCLKSEL_XO 0x1 ++#define EXYNOS_5250_REFCLKSEL_CLKCORE 0x2 ++ ++#define EXYNOS_5250_FSEL_9MHZ6 0x0 ++#define EXYNOS_5250_FSEL_10MHZ 0x1 ++#define EXYNOS_5250_FSEL_12MHZ 0x2 ++#define EXYNOS_5250_FSEL_19MHZ2 0x3 ++#define EXYNOS_5250_FSEL_20MHZ 0x4 ++#define EXYNOS_5250_FSEL_24MHZ 0x5 ++#define EXYNOS_5250_FSEL_50MHZ 0x7 ++ ++/* Normal host */ ++#define EXYNOS_5250_HOSTPHYCTRL0 0x0 ++ ++#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL BIT(31) ++#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT 19 ++#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK \ ++ (0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT) ++#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT 16 ++#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \ ++ (0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT) ++#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN BIT(11) ++#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE BIT(10) ++#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N BIT(9) ++#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK (0x3 << 7) ++#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL (0x0 << 7) ++#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0 (0x1 << 7) ++#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST (0x2 << 7) ++#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ BIT(6) ++#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP BIT(5) ++#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND BIT(4) ++#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE BIT(3) ++#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST BIT(2) ++#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST BIT(1) ++#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST BIT(0) ++ ++/* HSIC0 & HSIC1 */ ++#define EXYNOS_5250_HSICPHYCTRL1 0x10 ++#define EXYNOS_5250_HSICPHYCTRL2 0x20 ++ ++#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK (0x3 << 23) ++#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT (0x2 << 23) ++#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK (0x7f << 16) ++#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 (0x24 << 16) ++#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15 (0x1c << 16) ++#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16 (0x1a << 16) ++#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2 (0x15 << 16) ++#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20 (0x14 << 16) ++#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ BIT(6) ++#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP BIT(5) ++#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND BIT(4) ++#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE BIT(3) ++#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST BIT(2) ++#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST BIT(0) ++ ++/* EHCI control */ ++#define EXYNOS_5250_HOSTEHCICTRL 0x30 ++#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN BIT(29) ++#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 BIT(28) ++#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 BIT(27) ++#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16 BIT(26) ++#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN BIT(25) ++#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT 19 ++#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \ ++ (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT) ++#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT 13 ++#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK \ ++ (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT) ++#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT 7 ++#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \ ++ (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT) ++#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT 1 ++#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \ ++ (0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT) ++#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE BIT(0) ++ ++/* OHCI control */ ++#define EXYNOS_5250_HOSTOHCICTRL 0x34 ++#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT 1 ++#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \ ++ (0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT) ++#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN BIT(0) ++ ++/* USBOTG */ ++#define EXYNOS_5250_USBOTGSYS 0x38 ++#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET BIT(14) ++#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG BIT(13) ++#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST BIT(12) ++#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT 9 ++#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \ ++ (0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT) ++#define EXYNOS_5250_USBOTGSYS_ID_PULLUP BIT(8) ++#define EXYNOS_5250_USBOTGSYS_COMMON_ON BIT(7) ++#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT 4 ++#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \ ++ (0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT) ++#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP BIT(3) ++#define EXYNOS_5250_USBOTGSYS_OTGDISABLE BIT(2) ++#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG BIT(1) ++#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND BIT(0) ++ ++/* Isolation, configured in the power management unit */ ++#define EXYNOS_5250_USB_ISOL_OTG_OFFSET 0x704 ++#define EXYNOS_5250_USB_ISOL_OTG BIT(0) ++#define EXYNOS_5250_USB_ISOL_HOST_OFFSET 0x708 ++#define EXYNOS_5250_USB_ISOL_HOST BIT(0) ++ ++/* Mode swtich register */ ++#define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230 ++#define EXYNOS_5250_MODE_SWITCH_MASK 1 ++#define EXYNOS_5250_MODE_SWITCH_DEVICE 0 ++#define EXYNOS_5250_MODE_SWITCH_HOST 1 ++ ++enum exynos4x12_phy_id { ++ EXYNOS5250_DEVICE, ++ EXYNOS5250_HOST, ++ EXYNOS5250_HSIC0, ++ EXYNOS5250_HSIC1, ++ EXYNOS5250_NUM_PHYS, ++}; ++ ++/* ++ * exynos5250_rate_to_clk() converts the supplied clock rate to the value that ++ * can be written to the phy register. ++ */ ++static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg) ++{ ++ /* EXYNOS_5250_FSEL_MASK */ ++ ++ switch (rate) { ++ case 9600 * KHZ: ++ *reg = EXYNOS_5250_FSEL_9MHZ6; ++ break; ++ case 10 * MHZ: ++ *reg = EXYNOS_5250_FSEL_10MHZ; ++ break; ++ case 12 * MHZ: ++ *reg = EXYNOS_5250_FSEL_12MHZ; ++ break; ++ case 19200 * KHZ: ++ *reg = EXYNOS_5250_FSEL_19MHZ2; ++ break; ++ case 20 * MHZ: ++ *reg = EXYNOS_5250_FSEL_20MHZ; ++ break; ++ case 24 * MHZ: ++ *reg = EXYNOS_5250_FSEL_24MHZ; ++ break; ++ case 50 * MHZ: ++ *reg = EXYNOS_5250_FSEL_50MHZ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 offset; ++ u32 mask; ++ ++ switch (inst->cfg->id) { ++ case EXYNOS5250_DEVICE: ++ offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET; ++ mask = EXYNOS_5250_USB_ISOL_OTG; ++ break; ++ case EXYNOS5250_HOST: ++ offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET; ++ mask = EXYNOS_5250_USB_ISOL_HOST; ++ break; ++ default: ++ return; ++ } ++ ++ regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); ++} ++ ++static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 ctrl0; ++ u32 otg; ++ u32 ehci; ++ u32 ohci; ++ u32 hsic; ++ ++ switch (inst->cfg->id) { ++ case EXYNOS5250_DEVICE: ++ regmap_update_bits(drv->reg_sys, ++ EXYNOS_5250_MODE_SWITCH_OFFSET, ++ EXYNOS_5250_MODE_SWITCH_MASK, ++ EXYNOS_5250_MODE_SWITCH_DEVICE); ++ ++ /* OTG configuration */ ++ otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); ++ /* The clock */ ++ otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK; ++ otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT; ++ /* Reset */ ++ otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | ++ EXYNOS_5250_USBOTGSYS_FORCE_SLEEP | ++ EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG); ++ otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST | ++ EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | ++ EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | ++ EXYNOS_5250_USBOTGSYS_OTGDISABLE; ++ /* Ref clock */ ++ otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK; ++ otg |= EXYNOS_5250_REFCLKSEL_CLKCORE << ++ EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT; ++ writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); ++ udelay(100); ++ otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST | ++ EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | ++ EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | ++ EXYNOS_5250_USBOTGSYS_OTGDISABLE); ++ writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); ++ ++ ++ break; ++ case EXYNOS5250_HOST: ++ case EXYNOS5250_HSIC0: ++ case EXYNOS5250_HSIC1: ++ /* Host registers configuration */ ++ ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); ++ /* The clock */ ++ ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK; ++ ctrl0 |= drv->ref_reg_val << ++ EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT; ++ ++ /* Reset */ ++ ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST | ++ EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL | ++ EXYNOS_5250_HOSTPHYCTRL0_SIDDQ | ++ EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND | ++ EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP); ++ ctrl0 |= EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST | ++ EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST | ++ EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N; ++ writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); ++ udelay(10); ++ ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST | ++ EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST); ++ writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); ++ ++ /* OTG configuration */ ++ otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); ++ /* The clock */ ++ otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK; ++ otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT; ++ /* Reset */ ++ otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | ++ EXYNOS_5250_USBOTGSYS_FORCE_SLEEP | ++ EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG); ++ otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST | ++ EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | ++ EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | ++ EXYNOS_5250_USBOTGSYS_OTGDISABLE; ++ /* Ref clock */ ++ otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK; ++ otg |= EXYNOS_5250_REFCLKSEL_CLKCORE << ++ EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT; ++ writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); ++ udelay(10); ++ otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST | ++ EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | ++ EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET); ++ ++ /* HSIC phy configuration */ ++ hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 | ++ EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT | ++ EXYNOS_5250_HSICPHYCTRLX_PHYSWRST); ++ writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); ++ writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); ++ udelay(10); ++ hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST; ++ writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); ++ writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); ++ /* The following delay is necessary for the reset sequence to be ++ * completed */ ++ udelay(80); ++ ++ /* Enable EHCI DMA burst */ ++ ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL); ++ ehci |= EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN | ++ EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 | ++ EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 | ++ EXYNOS_5250_HOSTEHCICTRL_ENAINCR16; ++ writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL); ++ ++ /* OHCI settings */ ++ ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL); ++ /* Following code is based on the old driver */ ++ ohci |= 0x1 << 3; ++ writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL); ++ ++ break; ++ } ++ exynos5250_isol(inst, 0); ++ ++ return 0; ++} ++ ++static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 ctrl0; ++ u32 otg; ++ u32 hsic; ++ ++ exynos5250_isol(inst, 1); ++ ++ switch (inst->cfg->id) { ++ case EXYNOS5250_DEVICE: ++ otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); ++ otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | ++ EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG | ++ EXYNOS_5250_USBOTGSYS_FORCE_SLEEP); ++ writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); ++ break; ++ case EXYNOS5250_HOST: ++ ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); ++ ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ | ++ EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND | ++ EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP | ++ EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST | ++ EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL); ++ writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); ++ break; ++ case EXYNOS5250_HSIC0: ++ case EXYNOS5250_HSIC1: ++ hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 | ++ EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT | ++ EXYNOS_5250_HSICPHYCTRLX_SIDDQ | ++ EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP | ++ EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND ++ ); ++ writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); ++ writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); ++ break; ++ } ++ ++ return 0; ++} ++ ++ ++static const struct samsung_usb2_common_phy exynos5250_phys[] = { ++ { ++ .label = "device", ++ .id = EXYNOS5250_DEVICE, ++ .power_on = exynos5250_power_on, ++ .power_off = exynos5250_power_off, ++ }, ++ { ++ .label = "host", ++ .id = EXYNOS5250_HOST, ++ .power_on = exynos5250_power_on, ++ .power_off = exynos5250_power_off, ++ }, ++ { ++ .label = "hsic0", ++ .id = EXYNOS5250_HSIC0, ++ .power_on = exynos5250_power_on, ++ .power_off = exynos5250_power_off, ++ }, ++ { ++ .label = "hsic1", ++ .id = EXYNOS5250_HSIC1, ++ .power_on = exynos5250_power_on, ++ .power_off = exynos5250_power_off, ++ }, ++}; ++ ++const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = { ++ .has_mode_switch = 1, ++ .num_phys = EXYNOS5250_NUM_PHYS, ++ .phys = exynos5250_phys, ++ .rate_to_clk = exynos5250_rate_to_clk, ++}; +diff --git a/drivers/phy/samsung/phy-s5pv210-usb2.c b/drivers/phy/samsung/phy-s5pv210-usb2.c +new file mode 100644 +index 000000000000..f6f72339bbc3 +--- /dev/null ++++ b/drivers/phy/samsung/phy-s5pv210-usb2.c +@@ -0,0 +1,187 @@ ++/* ++ * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support ++ * ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Authors: Kamil Debski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include "phy-samsung-usb2.h" ++ ++/* Exynos USB PHY registers */ ++ ++/* PHY power control */ ++#define S5PV210_UPHYPWR 0x0 ++ ++#define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0) ++#define S5PV210_UPHYPWR_PHY0_PWR BIT(3) ++#define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4) ++#define S5PV210_UPHYPWR_PHY0 ( \ ++ S5PV210_UPHYPWR_PHY0_SUSPEND | \ ++ S5PV210_UPHYPWR_PHY0_PWR | \ ++ S5PV210_UPHYPWR_PHY0_OTG_PWR) ++ ++#define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6) ++#define S5PV210_UPHYPWR_PHY1_PWR BIT(7) ++#define S5PV210_UPHYPWR_PHY1 ( \ ++ S5PV210_UPHYPWR_PHY1_SUSPEND | \ ++ S5PV210_UPHYPWR_PHY1_PWR) ++ ++/* PHY clock control */ ++#define S5PV210_UPHYCLK 0x4 ++ ++#define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) ++#define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) ++#define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) ++#define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) ++ ++#define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2) ++#define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4) ++#define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7) ++ ++/* PHY reset control */ ++#define S5PV210_UPHYRST 0x8 ++ ++#define S5PV210_URSTCON_PHY0 BIT(0) ++#define S5PV210_URSTCON_OTG_HLINK BIT(1) ++#define S5PV210_URSTCON_OTG_PHYLINK BIT(2) ++#define S5PV210_URSTCON_PHY1_ALL BIT(3) ++#define S5PV210_URSTCON_HOST_LINK_ALL BIT(4) ++ ++/* Isolation, configured in the power management unit */ ++#define S5PV210_USB_ISOL_OFFSET 0x680c ++#define S5PV210_USB_ISOL_DEVICE BIT(0) ++#define S5PV210_USB_ISOL_HOST BIT(1) ++ ++ ++enum s5pv210_phy_id { ++ S5PV210_DEVICE, ++ S5PV210_HOST, ++ S5PV210_NUM_PHYS, ++}; ++ ++/* ++ * s5pv210_rate_to_clk() converts the supplied clock rate to the value that ++ * can be written to the phy register. ++ */ ++static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg) ++{ ++ switch (rate) { ++ case 12 * MHZ: ++ *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ; ++ break; ++ case 24 * MHZ: ++ *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ; ++ break; ++ case 48 * MHZ: ++ *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 mask; ++ ++ switch (inst->cfg->id) { ++ case S5PV210_DEVICE: ++ mask = S5PV210_USB_ISOL_DEVICE; ++ break; ++ case S5PV210_HOST: ++ mask = S5PV210_USB_ISOL_HOST; ++ break; ++ default: ++ return; ++ } ++ ++ regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, ++ mask, on ? 0 : mask); ++} ++ ++static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) ++{ ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ u32 rstbits = 0; ++ u32 phypwr = 0; ++ u32 rst; ++ u32 pwr; ++ ++ switch (inst->cfg->id) { ++ case S5PV210_DEVICE: ++ phypwr = S5PV210_UPHYPWR_PHY0; ++ rstbits = S5PV210_URSTCON_PHY0; ++ break; ++ case S5PV210_HOST: ++ phypwr = S5PV210_UPHYPWR_PHY1; ++ rstbits = S5PV210_URSTCON_PHY1_ALL | ++ S5PV210_URSTCON_HOST_LINK_ALL; ++ break; ++ } ++ ++ if (on) { ++ writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); ++ ++ pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); ++ pwr &= ~phypwr; ++ writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); ++ ++ rst = readl(drv->reg_phy + S5PV210_UPHYRST); ++ rst |= rstbits; ++ writel(rst, drv->reg_phy + S5PV210_UPHYRST); ++ udelay(10); ++ rst &= ~rstbits; ++ writel(rst, drv->reg_phy + S5PV210_UPHYRST); ++ } else { ++ pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); ++ pwr |= phypwr; ++ writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); ++ } ++} ++ ++static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst) ++{ ++ s5pv210_isol(inst, 0); ++ s5pv210_phy_pwr(inst, 1); ++ ++ return 0; ++} ++ ++static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst) ++{ ++ s5pv210_phy_pwr(inst, 0); ++ s5pv210_isol(inst, 1); ++ ++ return 0; ++} ++ ++static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = { ++ [S5PV210_DEVICE] = { ++ .label = "device", ++ .id = S5PV210_DEVICE, ++ .power_on = s5pv210_power_on, ++ .power_off = s5pv210_power_off, ++ }, ++ [S5PV210_HOST] = { ++ .label = "host", ++ .id = S5PV210_HOST, ++ .power_on = s5pv210_power_on, ++ .power_off = s5pv210_power_off, ++ }, ++}; ++ ++const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = { ++ .num_phys = ARRAY_SIZE(s5pv210_phys), ++ .phys = s5pv210_phys, ++ .rate_to_clk = s5pv210_rate_to_clk, ++}; +diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c +new file mode 100644 +index 000000000000..1d22d93b552d +--- /dev/null ++++ b/drivers/phy/samsung/phy-samsung-usb2.c +@@ -0,0 +1,267 @@ ++/* ++ * Samsung SoC USB 1.1/2.0 PHY driver ++ * ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Author: Kamil Debski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "phy-samsung-usb2.h" ++ ++static int samsung_usb2_phy_power_on(struct phy *phy) ++{ ++ struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ int ret; ++ ++ dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n", ++ inst->cfg->label); ++ ++ if (drv->vbus) { ++ ret = regulator_enable(drv->vbus); ++ if (ret) ++ goto err_regulator; ++ } ++ ++ ret = clk_prepare_enable(drv->clk); ++ if (ret) ++ goto err_main_clk; ++ ret = clk_prepare_enable(drv->ref_clk); ++ if (ret) ++ goto err_instance_clk; ++ if (inst->cfg->power_on) { ++ spin_lock(&drv->lock); ++ ret = inst->cfg->power_on(inst); ++ spin_unlock(&drv->lock); ++ if (ret) ++ goto err_power_on; ++ } ++ ++ return 0; ++ ++err_power_on: ++ clk_disable_unprepare(drv->ref_clk); ++err_instance_clk: ++ clk_disable_unprepare(drv->clk); ++err_main_clk: ++ if (drv->vbus) ++ regulator_disable(drv->vbus); ++err_regulator: ++ return ret; ++} ++ ++static int samsung_usb2_phy_power_off(struct phy *phy) ++{ ++ struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); ++ struct samsung_usb2_phy_driver *drv = inst->drv; ++ int ret = 0; ++ ++ dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n", ++ inst->cfg->label); ++ if (inst->cfg->power_off) { ++ spin_lock(&drv->lock); ++ ret = inst->cfg->power_off(inst); ++ spin_unlock(&drv->lock); ++ if (ret) ++ return ret; ++ } ++ clk_disable_unprepare(drv->ref_clk); ++ clk_disable_unprepare(drv->clk); ++ if (drv->vbus) ++ ret = regulator_disable(drv->vbus); ++ ++ return ret; ++} ++ ++static const struct phy_ops samsung_usb2_phy_ops = { ++ .power_on = samsung_usb2_phy_power_on, ++ .power_off = samsung_usb2_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static struct phy *samsung_usb2_phy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct samsung_usb2_phy_driver *drv; ++ ++ drv = dev_get_drvdata(dev); ++ if (!drv) ++ return ERR_PTR(-EINVAL); ++ ++ if (WARN_ON(args->args[0] >= drv->cfg->num_phys)) ++ return ERR_PTR(-ENODEV); ++ ++ return drv->instances[args->args[0]].phy; ++} ++ ++static const struct of_device_id samsung_usb2_phy_of_match[] = { ++#ifdef CONFIG_PHY_EXYNOS4X12_USB2 ++ { ++ .compatible = "samsung,exynos3250-usb2-phy", ++ .data = &exynos3250_usb2_phy_config, ++ }, ++#endif ++#ifdef CONFIG_PHY_EXYNOS4210_USB2 ++ { ++ .compatible = "samsung,exynos4210-usb2-phy", ++ .data = &exynos4210_usb2_phy_config, ++ }, ++#endif ++#ifdef CONFIG_PHY_EXYNOS4X12_USB2 ++ { ++ .compatible = "samsung,exynos4x12-usb2-phy", ++ .data = &exynos4x12_usb2_phy_config, ++ }, ++#endif ++#ifdef CONFIG_PHY_EXYNOS5250_USB2 ++ { ++ .compatible = "samsung,exynos5250-usb2-phy", ++ .data = &exynos5250_usb2_phy_config, ++ }, ++#endif ++#ifdef CONFIG_PHY_S5PV210_USB2 ++ { ++ .compatible = "samsung,s5pv210-usb2-phy", ++ .data = &s5pv210_usb2_phy_config, ++ }, ++#endif ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, samsung_usb2_phy_of_match); ++ ++static int samsung_usb2_phy_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *match; ++ const struct samsung_usb2_phy_config *cfg; ++ struct device *dev = &pdev->dev; ++ struct phy_provider *phy_provider; ++ struct resource *mem; ++ struct samsung_usb2_phy_driver *drv; ++ int i, ret; ++ ++ if (!pdev->dev.of_node) { ++ dev_err(dev, "This driver is required to be instantiated from device tree\n"); ++ return -EINVAL; ++ } ++ ++ match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node); ++ if (!match) { ++ dev_err(dev, "of_match_node() failed\n"); ++ return -EINVAL; ++ } ++ cfg = match->data; ++ ++ drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) + ++ cfg->num_phys * sizeof(struct samsung_usb2_phy_instance), ++ GFP_KERNEL); ++ if (!drv) ++ return -ENOMEM; ++ ++ dev_set_drvdata(dev, drv); ++ spin_lock_init(&drv->lock); ++ ++ drv->cfg = cfg; ++ drv->dev = dev; ++ ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ drv->reg_phy = devm_ioremap_resource(dev, mem); ++ if (IS_ERR(drv->reg_phy)) { ++ dev_err(dev, "Failed to map register memory (phy)\n"); ++ return PTR_ERR(drv->reg_phy); ++ } ++ ++ drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ "samsung,pmureg-phandle"); ++ if (IS_ERR(drv->reg_pmu)) { ++ dev_err(dev, "Failed to map PMU registers (via syscon)\n"); ++ return PTR_ERR(drv->reg_pmu); ++ } ++ ++ if (drv->cfg->has_mode_switch) { ++ drv->reg_sys = syscon_regmap_lookup_by_phandle( ++ pdev->dev.of_node, "samsung,sysreg-phandle"); ++ if (IS_ERR(drv->reg_sys)) { ++ dev_err(dev, "Failed to map system registers (via syscon)\n"); ++ return PTR_ERR(drv->reg_sys); ++ } ++ } ++ ++ drv->clk = devm_clk_get(dev, "phy"); ++ if (IS_ERR(drv->clk)) { ++ dev_err(dev, "Failed to get clock of phy controller\n"); ++ return PTR_ERR(drv->clk); ++ } ++ ++ drv->ref_clk = devm_clk_get(dev, "ref"); ++ if (IS_ERR(drv->ref_clk)) { ++ dev_err(dev, "Failed to get reference clock for the phy controller\n"); ++ return PTR_ERR(drv->ref_clk); ++ } ++ ++ drv->ref_rate = clk_get_rate(drv->ref_clk); ++ if (drv->cfg->rate_to_clk) { ++ ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val); ++ if (ret) ++ return ret; ++ } ++ ++ drv->vbus = devm_regulator_get(dev, "vbus"); ++ if (IS_ERR(drv->vbus)) { ++ ret = PTR_ERR(drv->vbus); ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ drv->vbus = NULL; ++ } ++ ++ for (i = 0; i < drv->cfg->num_phys; i++) { ++ char *label = drv->cfg->phys[i].label; ++ struct samsung_usb2_phy_instance *p = &drv->instances[i]; ++ ++ dev_dbg(dev, "Creating phy \"%s\"\n", label); ++ p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops); ++ if (IS_ERR(p->phy)) { ++ dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", ++ label); ++ return PTR_ERR(p->phy); ++ } ++ ++ p->cfg = &drv->cfg->phys[i]; ++ p->drv = drv; ++ phy_set_bus_width(p->phy, 8); ++ phy_set_drvdata(p->phy, p); ++ } ++ ++ phy_provider = devm_of_phy_provider_register(dev, ++ samsung_usb2_phy_xlate); ++ if (IS_ERR(phy_provider)) { ++ dev_err(drv->dev, "Failed to register phy provider\n"); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver samsung_usb2_phy_driver = { ++ .probe = samsung_usb2_phy_probe, ++ .driver = { ++ .of_match_table = samsung_usb2_phy_of_match, ++ .name = "samsung-usb2-phy", ++ } ++}; ++ ++module_platform_driver(samsung_usb2_phy_driver); ++MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver"); ++MODULE_AUTHOR("Kamil Debski "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:samsung-usb2-phy"); +diff --git a/drivers/phy/samsung/phy-samsung-usb2.h b/drivers/phy/samsung/phy-samsung-usb2.h +new file mode 100644 +index 000000000000..6563e7ca0ac4 +--- /dev/null ++++ b/drivers/phy/samsung/phy-samsung-usb2.h +@@ -0,0 +1,73 @@ ++/* ++ * Samsung SoC USB 1.1/2.0 PHY driver ++ * ++ * Copyright (C) 2013 Samsung Electronics Co., Ltd. ++ * Author: Kamil Debski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _PHY_EXYNOS_USB2_H ++#define _PHY_EXYNOS_USB2_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define KHZ 1000 ++#define MHZ (KHZ * KHZ) ++ ++struct samsung_usb2_phy_driver; ++struct samsung_usb2_phy_instance; ++struct samsung_usb2_phy_config; ++ ++struct samsung_usb2_phy_instance { ++ const struct samsung_usb2_common_phy *cfg; ++ struct phy *phy; ++ struct samsung_usb2_phy_driver *drv; ++ int int_cnt; ++ int ext_cnt; ++}; ++ ++struct samsung_usb2_phy_driver { ++ const struct samsung_usb2_phy_config *cfg; ++ struct clk *clk; ++ struct clk *ref_clk; ++ struct regulator *vbus; ++ unsigned long ref_rate; ++ u32 ref_reg_val; ++ struct device *dev; ++ void __iomem *reg_phy; ++ struct regmap *reg_pmu; ++ struct regmap *reg_sys; ++ spinlock_t lock; ++ struct samsung_usb2_phy_instance instances[0]; ++}; ++ ++struct samsung_usb2_common_phy { ++ int (*power_on)(struct samsung_usb2_phy_instance *); ++ int (*power_off)(struct samsung_usb2_phy_instance *); ++ unsigned int id; ++ char *label; ++}; ++ ++ ++struct samsung_usb2_phy_config { ++ const struct samsung_usb2_common_phy *phys; ++ int (*rate_to_clk)(unsigned long, u32 *); ++ unsigned int num_phys; ++ bool has_mode_switch; ++ bool has_refclk_sel; ++}; ++ ++extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config; ++extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; ++extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; ++extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config; ++extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config; ++#endif +diff --git a/drivers/phy/st/Kconfig b/drivers/phy/st/Kconfig +new file mode 100644 +index 000000000000..0814d3f87ec6 +--- /dev/null ++++ b/drivers/phy/st/Kconfig +@@ -0,0 +1,33 @@ ++# ++# Phy drivers for STMicro platforms ++# ++config PHY_MIPHY28LP ++ tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407" ++ depends on ARCH_STI ++ select GENERIC_PHY ++ help ++ Enable this to support the miphy transceiver (for SATA/PCIE/USB3) ++ that is part of STMicroelectronics STiH407 SoC. ++ ++config PHY_ST_SPEAR1310_MIPHY ++ tristate "ST SPEAR1310-MIPHY driver" ++ select GENERIC_PHY ++ depends on MACH_SPEAR1310 || COMPILE_TEST ++ help ++ Support for ST SPEAr1310 MIPHY which can be used for PCIe and SATA. ++ ++config PHY_ST_SPEAR1340_MIPHY ++ tristate "ST SPEAR1340-MIPHY driver" ++ select GENERIC_PHY ++ depends on MACH_SPEAR1340 || COMPILE_TEST ++ help ++ Support for ST SPEAr1340 MIPHY which can be used for PCIe and SATA. ++ ++config PHY_STIH407_USB ++ tristate "STMicroelectronics USB2 picoPHY driver for STiH407 family" ++ depends on RESET_CONTROLLER ++ depends on ARCH_STI || COMPILE_TEST ++ select GENERIC_PHY ++ help ++ Enable this support to enable the picoPHY device used by USB2 ++ and USB3 controllers on STMicroelectronics STiH407 SoC families. +diff --git a/drivers/phy/st/Makefile b/drivers/phy/st/Makefile +new file mode 100644 +index 000000000000..e2adfe2166d2 +--- /dev/null ++++ b/drivers/phy/st/Makefile +@@ -0,0 +1,4 @@ ++obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o ++obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o ++obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o ++obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o +diff --git a/drivers/phy/st/phy-miphy28lp.c b/drivers/phy/st/phy-miphy28lp.c +new file mode 100644 +index 000000000000..213e2e15339c +--- /dev/null ++++ b/drivers/phy/st/phy-miphy28lp.c +@@ -0,0 +1,1286 @@ ++/* ++ * Copyright (C) 2014 STMicroelectronics ++ * ++ * STMicroelectronics PHY driver MiPHY28lp (for SoC STiH407). ++ * ++ * Author: Alexandre Torgue ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* MiPHY registers */ ++#define MIPHY_CONF_RESET 0x00 ++#define RST_APPLI_SW BIT(0) ++#define RST_CONF_SW BIT(1) ++#define RST_MACRO_SW BIT(2) ++ ++#define MIPHY_RESET 0x01 ++#define RST_PLL_SW BIT(0) ++#define RST_COMP_SW BIT(2) ++ ++#define MIPHY_STATUS_1 0x02 ++#define PHY_RDY BIT(0) ++#define HFC_RDY BIT(1) ++#define HFC_PLL BIT(2) ++ ++#define MIPHY_CONTROL 0x04 ++#define TERM_EN_SW BIT(2) ++#define DIS_LINK_RST BIT(3) ++#define AUTO_RST_RX BIT(4) ++#define PX_RX_POL BIT(5) ++ ++#define MIPHY_BOUNDARY_SEL 0x0a ++#define TX_SEL BIT(6) ++#define SSC_SEL BIT(4) ++#define GENSEL_SEL BIT(0) ++ ++#define MIPHY_BOUNDARY_1 0x0b ++#define MIPHY_BOUNDARY_2 0x0c ++#define SSC_EN_SW BIT(2) ++ ++#define MIPHY_PLL_CLKREF_FREQ 0x0d ++#define MIPHY_SPEED 0x0e ++#define TX_SPDSEL_80DEC 0 ++#define TX_SPDSEL_40DEC 1 ++#define TX_SPDSEL_20DEC 2 ++#define RX_SPDSEL_80DEC 0 ++#define RX_SPDSEL_40DEC (1 << 2) ++#define RX_SPDSEL_20DEC (2 << 2) ++ ++#define MIPHY_CONF 0x0f ++#define MIPHY_CTRL_TEST_SEL 0x20 ++#define MIPHY_CTRL_TEST_1 0x21 ++#define MIPHY_CTRL_TEST_2 0x22 ++#define MIPHY_CTRL_TEST_3 0x23 ++#define MIPHY_CTRL_TEST_4 0x24 ++#define MIPHY_FEEDBACK_TEST 0x25 ++#define MIPHY_DEBUG_BUS 0x26 ++#define MIPHY_DEBUG_STATUS_MSB 0x27 ++#define MIPHY_DEBUG_STATUS_LSB 0x28 ++#define MIPHY_PWR_RAIL_1 0x29 ++#define MIPHY_PWR_RAIL_2 0x2a ++#define MIPHY_SYNCHAR_CONTROL 0x30 ++ ++#define MIPHY_COMP_FSM_1 0x3a ++#define COMP_START BIT(6) ++ ++#define MIPHY_COMP_FSM_6 0x3f ++#define COMP_DONE BIT(7) ++ ++#define MIPHY_COMP_POSTP 0x42 ++#define MIPHY_TX_CTRL_1 0x49 ++#define TX_REG_STEP_0V 0 ++#define TX_REG_STEP_P_25MV 1 ++#define TX_REG_STEP_P_50MV 2 ++#define TX_REG_STEP_N_25MV 7 ++#define TX_REG_STEP_N_50MV 6 ++#define TX_REG_STEP_N_75MV 5 ++ ++#define MIPHY_TX_CTRL_2 0x4a ++#define TX_SLEW_SW_40_PS 0 ++#define TX_SLEW_SW_80_PS 1 ++#define TX_SLEW_SW_120_PS 2 ++ ++#define MIPHY_TX_CTRL_3 0x4b ++#define MIPHY_TX_CAL_MAN 0x4e ++#define TX_SLEW_CAL_MAN_EN BIT(0) ++ ++#define MIPHY_TST_BIAS_BOOST_2 0x62 ++#define MIPHY_BIAS_BOOST_1 0x63 ++#define MIPHY_BIAS_BOOST_2 0x64 ++#define MIPHY_RX_DESBUFF_FDB_2 0x67 ++#define MIPHY_RX_DESBUFF_FDB_3 0x68 ++#define MIPHY_SIGDET_COMPENS1 0x69 ++#define MIPHY_SIGDET_COMPENS2 0x6a ++#define MIPHY_JITTER_PERIOD 0x6b ++#define MIPHY_JITTER_AMPLITUDE_1 0x6c ++#define MIPHY_JITTER_AMPLITUDE_2 0x6d ++#define MIPHY_JITTER_AMPLITUDE_3 0x6e ++#define MIPHY_RX_K_GAIN 0x78 ++#define MIPHY_RX_BUFFER_CTRL 0x7a ++#define VGA_GAIN BIT(0) ++#define EQ_DC_GAIN BIT(2) ++#define EQ_BOOST_GAIN BIT(3) ++ ++#define MIPHY_RX_VGA_GAIN 0x7b ++#define MIPHY_RX_EQU_GAIN_1 0x7f ++#define MIPHY_RX_EQU_GAIN_2 0x80 ++#define MIPHY_RX_EQU_GAIN_3 0x81 ++#define MIPHY_RX_CAL_CTRL_1 0x97 ++#define MIPHY_RX_CAL_CTRL_2 0x98 ++ ++#define MIPHY_RX_CAL_OFFSET_CTRL 0x99 ++#define CAL_OFFSET_VGA_64 (0x03 << 0) ++#define CAL_OFFSET_THRESHOLD_64 (0x03 << 2) ++#define VGA_OFFSET_POLARITY BIT(4) ++#define OFFSET_COMPENSATION_EN BIT(6) ++ ++#define MIPHY_RX_CAL_VGA_STEP 0x9a ++#define MIPHY_RX_CAL_EYE_MIN 0x9d ++#define MIPHY_RX_CAL_OPT_LENGTH 0x9f ++#define MIPHY_RX_LOCK_CTRL_1 0xc1 ++#define MIPHY_RX_LOCK_SETTINGS_OPT 0xc2 ++#define MIPHY_RX_LOCK_STEP 0xc4 ++ ++#define MIPHY_RX_SIGDET_SLEEP_OA 0xc9 ++#define MIPHY_RX_SIGDET_SLEEP_SEL 0xca ++#define MIPHY_RX_SIGDET_WAIT_SEL 0xcb ++#define MIPHY_RX_SIGDET_DATA_SEL 0xcc ++#define EN_ULTRA_LOW_POWER BIT(0) ++#define EN_FIRST_HALF BIT(1) ++#define EN_SECOND_HALF BIT(2) ++#define EN_DIGIT_SIGNAL_CHECK BIT(3) ++ ++#define MIPHY_RX_POWER_CTRL_1 0xcd ++#define MIPHY_RX_POWER_CTRL_2 0xce ++#define MIPHY_PLL_CALSET_CTRL 0xd3 ++#define MIPHY_PLL_CALSET_1 0xd4 ++#define MIPHY_PLL_CALSET_2 0xd5 ++#define MIPHY_PLL_CALSET_3 0xd6 ++#define MIPHY_PLL_CALSET_4 0xd7 ++#define MIPHY_PLL_SBR_1 0xe3 ++#define SET_NEW_CHANGE BIT(1) ++ ++#define MIPHY_PLL_SBR_2 0xe4 ++#define MIPHY_PLL_SBR_3 0xe5 ++#define MIPHY_PLL_SBR_4 0xe6 ++#define MIPHY_PLL_COMMON_MISC_2 0xe9 ++#define START_ACT_FILT BIT(6) ++ ++#define MIPHY_PLL_SPAREIN 0xeb ++ ++/* ++ * On STiH407 the glue logic can be different among MiPHY devices; for example: ++ * MiPHY0: OSC_FORCE_EXT means: ++ * 0: 30MHz crystal clk - 1: 100MHz ext clk routed through MiPHY1 ++ * MiPHY1: OSC_FORCE_EXT means: ++ * 1: 30MHz crystal clk - 0: 100MHz ext clk routed through MiPHY1 ++ * Some devices have not the possibility to check if the osc is ready. ++ */ ++#define MIPHY_OSC_FORCE_EXT BIT(3) ++#define MIPHY_OSC_RDY BIT(5) ++ ++#define MIPHY_CTRL_MASK 0x0f ++#define MIPHY_CTRL_DEFAULT 0 ++#define MIPHY_CTRL_SYNC_D_EN BIT(2) ++ ++/* SATA / PCIe defines */ ++#define SATA_CTRL_MASK 0x07 ++#define PCIE_CTRL_MASK 0xff ++#define SATA_CTRL_SELECT_SATA 1 ++#define SATA_CTRL_SELECT_PCIE 0 ++#define SYSCFG_PCIE_PCIE_VAL 0x80 ++#define SATA_SPDMODE 1 ++ ++#define MIPHY_SATA_BANK_NB 3 ++#define MIPHY_PCIE_BANK_NB 2 ++ ++enum { ++ SYSCFG_CTRL, ++ SYSCFG_STATUS, ++ SYSCFG_PCI, ++ SYSCFG_SATA, ++ SYSCFG_REG_MAX, ++}; ++ ++struct miphy28lp_phy { ++ struct phy *phy; ++ struct miphy28lp_dev *phydev; ++ void __iomem *base; ++ void __iomem *pipebase; ++ ++ bool osc_force_ext; ++ bool osc_rdy; ++ bool px_rx_pol_inv; ++ bool ssc; ++ bool tx_impedance; ++ ++ struct reset_control *miphy_rst; ++ ++ u32 sata_gen; ++ ++ /* Sysconfig registers offsets needed to configure the device */ ++ u32 syscfg_reg[SYSCFG_REG_MAX]; ++ u8 type; ++}; ++ ++struct miphy28lp_dev { ++ struct device *dev; ++ struct regmap *regmap; ++ struct mutex miphy_mutex; ++ struct miphy28lp_phy **phys; ++ int nphys; ++}; ++ ++struct miphy_initval { ++ u16 reg; ++ u16 val; ++}; ++ ++enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 }; ++ ++static char *PHY_TYPE_name[] = { "sata-up", "pcie-up", "", "usb3-up" }; ++ ++struct pll_ratio { ++ int clk_ref; ++ int calset_1; ++ int calset_2; ++ int calset_3; ++ int calset_4; ++ int cal_ctrl; ++}; ++ ++static struct pll_ratio sata_pll_ratio = { ++ .clk_ref = 0x1e, ++ .calset_1 = 0xc8, ++ .calset_2 = 0x00, ++ .calset_3 = 0x00, ++ .calset_4 = 0x00, ++ .cal_ctrl = 0x00, ++}; ++ ++static struct pll_ratio pcie_pll_ratio = { ++ .clk_ref = 0x1e, ++ .calset_1 = 0xa6, ++ .calset_2 = 0xaa, ++ .calset_3 = 0xaa, ++ .calset_4 = 0x00, ++ .cal_ctrl = 0x00, ++}; ++ ++static struct pll_ratio usb3_pll_ratio = { ++ .clk_ref = 0x1e, ++ .calset_1 = 0xa6, ++ .calset_2 = 0xaa, ++ .calset_3 = 0xaa, ++ .calset_4 = 0x04, ++ .cal_ctrl = 0x00, ++}; ++ ++struct miphy28lp_pll_gen { ++ int bank; ++ int speed; ++ int bias_boost_1; ++ int bias_boost_2; ++ int tx_ctrl_1; ++ int tx_ctrl_2; ++ int tx_ctrl_3; ++ int rx_k_gain; ++ int rx_vga_gain; ++ int rx_equ_gain_1; ++ int rx_equ_gain_2; ++ int rx_equ_gain_3; ++ int rx_buff_ctrl; ++}; ++ ++static struct miphy28lp_pll_gen sata_pll_gen[] = { ++ { ++ .bank = 0x00, ++ .speed = TX_SPDSEL_80DEC | RX_SPDSEL_80DEC, ++ .bias_boost_1 = 0x00, ++ .bias_boost_2 = 0xae, ++ .tx_ctrl_2 = 0x53, ++ .tx_ctrl_3 = 0x00, ++ .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, ++ .rx_vga_gain = 0x00, ++ .rx_equ_gain_1 = 0x7d, ++ .rx_equ_gain_2 = 0x56, ++ .rx_equ_gain_3 = 0x00, ++ }, ++ { ++ .bank = 0x01, ++ .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, ++ .bias_boost_1 = 0x00, ++ .bias_boost_2 = 0xae, ++ .tx_ctrl_2 = 0x72, ++ .tx_ctrl_3 = 0x20, ++ .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, ++ .rx_vga_gain = 0x00, ++ .rx_equ_gain_1 = 0x7d, ++ .rx_equ_gain_2 = 0x56, ++ .rx_equ_gain_3 = 0x00, ++ }, ++ { ++ .bank = 0x02, ++ .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, ++ .bias_boost_1 = 0x00, ++ .bias_boost_2 = 0xae, ++ .tx_ctrl_2 = 0xc0, ++ .tx_ctrl_3 = 0x20, ++ .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, ++ .rx_vga_gain = 0x00, ++ .rx_equ_gain_1 = 0x7d, ++ .rx_equ_gain_2 = 0x56, ++ .rx_equ_gain_3 = 0x00, ++ }, ++}; ++ ++static struct miphy28lp_pll_gen pcie_pll_gen[] = { ++ { ++ .bank = 0x00, ++ .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, ++ .bias_boost_1 = 0x00, ++ .bias_boost_2 = 0xa5, ++ .tx_ctrl_1 = TX_REG_STEP_N_25MV, ++ .tx_ctrl_2 = 0x71, ++ .tx_ctrl_3 = 0x60, ++ .rx_k_gain = 0x98, ++ .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, ++ .rx_vga_gain = 0x00, ++ .rx_equ_gain_1 = 0x79, ++ .rx_equ_gain_2 = 0x56, ++ }, ++ { ++ .bank = 0x01, ++ .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, ++ .bias_boost_1 = 0x00, ++ .bias_boost_2 = 0xa5, ++ .tx_ctrl_1 = TX_REG_STEP_N_25MV, ++ .tx_ctrl_2 = 0x70, ++ .tx_ctrl_3 = 0x60, ++ .rx_k_gain = 0xcc, ++ .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, ++ .rx_vga_gain = 0x00, ++ .rx_equ_gain_1 = 0x78, ++ .rx_equ_gain_2 = 0x07, ++ }, ++}; ++ ++static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ u8 val; ++ ++ /* Putting Macro in reset */ ++ writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); ++ ++ val = RST_APPLI_SW | RST_CONF_SW; ++ writeb_relaxed(val, base + MIPHY_CONF_RESET); ++ ++ writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); ++ ++ /* Bringing the MIPHY-CPU registers out of reset */ ++ if (miphy_phy->type == PHY_TYPE_PCIE) { ++ val = AUTO_RST_RX | TERM_EN_SW; ++ writeb_relaxed(val, base + MIPHY_CONTROL); ++ } else { ++ val = AUTO_RST_RX | TERM_EN_SW | DIS_LINK_RST; ++ writeb_relaxed(val, base + MIPHY_CONTROL); ++ } ++} ++ ++static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy, ++ struct pll_ratio *pll_ratio) ++{ ++ void __iomem *base = miphy_phy->base; ++ u8 val; ++ ++ /* Applying PLL Settings */ ++ writeb_relaxed(0x1d, base + MIPHY_PLL_SPAREIN); ++ writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); ++ ++ /* PLL Ratio */ ++ writeb_relaxed(pll_ratio->calset_1, base + MIPHY_PLL_CALSET_1); ++ writeb_relaxed(pll_ratio->calset_2, base + MIPHY_PLL_CALSET_2); ++ writeb_relaxed(pll_ratio->calset_3, base + MIPHY_PLL_CALSET_3); ++ writeb_relaxed(pll_ratio->calset_4, base + MIPHY_PLL_CALSET_4); ++ writeb_relaxed(pll_ratio->cal_ctrl, base + MIPHY_PLL_CALSET_CTRL); ++ ++ writeb_relaxed(TX_SEL, base + MIPHY_BOUNDARY_SEL); ++ ++ val = (0x68 << 1) | TX_SLEW_CAL_MAN_EN; ++ writeb_relaxed(val, base + MIPHY_TX_CAL_MAN); ++ ++ val = VGA_OFFSET_POLARITY | CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; ++ ++ if (miphy_phy->type != PHY_TYPE_SATA) ++ val |= OFFSET_COMPENSATION_EN; ++ ++ writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); ++ ++ if (miphy_phy->type == PHY_TYPE_USB3) { ++ writeb_relaxed(0x00, base + MIPHY_CONF); ++ writeb_relaxed(0x70, base + MIPHY_RX_LOCK_STEP); ++ writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_OA); ++ writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_SEL); ++ writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_WAIT_SEL); ++ ++ val = EN_DIGIT_SIGNAL_CHECK | EN_FIRST_HALF; ++ writeb_relaxed(val, base + MIPHY_RX_SIGDET_DATA_SEL); ++ } ++ ++} ++ ++static inline void miphy28lp_sata_config_gen(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(sata_pll_gen); i++) { ++ struct miphy28lp_pll_gen *gen = &sata_pll_gen[i]; ++ ++ /* Banked settings */ ++ writeb_relaxed(gen->bank, base + MIPHY_CONF); ++ writeb_relaxed(gen->speed, base + MIPHY_SPEED); ++ writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); ++ writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); ++ ++ /* TX buffer Settings */ ++ writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); ++ writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); ++ ++ /* RX Buffer Settings */ ++ writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); ++ writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); ++ writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); ++ writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); ++ writeb_relaxed(gen->rx_equ_gain_3, base + MIPHY_RX_EQU_GAIN_3); ++ } ++} ++ ++static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(pcie_pll_gen); i++) { ++ struct miphy28lp_pll_gen *gen = &pcie_pll_gen[i]; ++ ++ /* Banked settings */ ++ writeb_relaxed(gen->bank, base + MIPHY_CONF); ++ writeb_relaxed(gen->speed, base + MIPHY_SPEED); ++ writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); ++ writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); ++ ++ /* TX buffer Settings */ ++ writeb_relaxed(gen->tx_ctrl_1, base + MIPHY_TX_CTRL_1); ++ writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); ++ writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); ++ ++ writeb_relaxed(gen->rx_k_gain, base + MIPHY_RX_K_GAIN); ++ ++ /* RX Buffer Settings */ ++ writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); ++ writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); ++ writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); ++ writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); ++ } ++} ++ ++static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy) ++{ ++ unsigned long finish = jiffies + 5 * HZ; ++ u8 val; ++ ++ /* Waiting for Compensation to complete */ ++ do { ++ val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6); ++ ++ if (time_after_eq(jiffies, finish)) ++ return -EBUSY; ++ cpu_relax(); ++ } while (!(val & COMP_DONE)); ++ ++ return 0; ++} ++ ++ ++static inline int miphy28lp_compensation(struct miphy28lp_phy *miphy_phy, ++ struct pll_ratio *pll_ratio) ++{ ++ void __iomem *base = miphy_phy->base; ++ ++ /* Poll for HFC ready after reset release */ ++ /* Compensation measurement */ ++ writeb_relaxed(RST_PLL_SW | RST_COMP_SW, base + MIPHY_RESET); ++ ++ writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); ++ writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); ++ writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); ++ ++ if (miphy_phy->type == PHY_TYPE_PCIE) ++ writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); ++ ++ writeb_relaxed(0x00, base + MIPHY_RESET); ++ writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); ++ writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); ++ ++ /* TX compensation offset to re-center TX impedance */ ++ writeb_relaxed(0x00, base + MIPHY_COMP_POSTP); ++ ++ if (miphy_phy->type == PHY_TYPE_PCIE) ++ return miphy28lp_wait_compensation(miphy_phy); ++ ++ return 0; ++} ++ ++static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ u8 val; ++ ++ /* MIPHY Reset */ ++ writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); ++ writeb_relaxed(0x00, base + MIPHY_CONF_RESET); ++ writeb_relaxed(RST_COMP_SW, base + MIPHY_RESET); ++ ++ val = RST_COMP_SW | RST_PLL_SW; ++ writeb_relaxed(val, base + MIPHY_RESET); ++ ++ writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); ++ writeb_relaxed(0x1e, base + MIPHY_PLL_CLKREF_FREQ); ++ writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); ++ writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); ++ writeb_relaxed(0x00, base + MIPHY_RESET); ++ writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); ++ writeb_relaxed(0x00, base + MIPHY_CONF); ++ writeb_relaxed(0x00, base + MIPHY_BOUNDARY_1); ++ writeb_relaxed(0x00, base + MIPHY_TST_BIAS_BOOST_2); ++ writeb_relaxed(0x00, base + MIPHY_CONF); ++ writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); ++ writeb_relaxed(0xa5, base + MIPHY_DEBUG_BUS); ++ writeb_relaxed(0x00, base + MIPHY_CONF); ++} ++ ++static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ u8 val; ++ ++ /* Compensate Tx impedance to avoid out of range values */ ++ /* ++ * Enable the SSC on PLL for all banks ++ * SSC Modulation @ 31 KHz and 4000 ppm modulation amp ++ */ ++ val = readb_relaxed(base + MIPHY_BOUNDARY_2); ++ val |= SSC_EN_SW; ++ writeb_relaxed(val, base + MIPHY_BOUNDARY_2); ++ ++ val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); ++ val |= SSC_SEL; ++ writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); ++ ++ for (val = 0; val < MIPHY_SATA_BANK_NB; val++) { ++ writeb_relaxed(val, base + MIPHY_CONF); ++ ++ /* Add value to each reference clock cycle */ ++ /* and define the period length of the SSC */ ++ writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); ++ writeb_relaxed(0x6c, base + MIPHY_PLL_SBR_3); ++ writeb_relaxed(0x81, base + MIPHY_PLL_SBR_4); ++ ++ /* Clear any previous request */ ++ writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); ++ ++ /* requests the PLL to take in account new parameters */ ++ writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); ++ ++ /* To be sure there is no other pending requests */ ++ writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); ++ } ++} ++ ++static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ u8 val; ++ ++ /* Compensate Tx impedance to avoid out of range values */ ++ /* ++ * Enable the SSC on PLL for all banks ++ * SSC Modulation @ 31 KHz and 4000 ppm modulation amp ++ */ ++ val = readb_relaxed(base + MIPHY_BOUNDARY_2); ++ val |= SSC_EN_SW; ++ writeb_relaxed(val, base + MIPHY_BOUNDARY_2); ++ ++ val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); ++ val |= SSC_SEL; ++ writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); ++ ++ for (val = 0; val < MIPHY_PCIE_BANK_NB; val++) { ++ writeb_relaxed(val, base + MIPHY_CONF); ++ ++ /* Validate Step component */ ++ writeb_relaxed(0x69, base + MIPHY_PLL_SBR_3); ++ writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); ++ ++ /* Validate Period component */ ++ writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); ++ writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); ++ ++ /* Clear any previous request */ ++ writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); ++ ++ /* requests the PLL to take in account new parameters */ ++ writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); ++ ++ /* To be sure there is no other pending requests */ ++ writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); ++ } ++} ++ ++static inline void miphy_tune_tx_impedance(struct miphy28lp_phy *miphy_phy) ++{ ++ /* Compensate Tx impedance to avoid out of range values */ ++ writeb_relaxed(0x02, miphy_phy->base + MIPHY_COMP_POSTP); ++} ++ ++static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ int err; ++ u8 val; ++ ++ /* Putting Macro in reset */ ++ miphy28lp_set_reset(miphy_phy); ++ ++ /* PLL calibration */ ++ miphy28lp_pll_calibration(miphy_phy, &sata_pll_ratio); ++ ++ /* Banked settings Gen1/Gen2/Gen3 */ ++ miphy28lp_sata_config_gen(miphy_phy); ++ ++ /* Power control */ ++ /* Input bridge enable, manual input bridge control */ ++ writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); ++ ++ /* Macro out of reset */ ++ writeb_relaxed(0x00, base + MIPHY_CONF_RESET); ++ ++ /* Poll for HFC ready after reset release */ ++ /* Compensation measurement */ ++ err = miphy28lp_compensation(miphy_phy, &sata_pll_ratio); ++ if (err) ++ return err; ++ ++ if (miphy_phy->px_rx_pol_inv) { ++ /* Invert Rx polarity */ ++ val = readb_relaxed(miphy_phy->base + MIPHY_CONTROL); ++ val |= PX_RX_POL; ++ writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL); ++ } ++ ++ if (miphy_phy->ssc) ++ miphy_sata_tune_ssc(miphy_phy); ++ ++ if (miphy_phy->tx_impedance) ++ miphy_tune_tx_impedance(miphy_phy); ++ ++ return 0; ++} ++ ++static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ int err; ++ ++ /* Putting Macro in reset */ ++ miphy28lp_set_reset(miphy_phy); ++ ++ /* PLL calibration */ ++ miphy28lp_pll_calibration(miphy_phy, &pcie_pll_ratio); ++ ++ /* Banked settings Gen1/Gen2 */ ++ miphy28lp_pcie_config_gen(miphy_phy); ++ ++ /* Power control */ ++ /* Input bridge enable, manual input bridge control */ ++ writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); ++ ++ /* Macro out of reset */ ++ writeb_relaxed(0x00, base + MIPHY_CONF_RESET); ++ ++ /* Poll for HFC ready after reset release */ ++ /* Compensation measurement */ ++ err = miphy28lp_compensation(miphy_phy, &pcie_pll_ratio); ++ if (err) ++ return err; ++ ++ if (miphy_phy->ssc) ++ miphy_pcie_tune_ssc(miphy_phy); ++ ++ if (miphy_phy->tx_impedance) ++ miphy_tune_tx_impedance(miphy_phy); ++ ++ return 0; ++} ++ ++ ++static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy) ++{ ++ void __iomem *base = miphy_phy->base; ++ u8 val; ++ ++ /* Putting Macro in reset */ ++ miphy28lp_set_reset(miphy_phy); ++ ++ /* PLL calibration */ ++ miphy28lp_pll_calibration(miphy_phy, &usb3_pll_ratio); ++ ++ /* Writing The Speed Rate */ ++ writeb_relaxed(0x00, base + MIPHY_CONF); ++ ++ val = RX_SPDSEL_20DEC | TX_SPDSEL_20DEC; ++ writeb_relaxed(val, base + MIPHY_SPEED); ++ ++ /* RX Channel compensation and calibration */ ++ writeb_relaxed(0x1c, base + MIPHY_RX_LOCK_SETTINGS_OPT); ++ writeb_relaxed(0x51, base + MIPHY_RX_CAL_CTRL_1); ++ writeb_relaxed(0x70, base + MIPHY_RX_CAL_CTRL_2); ++ ++ val = OFFSET_COMPENSATION_EN | VGA_OFFSET_POLARITY | ++ CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; ++ writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); ++ writeb_relaxed(0x22, base + MIPHY_RX_CAL_VGA_STEP); ++ writeb_relaxed(0x0e, base + MIPHY_RX_CAL_OPT_LENGTH); ++ ++ val = EQ_DC_GAIN | VGA_GAIN; ++ writeb_relaxed(val, base + MIPHY_RX_BUFFER_CTRL); ++ writeb_relaxed(0x78, base + MIPHY_RX_EQU_GAIN_1); ++ writeb_relaxed(0x1b, base + MIPHY_SYNCHAR_CONTROL); ++ ++ /* TX compensation offset to re-center TX impedance */ ++ writeb_relaxed(0x02, base + MIPHY_COMP_POSTP); ++ ++ /* Enable GENSEL_SEL and SSC */ ++ /* TX_SEL=0 swing preemp forced by pipe registres */ ++ val = SSC_SEL | GENSEL_SEL; ++ writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); ++ ++ /* MIPHY Bias boost */ ++ writeb_relaxed(0x00, base + MIPHY_BIAS_BOOST_1); ++ writeb_relaxed(0xa7, base + MIPHY_BIAS_BOOST_2); ++ ++ /* SSC modulation */ ++ writeb_relaxed(SSC_EN_SW, base + MIPHY_BOUNDARY_2); ++ ++ /* MIPHY TX control */ ++ writeb_relaxed(0x00, base + MIPHY_CONF); ++ ++ /* Validate Step component */ ++ writeb_relaxed(0x5a, base + MIPHY_PLL_SBR_3); ++ writeb_relaxed(0xa0, base + MIPHY_PLL_SBR_4); ++ ++ /* Validate Period component */ ++ writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); ++ writeb_relaxed(0xa1, base + MIPHY_PLL_SBR_4); ++ ++ /* Clear any previous request */ ++ writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); ++ ++ /* requests the PLL to take in account new parameters */ ++ writeb_relaxed(0x02, base + MIPHY_PLL_SBR_1); ++ ++ /* To be sure there is no other pending requests */ ++ writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); ++ ++ /* Rx PI controller settings */ ++ writeb_relaxed(0xca, base + MIPHY_RX_K_GAIN); ++ ++ /* MIPHY RX input bridge control */ ++ /* INPUT_BRIDGE_EN_SW=1, manual input bridge control[0]=1 */ ++ writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); ++ writeb_relaxed(0x29, base + MIPHY_RX_POWER_CTRL_1); ++ writeb_relaxed(0x1a, base + MIPHY_RX_POWER_CTRL_2); ++ ++ /* MIPHY Reset for usb3 */ ++ miphy28_usb3_miphy_reset(miphy_phy); ++} ++ ++static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy) ++{ ++ unsigned long finish = jiffies + 5 * HZ; ++ u8 mask = HFC_PLL | HFC_RDY; ++ u8 val; ++ ++ /* ++ * For PCIe and USB3 check only that PLL and HFC are ready ++ * For SATA check also that phy is ready! ++ */ ++ if (miphy_phy->type == PHY_TYPE_SATA) ++ mask |= PHY_RDY; ++ ++ do { ++ val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1); ++ if ((val & mask) != mask) ++ cpu_relax(); ++ else ++ return 0; ++ } while (!time_after_eq(jiffies, finish)); ++ ++ return -EBUSY; ++} ++ ++static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) ++{ ++ struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; ++ unsigned long finish = jiffies + 5 * HZ; ++ u32 val; ++ ++ if (!miphy_phy->osc_rdy) ++ return 0; ++ ++ if (!miphy_phy->syscfg_reg[SYSCFG_STATUS]) ++ return -EINVAL; ++ ++ do { ++ regmap_read(miphy_dev->regmap, ++ miphy_phy->syscfg_reg[SYSCFG_STATUS], &val); ++ ++ if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) ++ cpu_relax(); ++ else ++ return 0; ++ } while (!time_after_eq(jiffies, finish)); ++ ++ return -EBUSY; ++} ++ ++static int miphy28lp_get_resource_byname(struct device_node *child, ++ char *rname, struct resource *res) ++{ ++ int index; ++ ++ index = of_property_match_string(child, "reg-names", rname); ++ if (index < 0) ++ return -ENODEV; ++ ++ return of_address_to_resource(child, index, res); ++} ++ ++static int miphy28lp_get_one_addr(struct device *dev, ++ struct device_node *child, char *rname, ++ void __iomem **base) ++{ ++ struct resource res; ++ int ret; ++ ++ ret = miphy28lp_get_resource_byname(child, rname, &res); ++ if (!ret) { ++ *base = devm_ioremap(dev, res.start, resource_size(&res)); ++ if (!*base) { ++ dev_err(dev, "failed to ioremap %s address region\n" ++ , rname); ++ return -ENOENT; ++ } ++ } ++ ++ return 0; ++} ++ ++/* MiPHY reset and sysconf setup */ ++static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) ++{ ++ int err; ++ struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; ++ ++ if (!miphy_phy->syscfg_reg[SYSCFG_CTRL]) ++ return -EINVAL; ++ ++ err = reset_control_assert(miphy_phy->miphy_rst); ++ if (err) { ++ dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); ++ return err; ++ } ++ ++ if (miphy_phy->osc_force_ext) ++ miphy_val |= MIPHY_OSC_FORCE_EXT; ++ ++ regmap_update_bits(miphy_dev->regmap, ++ miphy_phy->syscfg_reg[SYSCFG_CTRL], ++ MIPHY_CTRL_MASK, miphy_val); ++ ++ err = reset_control_deassert(miphy_phy->miphy_rst); ++ if (err) { ++ dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); ++ return err; ++ } ++ ++ return miphy_osc_is_ready(miphy_phy); ++} ++ ++static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) ++{ ++ struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; ++ int err, sata_conf = SATA_CTRL_SELECT_SATA; ++ ++ if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || ++ (!miphy_phy->syscfg_reg[SYSCFG_PCI]) || ++ (!miphy_phy->base)) ++ return -EINVAL; ++ ++ dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base); ++ ++ /* Configure the glue-logic */ ++ sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE); ++ ++ regmap_update_bits(miphy_dev->regmap, ++ miphy_phy->syscfg_reg[SYSCFG_SATA], ++ SATA_CTRL_MASK, sata_conf); ++ ++ regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], ++ PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE); ++ ++ /* MiPHY path and clocking init */ ++ err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); ++ ++ if (err) { ++ dev_err(miphy_dev->dev, "SATA phy setup failed\n"); ++ return err; ++ } ++ ++ /* initialize miphy */ ++ miphy28lp_configure_sata(miphy_phy); ++ ++ return miphy_is_ready(miphy_phy); ++} ++ ++static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy) ++{ ++ struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; ++ int err; ++ ++ if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || ++ (!miphy_phy->syscfg_reg[SYSCFG_PCI]) ++ || (!miphy_phy->base) || (!miphy_phy->pipebase)) ++ return -EINVAL; ++ ++ dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base); ++ ++ /* Configure the glue-logic */ ++ regmap_update_bits(miphy_dev->regmap, ++ miphy_phy->syscfg_reg[SYSCFG_SATA], ++ SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE); ++ ++ regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], ++ PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL); ++ ++ /* MiPHY path and clocking init */ ++ err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); ++ ++ if (err) { ++ dev_err(miphy_dev->dev, "PCIe phy setup failed\n"); ++ return err; ++ } ++ ++ /* initialize miphy */ ++ err = miphy28lp_configure_pcie(miphy_phy); ++ if (err) ++ return err; ++ ++ /* PIPE Wrapper Configuration */ ++ writeb_relaxed(0x68, miphy_phy->pipebase + 0x104); /* Rise_0 */ ++ writeb_relaxed(0x61, miphy_phy->pipebase + 0x105); /* Rise_1 */ ++ writeb_relaxed(0x68, miphy_phy->pipebase + 0x108); /* Fall_0 */ ++ writeb_relaxed(0x61, miphy_phy->pipebase + 0x109); /* Fall-1 */ ++ writeb_relaxed(0x68, miphy_phy->pipebase + 0x10c); /* Threshold_0 */ ++ writeb_relaxed(0x60, miphy_phy->pipebase + 0x10d); /* Threshold_1 */ ++ ++ /* Wait for phy_ready */ ++ return miphy_is_ready(miphy_phy); ++} ++ ++static int miphy28lp_init_usb3(struct miphy28lp_phy *miphy_phy) ++{ ++ struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; ++ int err; ++ ++ if ((!miphy_phy->base) || (!miphy_phy->pipebase)) ++ return -EINVAL; ++ ++ dev_info(miphy_dev->dev, "usb3-up mode, addr 0x%p\n", miphy_phy->base); ++ ++ /* MiPHY path and clocking init */ ++ err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_SYNC_D_EN); ++ if (err) { ++ dev_err(miphy_dev->dev, "USB3 phy setup failed\n"); ++ return err; ++ } ++ ++ /* initialize miphy */ ++ miphy28lp_configure_usb3(miphy_phy); ++ ++ /* PIPE Wrapper Configuration */ ++ writeb_relaxed(0x68, miphy_phy->pipebase + 0x23); ++ writeb_relaxed(0x61, miphy_phy->pipebase + 0x24); ++ writeb_relaxed(0x68, miphy_phy->pipebase + 0x26); ++ writeb_relaxed(0x61, miphy_phy->pipebase + 0x27); ++ writeb_relaxed(0x18, miphy_phy->pipebase + 0x29); ++ writeb_relaxed(0x61, miphy_phy->pipebase + 0x2a); ++ ++ /* pipe Wrapper usb3 TX swing de-emph margin PREEMPH[7:4], SWING[3:0] */ ++ writeb_relaxed(0X67, miphy_phy->pipebase + 0x68); ++ writeb_relaxed(0x0d, miphy_phy->pipebase + 0x69); ++ writeb_relaxed(0X67, miphy_phy->pipebase + 0x6a); ++ writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6b); ++ writeb_relaxed(0X67, miphy_phy->pipebase + 0x6c); ++ writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6d); ++ writeb_relaxed(0X67, miphy_phy->pipebase + 0x6e); ++ writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6f); ++ ++ return miphy_is_ready(miphy_phy); ++} ++ ++static int miphy28lp_init(struct phy *phy) ++{ ++ struct miphy28lp_phy *miphy_phy = phy_get_drvdata(phy); ++ struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; ++ int ret; ++ ++ mutex_lock(&miphy_dev->miphy_mutex); ++ ++ switch (miphy_phy->type) { ++ ++ case PHY_TYPE_SATA: ++ ret = miphy28lp_init_sata(miphy_phy); ++ break; ++ case PHY_TYPE_PCIE: ++ ret = miphy28lp_init_pcie(miphy_phy); ++ break; ++ case PHY_TYPE_USB3: ++ ret = miphy28lp_init_usb3(miphy_phy); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ mutex_unlock(&miphy_dev->miphy_mutex); ++ ++ return ret; ++} ++ ++static int miphy28lp_get_addr(struct miphy28lp_phy *miphy_phy) ++{ ++ struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; ++ struct device_node *phynode = miphy_phy->phy->dev.of_node; ++ int err; ++ ++ if ((miphy_phy->type != PHY_TYPE_SATA) && ++ (miphy_phy->type != PHY_TYPE_PCIE) && ++ (miphy_phy->type != PHY_TYPE_USB3)) { ++ return -EINVAL; ++ } ++ ++ err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, ++ PHY_TYPE_name[miphy_phy->type - PHY_TYPE_SATA], ++ &miphy_phy->base); ++ if (err) ++ return err; ++ ++ if ((miphy_phy->type == PHY_TYPE_PCIE) || ++ (miphy_phy->type == PHY_TYPE_USB3)) { ++ err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, "pipew", ++ &miphy_phy->pipebase); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++static struct phy *miphy28lp_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct miphy28lp_dev *miphy_dev = dev_get_drvdata(dev); ++ struct miphy28lp_phy *miphy_phy = NULL; ++ struct device_node *phynode = args->np; ++ int ret, index = 0; ++ ++ if (args->args_count != 1) { ++ dev_err(dev, "Invalid number of cells in 'phy' property\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ for (index = 0; index < miphy_dev->nphys; index++) ++ if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { ++ miphy_phy = miphy_dev->phys[index]; ++ break; ++ } ++ ++ if (!miphy_phy) { ++ dev_err(dev, "Failed to find appropriate phy\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ miphy_phy->type = args->args[0]; ++ ++ ret = miphy28lp_get_addr(miphy_phy); ++ if (ret < 0) ++ return ERR_PTR(ret); ++ ++ return miphy_phy->phy; ++} ++ ++static const struct phy_ops miphy28lp_ops = { ++ .init = miphy28lp_init, ++ .owner = THIS_MODULE, ++}; ++ ++static int miphy28lp_probe_resets(struct device_node *node, ++ struct miphy28lp_phy *miphy_phy) ++{ ++ struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; ++ int err; ++ ++ miphy_phy->miphy_rst = ++ of_reset_control_get_shared(node, "miphy-sw-rst"); ++ ++ if (IS_ERR(miphy_phy->miphy_rst)) { ++ dev_err(miphy_dev->dev, ++ "miphy soft reset control not defined\n"); ++ return PTR_ERR(miphy_phy->miphy_rst); ++ } ++ ++ err = reset_control_deassert(miphy_phy->miphy_rst); ++ if (err) { ++ dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int miphy28lp_of_probe(struct device_node *np, ++ struct miphy28lp_phy *miphy_phy) ++{ ++ int i; ++ u32 ctrlreg; ++ ++ miphy_phy->osc_force_ext = ++ of_property_read_bool(np, "st,osc-force-ext"); ++ ++ miphy_phy->osc_rdy = of_property_read_bool(np, "st,osc-rdy"); ++ ++ miphy_phy->px_rx_pol_inv = ++ of_property_read_bool(np, "st,px_rx_pol_inv"); ++ ++ miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on"); ++ ++ miphy_phy->tx_impedance = ++ of_property_read_bool(np, "st,tx-impedance-comp"); ++ ++ of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen); ++ if (!miphy_phy->sata_gen) ++ miphy_phy->sata_gen = SATA_GEN1; ++ ++ for (i = 0; i < SYSCFG_REG_MAX; i++) { ++ if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg)) ++ miphy_phy->syscfg_reg[i] = ctrlreg; ++ } ++ ++ return 0; ++} ++ ++static int miphy28lp_probe(struct platform_device *pdev) ++{ ++ struct device_node *child, *np = pdev->dev.of_node; ++ struct miphy28lp_dev *miphy_dev; ++ struct phy_provider *provider; ++ struct phy *phy; ++ int ret, port = 0; ++ ++ miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); ++ if (!miphy_dev) ++ return -ENOMEM; ++ ++ miphy_dev->nphys = of_get_child_count(np); ++ miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys, ++ sizeof(*miphy_dev->phys), GFP_KERNEL); ++ if (!miphy_dev->phys) ++ return -ENOMEM; ++ ++ miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); ++ if (IS_ERR(miphy_dev->regmap)) { ++ dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); ++ return PTR_ERR(miphy_dev->regmap); ++ } ++ ++ miphy_dev->dev = &pdev->dev; ++ ++ dev_set_drvdata(&pdev->dev, miphy_dev); ++ ++ mutex_init(&miphy_dev->miphy_mutex); ++ ++ for_each_child_of_node(np, child) { ++ struct miphy28lp_phy *miphy_phy; ++ ++ miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), ++ GFP_KERNEL); ++ if (!miphy_phy) { ++ ret = -ENOMEM; ++ goto put_child; ++ } ++ ++ miphy_dev->phys[port] = miphy_phy; ++ ++ phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops); ++ if (IS_ERR(phy)) { ++ dev_err(&pdev->dev, "failed to create PHY\n"); ++ ret = PTR_ERR(phy); ++ goto put_child; ++ } ++ ++ miphy_dev->phys[port]->phy = phy; ++ miphy_dev->phys[port]->phydev = miphy_dev; ++ ++ ret = miphy28lp_of_probe(child, miphy_phy); ++ if (ret) ++ goto put_child; ++ ++ ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]); ++ if (ret) ++ goto put_child; ++ ++ phy_set_drvdata(phy, miphy_dev->phys[port]); ++ port++; ++ ++ } ++ ++ provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate); ++ return PTR_ERR_OR_ZERO(provider); ++put_child: ++ of_node_put(child); ++ return ret; ++} ++ ++static const struct of_device_id miphy28lp_of_match[] = { ++ {.compatible = "st,miphy28lp-phy", }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, miphy28lp_of_match); ++ ++static struct platform_driver miphy28lp_driver = { ++ .probe = miphy28lp_probe, ++ .driver = { ++ .name = "miphy28lp-phy", ++ .of_match_table = miphy28lp_of_match, ++ } ++}; ++ ++module_platform_driver(miphy28lp_driver); ++ ++MODULE_AUTHOR("Alexandre Torgue "); ++MODULE_DESCRIPTION("STMicroelectronics miphy28lp driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/st/phy-spear1310-miphy.c b/drivers/phy/st/phy-spear1310-miphy.c +new file mode 100644 +index 000000000000..ed67e98e54ca +--- /dev/null ++++ b/drivers/phy/st/phy-spear1310-miphy.c +@@ -0,0 +1,261 @@ ++/* ++ * ST SPEAr1310-miphy driver ++ * ++ * Copyright (C) 2014 ST Microelectronics ++ * Pratyush Anand ++ * Mohit Kumar ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SPEAr1310 Registers */ ++#define SPEAR1310_PCIE_SATA_CFG 0x3A4 ++ #define SPEAR1310_PCIE_SATA2_SEL_PCIE (0 << 31) ++ #define SPEAR1310_PCIE_SATA1_SEL_PCIE (0 << 30) ++ #define SPEAR1310_PCIE_SATA0_SEL_PCIE (0 << 29) ++ #define SPEAR1310_PCIE_SATA2_SEL_SATA BIT(31) ++ #define SPEAR1310_PCIE_SATA1_SEL_SATA BIT(30) ++ #define SPEAR1310_PCIE_SATA0_SEL_SATA BIT(29) ++ #define SPEAR1310_SATA2_CFG_TX_CLK_EN BIT(27) ++ #define SPEAR1310_SATA2_CFG_RX_CLK_EN BIT(26) ++ #define SPEAR1310_SATA2_CFG_POWERUP_RESET BIT(25) ++ #define SPEAR1310_SATA2_CFG_PM_CLK_EN BIT(24) ++ #define SPEAR1310_SATA1_CFG_TX_CLK_EN BIT(23) ++ #define SPEAR1310_SATA1_CFG_RX_CLK_EN BIT(22) ++ #define SPEAR1310_SATA1_CFG_POWERUP_RESET BIT(21) ++ #define SPEAR1310_SATA1_CFG_PM_CLK_EN BIT(20) ++ #define SPEAR1310_SATA0_CFG_TX_CLK_EN BIT(19) ++ #define SPEAR1310_SATA0_CFG_RX_CLK_EN BIT(18) ++ #define SPEAR1310_SATA0_CFG_POWERUP_RESET BIT(17) ++ #define SPEAR1310_SATA0_CFG_PM_CLK_EN BIT(16) ++ #define SPEAR1310_PCIE2_CFG_DEVICE_PRESENT BIT(11) ++ #define SPEAR1310_PCIE2_CFG_POWERUP_RESET BIT(10) ++ #define SPEAR1310_PCIE2_CFG_CORE_CLK_EN BIT(9) ++ #define SPEAR1310_PCIE2_CFG_AUX_CLK_EN BIT(8) ++ #define SPEAR1310_PCIE1_CFG_DEVICE_PRESENT BIT(7) ++ #define SPEAR1310_PCIE1_CFG_POWERUP_RESET BIT(6) ++ #define SPEAR1310_PCIE1_CFG_CORE_CLK_EN BIT(5) ++ #define SPEAR1310_PCIE1_CFG_AUX_CLK_EN BIT(4) ++ #define SPEAR1310_PCIE0_CFG_DEVICE_PRESENT BIT(3) ++ #define SPEAR1310_PCIE0_CFG_POWERUP_RESET BIT(2) ++ #define SPEAR1310_PCIE0_CFG_CORE_CLK_EN BIT(1) ++ #define SPEAR1310_PCIE0_CFG_AUX_CLK_EN BIT(0) ++ ++ #define SPEAR1310_PCIE_CFG_MASK(x) ((0xF << (x * 4)) | BIT((x + 29))) ++ #define SPEAR1310_SATA_CFG_MASK(x) ((0xF << (x * 4 + 16)) | \ ++ BIT((x + 29))) ++ #define SPEAR1310_PCIE_CFG_VAL(x) \ ++ (SPEAR1310_PCIE_SATA##x##_SEL_PCIE | \ ++ SPEAR1310_PCIE##x##_CFG_AUX_CLK_EN | \ ++ SPEAR1310_PCIE##x##_CFG_CORE_CLK_EN | \ ++ SPEAR1310_PCIE##x##_CFG_POWERUP_RESET | \ ++ SPEAR1310_PCIE##x##_CFG_DEVICE_PRESENT) ++ #define SPEAR1310_SATA_CFG_VAL(x) \ ++ (SPEAR1310_PCIE_SATA##x##_SEL_SATA | \ ++ SPEAR1310_SATA##x##_CFG_PM_CLK_EN | \ ++ SPEAR1310_SATA##x##_CFG_POWERUP_RESET | \ ++ SPEAR1310_SATA##x##_CFG_RX_CLK_EN | \ ++ SPEAR1310_SATA##x##_CFG_TX_CLK_EN) ++ ++#define SPEAR1310_PCIE_MIPHY_CFG_1 0x3A8 ++ #define SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT BIT(31) ++ #define SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 BIT(28) ++ #define SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(x) (x << 16) ++ #define SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT BIT(15) ++ #define SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 BIT(12) ++ #define SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(x) (x << 0) ++ #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_MASK (0xFFFF) ++ #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK (0xFFFF << 16) ++ #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA \ ++ (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ ++ SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 | \ ++ SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(60) | \ ++ SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ ++ SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 | \ ++ SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(60)) ++ #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ ++ (SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(120)) ++ #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE \ ++ (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ ++ SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(25) | \ ++ SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ ++ SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(25)) ++ ++#define SPEAR1310_PCIE_MIPHY_CFG_2 0x3AC ++ ++enum spear1310_miphy_mode { ++ SATA, ++ PCIE, ++}; ++ ++struct spear1310_miphy_priv { ++ /* instance id of this phy */ ++ u32 id; ++ /* phy mode: 0 for SATA 1 for PCIe */ ++ enum spear1310_miphy_mode mode; ++ /* regmap for any soc specific misc registers */ ++ struct regmap *misc; ++ /* phy struct pointer */ ++ struct phy *phy; ++}; ++ ++static int spear1310_miphy_pcie_init(struct spear1310_miphy_priv *priv) ++{ ++ u32 val; ++ ++ regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, ++ SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, ++ SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE); ++ ++ switch (priv->id) { ++ case 0: ++ val = SPEAR1310_PCIE_CFG_VAL(0); ++ break; ++ case 1: ++ val = SPEAR1310_PCIE_CFG_VAL(1); ++ break; ++ case 2: ++ val = SPEAR1310_PCIE_CFG_VAL(2); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, ++ SPEAR1310_PCIE_CFG_MASK(priv->id), val); ++ ++ return 0; ++} ++ ++static int spear1310_miphy_pcie_exit(struct spear1310_miphy_priv *priv) ++{ ++ regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, ++ SPEAR1310_PCIE_CFG_MASK(priv->id), 0); ++ ++ regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, ++ SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, 0); ++ ++ return 0; ++} ++ ++static int spear1310_miphy_init(struct phy *phy) ++{ ++ struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); ++ int ret = 0; ++ ++ if (priv->mode == PCIE) ++ ret = spear1310_miphy_pcie_init(priv); ++ ++ return ret; ++} ++ ++static int spear1310_miphy_exit(struct phy *phy) ++{ ++ struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); ++ int ret = 0; ++ ++ if (priv->mode == PCIE) ++ ret = spear1310_miphy_pcie_exit(priv); ++ ++ return ret; ++} ++ ++static const struct of_device_id spear1310_miphy_of_match[] = { ++ { .compatible = "st,spear1310-miphy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match); ++ ++static const struct phy_ops spear1310_miphy_ops = { ++ .init = spear1310_miphy_init, ++ .exit = spear1310_miphy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static struct phy *spear1310_miphy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct spear1310_miphy_priv *priv = dev_get_drvdata(dev); ++ ++ if (args->args_count < 1) { ++ dev_err(dev, "DT did not pass correct no of args\n"); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ priv->mode = args->args[0]; ++ ++ if (priv->mode != SATA && priv->mode != PCIE) { ++ dev_err(dev, "DT did not pass correct phy mode\n"); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ return priv->phy; ++} ++ ++static int spear1310_miphy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct spear1310_miphy_priv *priv; ++ struct phy_provider *phy_provider; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->misc = ++ syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); ++ if (IS_ERR(priv->misc)) { ++ dev_err(dev, "failed to find misc regmap\n"); ++ return PTR_ERR(priv->misc); ++ } ++ ++ if (of_property_read_u32(dev->of_node, "phy-id", &priv->id)) { ++ dev_err(dev, "failed to find phy id\n"); ++ return -EINVAL; ++ } ++ ++ priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops); ++ if (IS_ERR(priv->phy)) { ++ dev_err(dev, "failed to create SATA PCIe PHY\n"); ++ return PTR_ERR(priv->phy); ++ } ++ ++ dev_set_drvdata(dev, priv); ++ phy_set_drvdata(priv->phy, priv); ++ ++ phy_provider = ++ devm_of_phy_provider_register(dev, spear1310_miphy_xlate); ++ if (IS_ERR(phy_provider)) { ++ dev_err(dev, "failed to register phy provider\n"); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver spear1310_miphy_driver = { ++ .probe = spear1310_miphy_probe, ++ .driver = { ++ .name = "spear1310-miphy", ++ .of_match_table = of_match_ptr(spear1310_miphy_of_match), ++ }, ++}; ++ ++module_platform_driver(spear1310_miphy_driver); ++ ++MODULE_DESCRIPTION("ST SPEAR1310-MIPHY driver"); ++MODULE_AUTHOR("Pratyush Anand "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/st/phy-spear1340-miphy.c b/drivers/phy/st/phy-spear1340-miphy.c +new file mode 100644 +index 000000000000..97280c0cf612 +--- /dev/null ++++ b/drivers/phy/st/phy-spear1340-miphy.c +@@ -0,0 +1,294 @@ ++/* ++ * ST spear1340-miphy driver ++ * ++ * Copyright (C) 2014 ST Microelectronics ++ * Pratyush Anand ++ * Mohit Kumar ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SPEAr1340 Registers */ ++/* Power Management Registers */ ++#define SPEAR1340_PCM_CFG 0x100 ++ #define SPEAR1340_PCM_CFG_SATA_POWER_EN BIT(11) ++#define SPEAR1340_PCM_WKUP_CFG 0x104 ++#define SPEAR1340_SWITCH_CTR 0x108 ++ ++#define SPEAR1340_PERIP1_SW_RST 0x318 ++ #define SPEAR1340_PERIP1_SW_RSATA BIT(12) ++#define SPEAR1340_PERIP2_SW_RST 0x31C ++#define SPEAR1340_PERIP3_SW_RST 0x320 ++ ++/* PCIE - SATA configuration registers */ ++#define SPEAR1340_PCIE_SATA_CFG 0x424 ++ /* PCIE CFG MASks */ ++ #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT BIT(11) ++ #define SPEAR1340_PCIE_CFG_POWERUP_RESET BIT(10) ++ #define SPEAR1340_PCIE_CFG_CORE_CLK_EN BIT(9) ++ #define SPEAR1340_PCIE_CFG_AUX_CLK_EN BIT(8) ++ #define SPEAR1340_SATA_CFG_TX_CLK_EN BIT(4) ++ #define SPEAR1340_SATA_CFG_RX_CLK_EN BIT(3) ++ #define SPEAR1340_SATA_CFG_POWERUP_RESET BIT(2) ++ #define SPEAR1340_SATA_CFG_PM_CLK_EN BIT(1) ++ #define SPEAR1340_PCIE_SATA_SEL_PCIE (0) ++ #define SPEAR1340_PCIE_SATA_SEL_SATA (1) ++ #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F ++ #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \ ++ SPEAR1340_PCIE_CFG_AUX_CLK_EN | \ ++ SPEAR1340_PCIE_CFG_CORE_CLK_EN | \ ++ SPEAR1340_PCIE_CFG_POWERUP_RESET | \ ++ SPEAR1340_PCIE_CFG_DEVICE_PRESENT) ++ #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \ ++ SPEAR1340_SATA_CFG_PM_CLK_EN | \ ++ SPEAR1340_SATA_CFG_POWERUP_RESET | \ ++ SPEAR1340_SATA_CFG_RX_CLK_EN | \ ++ SPEAR1340_SATA_CFG_TX_CLK_EN) ++ ++#define SPEAR1340_PCIE_MIPHY_CFG 0x428 ++ #define SPEAR1340_MIPHY_OSC_BYPASS_EXT BIT(31) ++ #define SPEAR1340_MIPHY_CLK_REF_DIV2 BIT(27) ++ #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27) ++ #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27) ++ #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0) ++ #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF ++ #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \ ++ (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ ++ SPEAR1340_MIPHY_CLK_REF_DIV2 | \ ++ SPEAR1340_MIPHY_PLL_RATIO_TOP(60)) ++ #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ ++ (SPEAR1340_MIPHY_PLL_RATIO_TOP(120)) ++ #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \ ++ (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ ++ SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) ++ ++enum spear1340_miphy_mode { ++ SATA, ++ PCIE, ++}; ++ ++struct spear1340_miphy_priv { ++ /* phy mode: 0 for SATA 1 for PCIe */ ++ enum spear1340_miphy_mode mode; ++ /* regmap for any soc specific misc registers */ ++ struct regmap *misc; ++ /* phy struct pointer */ ++ struct phy *phy; ++}; ++ ++static int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv) ++{ ++ regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, ++ SPEAR1340_PCIE_SATA_CFG_MASK, ++ SPEAR1340_SATA_CFG_VAL); ++ regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, ++ SPEAR1340_PCIE_MIPHY_CFG_MASK, ++ SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK); ++ /* Switch on sata power domain */ ++ regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, ++ SPEAR1340_PCM_CFG_SATA_POWER_EN, ++ SPEAR1340_PCM_CFG_SATA_POWER_EN); ++ /* Wait for SATA power domain on */ ++ msleep(20); ++ ++ /* Disable PCIE SATA Controller reset */ ++ regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, ++ SPEAR1340_PERIP1_SW_RSATA, 0); ++ /* Wait for SATA reset de-assert completion */ ++ msleep(20); ++ ++ return 0; ++} ++ ++static int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv) ++{ ++ regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, ++ SPEAR1340_PCIE_SATA_CFG_MASK, 0); ++ regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, ++ SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); ++ ++ /* Enable PCIE SATA Controller reset */ ++ regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, ++ SPEAR1340_PERIP1_SW_RSATA, ++ SPEAR1340_PERIP1_SW_RSATA); ++ /* Wait for SATA power domain off */ ++ msleep(20); ++ /* Switch off sata power domain */ ++ regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, ++ SPEAR1340_PCM_CFG_SATA_POWER_EN, 0); ++ /* Wait for SATA reset assert completion */ ++ msleep(20); ++ ++ return 0; ++} ++ ++static int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv) ++{ ++ regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, ++ SPEAR1340_PCIE_MIPHY_CFG_MASK, ++ SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE); ++ regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, ++ SPEAR1340_PCIE_SATA_CFG_MASK, ++ SPEAR1340_PCIE_CFG_VAL); ++ ++ return 0; ++} ++ ++static int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv) ++{ ++ regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, ++ SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); ++ regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, ++ SPEAR1340_PCIE_SATA_CFG_MASK, 0); ++ ++ return 0; ++} ++ ++static int spear1340_miphy_init(struct phy *phy) ++{ ++ struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); ++ int ret = 0; ++ ++ if (priv->mode == SATA) ++ ret = spear1340_miphy_sata_init(priv); ++ else if (priv->mode == PCIE) ++ ret = spear1340_miphy_pcie_init(priv); ++ ++ return ret; ++} ++ ++static int spear1340_miphy_exit(struct phy *phy) ++{ ++ struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); ++ int ret = 0; ++ ++ if (priv->mode == SATA) ++ ret = spear1340_miphy_sata_exit(priv); ++ else if (priv->mode == PCIE) ++ ret = spear1340_miphy_pcie_exit(priv); ++ ++ return ret; ++} ++ ++static const struct of_device_id spear1340_miphy_of_match[] = { ++ { .compatible = "st,spear1340-miphy" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match); ++ ++static const struct phy_ops spear1340_miphy_ops = { ++ .init = spear1340_miphy_init, ++ .exit = spear1340_miphy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++#ifdef CONFIG_PM_SLEEP ++static int spear1340_miphy_suspend(struct device *dev) ++{ ++ struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); ++ int ret = 0; ++ ++ if (priv->mode == SATA) ++ ret = spear1340_miphy_sata_exit(priv); ++ ++ return ret; ++} ++ ++static int spear1340_miphy_resume(struct device *dev) ++{ ++ struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); ++ int ret = 0; ++ ++ if (priv->mode == SATA) ++ ret = spear1340_miphy_sata_init(priv); ++ ++ return ret; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend, ++ spear1340_miphy_resume); ++ ++static struct phy *spear1340_miphy_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); ++ ++ if (args->args_count < 1) { ++ dev_err(dev, "DT did not pass correct no of args\n"); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ priv->mode = args->args[0]; ++ ++ if (priv->mode != SATA && priv->mode != PCIE) { ++ dev_err(dev, "DT did not pass correct phy mode\n"); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ return priv->phy; ++} ++ ++static int spear1340_miphy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct spear1340_miphy_priv *priv; ++ struct phy_provider *phy_provider; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->misc = ++ syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); ++ if (IS_ERR(priv->misc)) { ++ dev_err(dev, "failed to find misc regmap\n"); ++ return PTR_ERR(priv->misc); ++ } ++ ++ priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops); ++ if (IS_ERR(priv->phy)) { ++ dev_err(dev, "failed to create SATA PCIe PHY\n"); ++ return PTR_ERR(priv->phy); ++ } ++ ++ dev_set_drvdata(dev, priv); ++ phy_set_drvdata(priv->phy, priv); ++ ++ phy_provider = ++ devm_of_phy_provider_register(dev, spear1340_miphy_xlate); ++ if (IS_ERR(phy_provider)) { ++ dev_err(dev, "failed to register phy provider\n"); ++ return PTR_ERR(phy_provider); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver spear1340_miphy_driver = { ++ .probe = spear1340_miphy_probe, ++ .driver = { ++ .name = "spear1340-miphy", ++ .pm = &spear1340_miphy_pm_ops, ++ .of_match_table = of_match_ptr(spear1340_miphy_of_match), ++ }, ++}; ++ ++module_platform_driver(spear1340_miphy_driver); ++ ++MODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver"); ++MODULE_AUTHOR("Pratyush Anand "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/st/phy-stih407-usb.c b/drivers/phy/st/phy-stih407-usb.c +new file mode 100644 +index 000000000000..b1f44ab669fb +--- /dev/null ++++ b/drivers/phy/st/phy-stih407-usb.c +@@ -0,0 +1,180 @@ ++/* ++ * Copyright (C) 2014 STMicroelectronics ++ * ++ * STMicroelectronics Generic PHY driver for STiH407 USB2. ++ * ++ * Author: Giuseppe Cavallaro ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2, as ++ * published by the Free Software Foundation. ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PHYPARAM_REG 1 ++#define PHYCTRL_REG 2 ++ ++/* Default PHY_SEL and REFCLKSEL configuration */ ++#define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 ++#define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f ++ ++/* ports parameters overriding */ ++#define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc ++#define STIH407_USB_PICOPHY_PARAM_MASK 0xffffffff ++ ++struct stih407_usb2_picophy { ++ struct phy *phy; ++ struct regmap *regmap; ++ struct device *dev; ++ struct reset_control *rstc; ++ struct reset_control *rstport; ++ int ctrl; ++ int param; ++}; ++ ++static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev) ++{ ++ reset_control_deassert(phy_dev->rstc); ++ ++ return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl, ++ STIH407_USB_PICOPHY_CTRL_PORT_MASK, ++ STIH407_USB_PICOPHY_CTRL_PORT_CONF); ++} ++ ++static int stih407_usb2_init_port(struct phy *phy) ++{ ++ int ret; ++ struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); ++ ++ stih407_usb2_pico_ctrl(phy_dev); ++ ++ ret = regmap_update_bits(phy_dev->regmap, ++ phy_dev->param, ++ STIH407_USB_PICOPHY_PARAM_MASK, ++ STIH407_USB_PICOPHY_PARAM_DEF); ++ if (ret) ++ return ret; ++ ++ return reset_control_deassert(phy_dev->rstport); ++} ++ ++static int stih407_usb2_exit_port(struct phy *phy) ++{ ++ struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); ++ ++ /* ++ * Only port reset is asserted, phy global reset is kept untouched ++ * as other ports may still be active. When all ports are in reset ++ * state, assumption is made that power will be cut off on the phy, in ++ * case of suspend for instance. Theoretically, asserting individual ++ * reset (like here) or global reset should be equivalent. ++ */ ++ return reset_control_assert(phy_dev->rstport); ++} ++ ++static const struct phy_ops stih407_usb2_picophy_data = { ++ .init = stih407_usb2_init_port, ++ .exit = stih407_usb2_exit_port, ++ .owner = THIS_MODULE, ++}; ++ ++static int stih407_usb2_picophy_probe(struct platform_device *pdev) ++{ ++ struct stih407_usb2_picophy *phy_dev; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct phy_provider *phy_provider; ++ struct phy *phy; ++ int ret; ++ ++ phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); ++ if (!phy_dev) ++ return -ENOMEM; ++ ++ phy_dev->dev = dev; ++ dev_set_drvdata(dev, phy_dev); ++ ++ phy_dev->rstc = devm_reset_control_get_shared(dev, "global"); ++ if (IS_ERR(phy_dev->rstc)) { ++ dev_err(dev, "failed to ctrl picoPHY reset\n"); ++ return PTR_ERR(phy_dev->rstc); ++ } ++ ++ phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port"); ++ if (IS_ERR(phy_dev->rstport)) { ++ dev_err(dev, "failed to ctrl picoPHY reset\n"); ++ return PTR_ERR(phy_dev->rstport); ++ } ++ ++ /* 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"); ++ 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 = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); ++ if (IS_ERR(phy)) { ++ dev_err(dev, "failed to create Display Port PHY\n"); ++ return PTR_ERR(phy); ++ } ++ ++ phy_dev->phy = phy; ++ phy_set_drvdata(phy, phy_dev); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ dev_info(dev, "STiH407 USB Generic picoPHY driver probed!"); ++ ++ return 0; ++} ++ ++static const struct of_device_id stih407_usb2_picophy_of_match[] = { ++ { .compatible = "st,stih407-usb2-phy" }, ++ { /*sentinel */ }, ++}; ++ ++MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match); ++ ++static struct platform_driver stih407_usb2_picophy_driver = { ++ .probe = stih407_usb2_picophy_probe, ++ .driver = { ++ .name = "stih407-usb-genphy", ++ .of_match_table = stih407_usb2_picophy_of_match, ++ } ++}; ++ ++module_platform_driver(stih407_usb2_picophy_driver); ++ ++MODULE_AUTHOR("Giuseppe Cavallaro "); ++MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig +new file mode 100644 +index 000000000000..20503562666c +--- /dev/null ++++ b/drivers/phy/ti/Kconfig +@@ -0,0 +1,78 @@ ++# ++# Phy drivers for TI platforms ++# ++config PHY_DA8XX_USB ++ tristate "TI DA8xx USB PHY Driver" ++ depends on ARCH_DAVINCI_DA8XX ++ select GENERIC_PHY ++ select MFD_SYSCON ++ help ++ Enable this to support the USB PHY on DA8xx SoCs. ++ ++ This driver controls both the USB 1.1 PHY and the USB 2.0 PHY. ++ ++config PHY_DM816X_USB ++ tristate "TI dm816x USB PHY driver" ++ depends on ARCH_OMAP2PLUS ++ depends on USB_SUPPORT ++ select GENERIC_PHY ++ select USB_PHY ++ help ++ Enable this for dm816x USB to work. ++ ++config OMAP_CONTROL_PHY ++ tristate "OMAP CONTROL PHY Driver" ++ depends on ARCH_OMAP2PLUS || COMPILE_TEST ++ help ++ Enable this to add support for the PHY part present in the control ++ module. This driver has API to power on the USB2 PHY and to write to ++ the mailbox. The mailbox is present only in omap4 and the register to ++ power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an ++ additional register to power on USB3 PHY/SATA PHY/PCIE PHY ++ (PIPE3 PHY). ++ ++config OMAP_USB2 ++ tristate "OMAP USB2 PHY Driver" ++ depends on ARCH_OMAP2PLUS ++ depends on USB_SUPPORT ++ select GENERIC_PHY ++ select USB_PHY ++ select OMAP_CONTROL_PHY ++ depends on OMAP_OCP2SCP ++ help ++ Enable this to support the transceiver that is part of SOC. This ++ driver takes care of all the PHY functionality apart from comparator. ++ The USB OTG controller communicates with the comparator using this ++ driver. ++ ++config TI_PIPE3 ++ tristate "TI PIPE3 PHY Driver" ++ depends on ARCH_OMAP2PLUS || COMPILE_TEST ++ select GENERIC_PHY ++ select OMAP_CONTROL_PHY ++ depends on OMAP_OCP2SCP ++ help ++ Enable this to support the PIPE3 PHY that is part of TI SOCs. This ++ driver takes care of all the PHY functionality apart from comparator. ++ This driver interacts with the "OMAP Control PHY Driver" to power ++ on/off the PHY. ++ ++config PHY_TUSB1210 ++ tristate "TI TUSB1210 ULPI PHY module" ++ depends on USB_ULPI_BUS ++ select GENERIC_PHY ++ help ++ Support for TI TUSB1210 USB ULPI PHY. ++ ++config TWL4030_USB ++ tristate "TWL4030 USB Transceiver Driver" ++ depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS ++ depends on USB_SUPPORT ++ depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't 'y' ++ select GENERIC_PHY ++ select USB_PHY ++ help ++ Enable this to support the USB OTG transceiver on TWL4030 ++ family chips (including the TWL5030 and TPS659x0 devices). ++ This transceiver supports high and full speed devices plus, ++ in host mode, low speed. +diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile +new file mode 100644 +index 000000000000..0cc3a1a557a3 +--- /dev/null ++++ b/drivers/phy/ti/Makefile +@@ -0,0 +1,7 @@ ++obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o ++obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o ++obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o ++obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o ++obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o ++obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o ++obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o +diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c +new file mode 100644 +index 000000000000..1b82bff6330f +--- /dev/null ++++ b/drivers/phy/ti/phy-da8xx-usb.c +@@ -0,0 +1,251 @@ ++/* ++ * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver ++ * ++ * Copyright (C) 2016 David Lechner ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PHY_INIT_BITS (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN) ++ ++struct da8xx_usb_phy { ++ struct phy_provider *phy_provider; ++ struct phy *usb11_phy; ++ struct phy *usb20_phy; ++ struct clk *usb11_clk; ++ struct clk *usb20_clk; ++ struct regmap *regmap; ++}; ++ ++static int da8xx_usb11_phy_power_on(struct phy *phy) ++{ ++ struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); ++ int ret; ++ ++ ret = clk_prepare_enable(d_phy->usb11_clk); ++ if (ret) ++ return ret; ++ ++ regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, ++ CFGCHIP2_USB1SUSPENDM); ++ ++ return 0; ++} ++ ++static int da8xx_usb11_phy_power_off(struct phy *phy) ++{ ++ struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); ++ ++ regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0); ++ ++ clk_disable_unprepare(d_phy->usb11_clk); ++ ++ return 0; ++} ++ ++static const struct phy_ops da8xx_usb11_phy_ops = { ++ .power_on = da8xx_usb11_phy_power_on, ++ .power_off = da8xx_usb11_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int da8xx_usb20_phy_power_on(struct phy *phy) ++{ ++ struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); ++ int ret; ++ ++ ret = clk_prepare_enable(d_phy->usb20_clk); ++ if (ret) ++ return ret; ++ ++ regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0); ++ ++ return 0; ++} ++ ++static int da8xx_usb20_phy_power_off(struct phy *phy) ++{ ++ struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); ++ ++ regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, ++ CFGCHIP2_OTGPWRDN); ++ ++ clk_disable_unprepare(d_phy->usb20_clk); ++ ++ return 0; ++} ++ ++static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode) ++{ ++ struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); ++ u32 val; ++ ++ switch (mode) { ++ case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */ ++ val = CFGCHIP2_OTGMODE_FORCE_HOST; ++ break; ++ case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */ ++ val = CFGCHIP2_OTGMODE_FORCE_DEVICE; ++ break; ++ case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */ ++ val = CFGCHIP2_OTGMODE_NO_OVERRIDE; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK, ++ val); ++ ++ return 0; ++} ++ ++static const struct phy_ops da8xx_usb20_phy_ops = { ++ .power_on = da8xx_usb20_phy_power_on, ++ .power_off = da8xx_usb20_phy_power_off, ++ .set_mode = da8xx_usb20_phy_set_mode, ++ .owner = THIS_MODULE, ++}; ++ ++static struct phy *da8xx_usb_phy_of_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev); ++ ++ if (!d_phy) ++ return ERR_PTR(-ENODEV); ++ ++ switch (args->args[0]) { ++ case 0: ++ return d_phy->usb20_phy; ++ case 1: ++ return d_phy->usb11_phy; ++ default: ++ return ERR_PTR(-EINVAL); ++ } ++} ++ ++static int da8xx_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct da8xx_usb_phy *d_phy; ++ ++ d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL); ++ if (!d_phy) ++ return -ENOMEM; ++ ++ if (node) ++ d_phy->regmap = syscon_regmap_lookup_by_compatible( ++ "ti,da830-cfgchip"); ++ else ++ d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon"); ++ if (IS_ERR(d_phy->regmap)) { ++ dev_err(dev, "Failed to get syscon\n"); ++ return PTR_ERR(d_phy->regmap); ++ } ++ ++ d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy"); ++ if (IS_ERR(d_phy->usb11_clk)) { ++ dev_err(dev, "Failed to get usb11_phy clock\n"); ++ return PTR_ERR(d_phy->usb11_clk); ++ } ++ ++ d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy"); ++ if (IS_ERR(d_phy->usb20_clk)) { ++ dev_err(dev, "Failed to get usb20_phy clock\n"); ++ return PTR_ERR(d_phy->usb20_clk); ++ } ++ ++ d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops); ++ if (IS_ERR(d_phy->usb11_phy)) { ++ dev_err(dev, "Failed to create usb11 phy\n"); ++ return PTR_ERR(d_phy->usb11_phy); ++ } ++ ++ d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops); ++ if (IS_ERR(d_phy->usb20_phy)) { ++ dev_err(dev, "Failed to create usb20 phy\n"); ++ return PTR_ERR(d_phy->usb20_phy); ++ } ++ ++ platform_set_drvdata(pdev, d_phy); ++ phy_set_drvdata(d_phy->usb11_phy, d_phy); ++ phy_set_drvdata(d_phy->usb20_phy, d_phy); ++ ++ if (node) { ++ d_phy->phy_provider = devm_of_phy_provider_register(dev, ++ da8xx_usb_phy_of_xlate); ++ if (IS_ERR(d_phy->phy_provider)) { ++ dev_err(dev, "Failed to create phy provider\n"); ++ return PTR_ERR(d_phy->phy_provider); ++ } ++ } else { ++ int ret; ++ ++ ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", ++ "ohci-da8xx"); ++ if (ret) ++ dev_warn(dev, "Failed to create usb11 phy lookup\n"); ++ ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy", ++ "musb-da8xx"); ++ if (ret) ++ dev_warn(dev, "Failed to create usb20 phy lookup\n"); ++ } ++ ++ regmap_write_bits(d_phy->regmap, CFGCHIP(2), ++ PHY_INIT_BITS, PHY_INIT_BITS); ++ ++ return 0; ++} ++ ++static int da8xx_usb_phy_remove(struct platform_device *pdev) ++{ ++ struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev); ++ ++ if (!pdev->dev.of_node) { ++ phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx"); ++ phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx"); ++ } ++ ++ return 0; ++} ++ ++static const struct of_device_id da8xx_usb_phy_ids[] = { ++ { .compatible = "ti,da830-usb-phy" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids); ++ ++static struct platform_driver da8xx_usb_phy_driver = { ++ .probe = da8xx_usb_phy_probe, ++ .remove = da8xx_usb_phy_remove, ++ .driver = { ++ .name = "da8xx-usb-phy", ++ .of_match_table = da8xx_usb_phy_ids, ++ }, ++}; ++ ++module_platform_driver(da8xx_usb_phy_driver); ++ ++MODULE_ALIAS("platform:da8xx-usb-phy"); ++MODULE_AUTHOR("David Lechner "); ++MODULE_DESCRIPTION("TI DA8xx USB PHY driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c +new file mode 100644 +index 000000000000..cbcce7cf0028 +--- /dev/null ++++ b/drivers/phy/ti/phy-dm816x-usb.c +@@ -0,0 +1,290 @@ ++/* ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation version 2. ++ * ++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* ++ * TRM has two sets of USB_CTRL registers.. The correct register bits ++ * are in TRM section 24.9.8.2 USB_CTRL Register. The TRM documents the ++ * phy as being SR70LX Synopsys USB 2.0 OTG nanoPHY. It also seems at ++ * least dm816x rev c ignores writes to USB_CTRL register, but the TI ++ * kernel is writing to those so it's possible that later revisions ++ * have worknig USB_CTRL register. ++ * ++ * Also note that At least USB_CTRL register seems to be dm816x specific ++ * according to the TRM. It's possible that USBPHY_CTRL is more generic, ++ * but that would have to be checked against the SR70LX documentation ++ * which does not seem to be publicly available. ++ * ++ * Finally, the phy on dm814x and am335x is different from dm816x. ++ */ ++#define DM816X_USB_CTRL_PHYCLKSRC BIT(8) /* 1 = PLL ref clock */ ++#define DM816X_USB_CTRL_PHYSLEEP1 BIT(1) /* Enable the first phy */ ++#define DM816X_USB_CTRL_PHYSLEEP0 BIT(0) /* Enable the second phy */ ++ ++#define DM816X_USBPHY_CTRL_TXRISETUNE 1 ++#define DM816X_USBPHY_CTRL_TXVREFTUNE 0xc ++#define DM816X_USBPHY_CTRL_TXPREEMTUNE 0x2 ++ ++struct dm816x_usb_phy { ++ struct regmap *syscon; ++ struct device *dev; ++ unsigned int instance; ++ struct clk *refclk; ++ struct usb_phy phy; ++ unsigned int usb_ctrl; /* Shared between phy0 and phy1 */ ++ unsigned int usbphy_ctrl; ++}; ++ ++static int dm816x_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) ++{ ++ otg->host = host; ++ if (!host) ++ otg->state = OTG_STATE_UNDEFINED; ++ ++ return 0; ++} ++ ++static int dm816x_usb_phy_set_peripheral(struct usb_otg *otg, ++ struct usb_gadget *gadget) ++{ ++ otg->gadget = gadget; ++ if (!gadget) ++ otg->state = OTG_STATE_UNDEFINED; ++ ++ return 0; ++} ++ ++static int dm816x_usb_phy_init(struct phy *x) ++{ ++ struct dm816x_usb_phy *phy = phy_get_drvdata(x); ++ unsigned int val; ++ int error; ++ ++ if (clk_get_rate(phy->refclk) != 24000000) ++ dev_warn(phy->dev, "nonstandard phy refclk\n"); ++ ++ /* Set PLL ref clock and put phys to sleep */ ++ error = regmap_update_bits(phy->syscon, phy->usb_ctrl, ++ DM816X_USB_CTRL_PHYCLKSRC | ++ DM816X_USB_CTRL_PHYSLEEP1 | ++ DM816X_USB_CTRL_PHYSLEEP0, ++ 0); ++ regmap_read(phy->syscon, phy->usb_ctrl, &val); ++ if ((val & 3) != 0) ++ dev_info(phy->dev, ++ "Working dm816x USB_CTRL! (0x%08x)\n", ++ val); ++ ++ /* ++ * TI kernel sets these values for "symmetrical eye diagram and ++ * better signal quality" so let's assume somebody checked the ++ * values with a scope and set them here too. ++ */ ++ regmap_read(phy->syscon, phy->usbphy_ctrl, &val); ++ val |= DM816X_USBPHY_CTRL_TXRISETUNE | ++ DM816X_USBPHY_CTRL_TXVREFTUNE | ++ DM816X_USBPHY_CTRL_TXPREEMTUNE; ++ regmap_write(phy->syscon, phy->usbphy_ctrl, val); ++ ++ return 0; ++} ++ ++static const struct phy_ops ops = { ++ .init = dm816x_usb_phy_init, ++ .owner = THIS_MODULE, ++}; ++ ++static int __maybe_unused dm816x_usb_phy_runtime_suspend(struct device *dev) ++{ ++ struct dm816x_usb_phy *phy = dev_get_drvdata(dev); ++ unsigned int mask, val; ++ int error = 0; ++ ++ mask = BIT(phy->instance); ++ val = ~BIT(phy->instance); ++ error = regmap_update_bits(phy->syscon, phy->usb_ctrl, ++ mask, val); ++ if (error) ++ dev_err(phy->dev, "phy%i failed to power off\n", ++ phy->instance); ++ clk_disable(phy->refclk); ++ ++ return 0; ++} ++ ++static int __maybe_unused dm816x_usb_phy_runtime_resume(struct device *dev) ++{ ++ struct dm816x_usb_phy *phy = dev_get_drvdata(dev); ++ unsigned int mask, val; ++ int error; ++ ++ error = clk_enable(phy->refclk); ++ if (error) ++ return error; ++ ++ /* ++ * Note that at least dm816x rev c does not seem to do ++ * anything with the USB_CTRL register. But let's follow ++ * what the TI tree is doing in case later revisions use ++ * USB_CTRL. ++ */ ++ mask = BIT(phy->instance); ++ val = BIT(phy->instance); ++ error = regmap_update_bits(phy->syscon, phy->usb_ctrl, ++ mask, val); ++ if (error) { ++ dev_err(phy->dev, "phy%i failed to power on\n", ++ phy->instance); ++ clk_disable(phy->refclk); ++ return error; ++ } ++ ++ return 0; ++} ++ ++static UNIVERSAL_DEV_PM_OPS(dm816x_usb_phy_pm_ops, ++ dm816x_usb_phy_runtime_suspend, ++ dm816x_usb_phy_runtime_resume, ++ NULL); ++ ++#ifdef CONFIG_OF ++static const struct of_device_id dm816x_usb_phy_id_table[] = { ++ { ++ .compatible = "ti,dm8168-usb-phy", ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, dm816x_usb_phy_id_table); ++#endif ++ ++static int dm816x_usb_phy_probe(struct platform_device *pdev) ++{ ++ struct dm816x_usb_phy *phy; ++ struct resource *res; ++ struct phy *generic_phy; ++ struct phy_provider *phy_provider; ++ struct usb_otg *otg; ++ const struct of_device_id *of_id; ++ const struct usb_phy_data *phy_data; ++ int error; ++ ++ of_id = of_match_device(of_match_ptr(dm816x_usb_phy_id_table), ++ &pdev->dev); ++ if (!of_id) ++ return -EINVAL; ++ ++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -ENOENT; ++ ++ phy->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, ++ "syscon"); ++ if (IS_ERR(phy->syscon)) ++ return PTR_ERR(phy->syscon); ++ ++ /* ++ * According to sprs614e.pdf, the first usb_ctrl is shared and ++ * the second instance for usb_ctrl is reserved.. Also the ++ * register bits are different from earlier TRMs. ++ */ ++ phy->usb_ctrl = 0x20; ++ phy->usbphy_ctrl = (res->start & 0xff) + 4; ++ if (phy->usbphy_ctrl == 0x2c) ++ phy->instance = 1; ++ ++ phy_data = of_id->data; ++ ++ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); ++ if (!otg) ++ return -ENOMEM; ++ ++ phy->dev = &pdev->dev; ++ phy->phy.dev = phy->dev; ++ phy->phy.label = "dm8168_usb_phy"; ++ phy->phy.otg = otg; ++ phy->phy.type = USB_PHY_TYPE_USB2; ++ otg->set_host = dm816x_usb_phy_set_host; ++ otg->set_peripheral = dm816x_usb_phy_set_peripheral; ++ otg->usb_phy = &phy->phy; ++ ++ platform_set_drvdata(pdev, phy); ++ ++ phy->refclk = devm_clk_get(phy->dev, "refclk"); ++ if (IS_ERR(phy->refclk)) ++ return PTR_ERR(phy->refclk); ++ error = clk_prepare(phy->refclk); ++ if (error) ++ return error; ++ ++ pm_runtime_enable(phy->dev); ++ generic_phy = devm_phy_create(phy->dev, NULL, &ops); ++ if (IS_ERR(generic_phy)) ++ return PTR_ERR(generic_phy); ++ ++ phy_set_drvdata(generic_phy, phy); ++ ++ phy_provider = devm_of_phy_provider_register(phy->dev, ++ of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ usb_add_phy_dev(&phy->phy); ++ ++ return 0; ++} ++ ++static int dm816x_usb_phy_remove(struct platform_device *pdev) ++{ ++ struct dm816x_usb_phy *phy = platform_get_drvdata(pdev); ++ ++ usb_remove_phy(&phy->phy); ++ pm_runtime_disable(phy->dev); ++ clk_unprepare(phy->refclk); ++ ++ return 0; ++} ++ ++static struct platform_driver dm816x_usb_phy_driver = { ++ .probe = dm816x_usb_phy_probe, ++ .remove = dm816x_usb_phy_remove, ++ .driver = { ++ .name = "dm816x-usb-phy", ++ .pm = &dm816x_usb_phy_pm_ops, ++ .of_match_table = of_match_ptr(dm816x_usb_phy_id_table), ++ }, ++}; ++ ++module_platform_driver(dm816x_usb_phy_driver); ++ ++MODULE_ALIAS("platform:dm816x_usb"); ++MODULE_AUTHOR("Tony Lindgren "); ++MODULE_DESCRIPTION("dm816x usb phy driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/ti/phy-omap-control.c b/drivers/phy/ti/phy-omap-control.c +new file mode 100644 +index 000000000000..e9c41b3fa0ee +--- /dev/null ++++ b/drivers/phy/ti/phy-omap-control.c +@@ -0,0 +1,360 @@ ++/* ++ * omap-control-phy.c - The PHY part of control module. ++ * ++ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Author: Kishon Vijay Abraham I ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * omap_control_pcie_pcs - set the PCS delay count ++ * @dev: the control module device ++ * @delay: 8 bit delay value ++ */ ++void omap_control_pcie_pcs(struct device *dev, u8 delay) ++{ ++ u32 val; ++ struct omap_control_phy *control_phy; ++ ++ if (IS_ERR(dev) || !dev) { ++ pr_err("%s: invalid device\n", __func__); ++ return; ++ } ++ ++ control_phy = dev_get_drvdata(dev); ++ if (!control_phy) { ++ dev_err(dev, "%s: invalid control phy device\n", __func__); ++ return; ++ } ++ ++ if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { ++ dev_err(dev, "%s: unsupported operation\n", __func__); ++ return; ++ } ++ ++ val = readl(control_phy->pcie_pcs); ++ val &= ~(OMAP_CTRL_PCIE_PCS_MASK << ++ OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); ++ val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); ++ writel(val, control_phy->pcie_pcs); ++} ++EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); ++ ++/** ++ * omap_control_phy_power - power on/off the phy using control module reg ++ * @dev: the control module device ++ * @on: 0 or 1, based on powering on or off the PHY ++ */ ++void omap_control_phy_power(struct device *dev, int on) ++{ ++ u32 val; ++ unsigned long rate; ++ struct omap_control_phy *control_phy; ++ ++ if (IS_ERR(dev) || !dev) { ++ pr_err("%s: invalid device\n", __func__); ++ return; ++ } ++ ++ control_phy = dev_get_drvdata(dev); ++ if (!control_phy) { ++ dev_err(dev, "%s: invalid control phy device\n", __func__); ++ return; ++ } ++ ++ if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) ++ return; ++ ++ val = readl(control_phy->power); ++ ++ switch (control_phy->type) { ++ case OMAP_CTRL_TYPE_USB2: ++ if (on) ++ val &= ~OMAP_CTRL_DEV_PHY_PD; ++ else ++ val |= OMAP_CTRL_DEV_PHY_PD; ++ break; ++ ++ case OMAP_CTRL_TYPE_PCIE: ++ case OMAP_CTRL_TYPE_PIPE3: ++ rate = clk_get_rate(control_phy->sys_clk); ++ rate = rate/1000000; ++ ++ if (on) { ++ val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | ++ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); ++ val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << ++ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; ++ val |= rate << ++ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; ++ } else { ++ val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; ++ val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << ++ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; ++ } ++ break; ++ ++ case OMAP_CTRL_TYPE_DRA7USB2: ++ if (on) ++ val &= ~OMAP_CTRL_USB2_PHY_PD; ++ else ++ val |= OMAP_CTRL_USB2_PHY_PD; ++ break; ++ ++ case OMAP_CTRL_TYPE_AM437USB2: ++ if (on) { ++ val &= ~(AM437X_CTRL_USB2_PHY_PD | ++ AM437X_CTRL_USB2_OTG_PD); ++ val |= (AM437X_CTRL_USB2_OTGVDET_EN | ++ AM437X_CTRL_USB2_OTGSESSEND_EN); ++ } else { ++ val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | ++ AM437X_CTRL_USB2_OTGSESSEND_EN); ++ val |= (AM437X_CTRL_USB2_PHY_PD | ++ AM437X_CTRL_USB2_OTG_PD); ++ } ++ break; ++ default: ++ dev_err(dev, "%s: type %d not recognized\n", ++ __func__, control_phy->type); ++ break; ++ } ++ ++ writel(val, control_phy->power); ++} ++EXPORT_SYMBOL_GPL(omap_control_phy_power); ++ ++/** ++ * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded ++ * @ctrl_phy: struct omap_control_phy * ++ * ++ * Writes to the mailbox register to notify the usb core that a usb ++ * device has been connected. ++ */ ++static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy) ++{ ++ u32 val; ++ ++ val = readl(ctrl_phy->otghs_control); ++ val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); ++ val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; ++ writel(val, ctrl_phy->otghs_control); ++} ++ ++/** ++ * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high ++ * impedance ++ * @ctrl_phy: struct omap_control_phy * ++ * ++ * Writes to the mailbox register to notify the usb core that it has been ++ * connected to a usb host. ++ */ ++static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy) ++{ ++ u32 val; ++ ++ val = readl(ctrl_phy->otghs_control); ++ val &= ~OMAP_CTRL_DEV_SESSEND; ++ val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | ++ OMAP_CTRL_DEV_VBUSVALID; ++ writel(val, ctrl_phy->otghs_control); ++} ++ ++/** ++ * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high ++ * impedance ++ * @ctrl_phy: struct omap_control_phy * ++ * ++ * Writes to the mailbox register to notify the usb core it's now in ++ * disconnected state. ++ */ ++static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy) ++{ ++ u32 val; ++ ++ val = readl(ctrl_phy->otghs_control); ++ val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); ++ val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; ++ writel(val, ctrl_phy->otghs_control); ++} ++ ++/** ++ * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode ++ * or device mode or to denote disconnected state ++ * @dev: the control module device ++ * @mode: The mode to which usb should be configured ++ * ++ * This is an API to write to the mailbox register to notify the usb core that ++ * a usb device has been connected. ++ */ ++void omap_control_usb_set_mode(struct device *dev, ++ enum omap_control_usb_mode mode) ++{ ++ struct omap_control_phy *ctrl_phy; ++ ++ if (IS_ERR(dev) || !dev) ++ return; ++ ++ ctrl_phy = dev_get_drvdata(dev); ++ if (!ctrl_phy) { ++ dev_err(dev, "Invalid control phy device\n"); ++ return; ++ } ++ ++ if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS) ++ return; ++ ++ switch (mode) { ++ case USB_MODE_HOST: ++ omap_control_usb_host_mode(ctrl_phy); ++ break; ++ case USB_MODE_DEVICE: ++ omap_control_usb_device_mode(ctrl_phy); ++ break; ++ case USB_MODE_DISCONNECT: ++ omap_control_usb_set_sessionend(ctrl_phy); ++ break; ++ default: ++ dev_vdbg(dev, "invalid omap control usb mode\n"); ++ } ++} ++EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); ++ ++static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; ++static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; ++static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; ++static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; ++static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; ++static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; ++ ++static const struct of_device_id omap_control_phy_id_table[] = { ++ { ++ .compatible = "ti,control-phy-otghs", ++ .data = &otghs_data, ++ }, ++ { ++ .compatible = "ti,control-phy-usb2", ++ .data = &usb2_data, ++ }, ++ { ++ .compatible = "ti,control-phy-pipe3", ++ .data = &pipe3_data, ++ }, ++ { ++ .compatible = "ti,control-phy-pcie", ++ .data = &pcie_data, ++ }, ++ { ++ .compatible = "ti,control-phy-usb2-dra7", ++ .data = &dra7usb2_data, ++ }, ++ { ++ .compatible = "ti,control-phy-usb2-am437", ++ .data = &am437usb2_data, ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, omap_control_phy_id_table); ++ ++static int omap_control_phy_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ const struct of_device_id *of_id; ++ struct omap_control_phy *control_phy; ++ ++ of_id = of_match_device(omap_control_phy_id_table, &pdev->dev); ++ if (!of_id) ++ return -EINVAL; ++ ++ control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy), ++ GFP_KERNEL); ++ if (!control_phy) ++ return -ENOMEM; ++ ++ control_phy->dev = &pdev->dev; ++ control_phy->type = *(enum omap_control_phy_type *)of_id->data; ++ ++ if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) { ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "otghs_control"); ++ control_phy->otghs_control = devm_ioremap_resource( ++ &pdev->dev, res); ++ if (IS_ERR(control_phy->otghs_control)) ++ return PTR_ERR(control_phy->otghs_control); ++ } else { ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "power"); ++ control_phy->power = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(control_phy->power)) { ++ dev_err(&pdev->dev, "Couldn't get power register\n"); ++ return PTR_ERR(control_phy->power); ++ } ++ } ++ ++ if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || ++ control_phy->type == OMAP_CTRL_TYPE_PCIE) { ++ control_phy->sys_clk = devm_clk_get(control_phy->dev, ++ "sys_clkin"); ++ if (IS_ERR(control_phy->sys_clk)) { ++ pr_err("%s: unable to get sys_clkin\n", __func__); ++ return -EINVAL; ++ } ++ } ++ ++ if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "pcie_pcs"); ++ control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(control_phy->pcie_pcs)) ++ return PTR_ERR(control_phy->pcie_pcs); ++ } ++ ++ dev_set_drvdata(control_phy->dev, control_phy); ++ ++ return 0; ++} ++ ++static struct platform_driver omap_control_phy_driver = { ++ .probe = omap_control_phy_probe, ++ .driver = { ++ .name = "omap-control-phy", ++ .of_match_table = omap_control_phy_id_table, ++ }, ++}; ++ ++static int __init omap_control_phy_init(void) ++{ ++ return platform_driver_register(&omap_control_phy_driver); ++} ++subsys_initcall(omap_control_phy_init); ++ ++static void __exit omap_control_phy_exit(void) ++{ ++ platform_driver_unregister(&omap_control_phy_driver); ++} ++module_exit(omap_control_phy_exit); ++ ++MODULE_ALIAS("platform:omap_control_phy"); ++MODULE_AUTHOR("Texas Instruments Inc."); ++MODULE_DESCRIPTION("OMAP Control Module PHY Driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c +new file mode 100644 +index 000000000000..fe909fd8144f +--- /dev/null ++++ b/drivers/phy/ti/phy-omap-usb2.c +@@ -0,0 +1,439 @@ ++/* ++ * omap-usb2.c - USB PHY, talking to musb controller in OMAP. ++ * ++ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Author: Kishon Vijay Abraham I ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define USB2PHY_DISCON_BYP_LATCH (1 << 31) ++#define USB2PHY_ANA_CONFIG1 0x4c ++ ++/** ++ * omap_usb2_set_comparator - links the comparator present in the sytem with ++ * this phy ++ * @comparator - the companion phy(comparator) for this phy ++ * ++ * The phy companion driver should call this API passing the phy_companion ++ * filled with set_vbus and start_srp to be used by usb phy. ++ * ++ * For use by phy companion driver ++ */ ++int omap_usb2_set_comparator(struct phy_companion *comparator) ++{ ++ struct omap_usb *phy; ++ struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2); ++ ++ if (IS_ERR(x)) ++ return -ENODEV; ++ ++ phy = phy_to_omapusb(x); ++ phy->comparator = comparator; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); ++ ++static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) ++{ ++ struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); ++ ++ if (!phy->comparator) ++ return -ENODEV; ++ ++ return phy->comparator->set_vbus(phy->comparator, enabled); ++} ++ ++static int omap_usb_start_srp(struct usb_otg *otg) ++{ ++ struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); ++ ++ if (!phy->comparator) ++ return -ENODEV; ++ ++ return phy->comparator->start_srp(phy->comparator); ++} ++ ++static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) ++{ ++ otg->host = host; ++ if (!host) ++ otg->state = OTG_STATE_UNDEFINED; ++ ++ return 0; ++} ++ ++static int omap_usb_set_peripheral(struct usb_otg *otg, ++ struct usb_gadget *gadget) ++{ ++ otg->gadget = gadget; ++ if (!gadget) ++ otg->state = OTG_STATE_UNDEFINED; ++ ++ return 0; ++} ++ ++static int omap_usb_phy_power(struct omap_usb *phy, int on) ++{ ++ u32 val; ++ int ret; ++ ++ if (!phy->syscon_phy_power) { ++ omap_control_phy_power(phy->control_dev, on); ++ return 0; ++ } ++ ++ if (on) ++ val = phy->power_on; ++ else ++ val = phy->power_off; ++ ++ ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg, ++ phy->mask, val); ++ return ret; ++} ++ ++static int omap_usb_power_off(struct phy *x) ++{ ++ struct omap_usb *phy = phy_get_drvdata(x); ++ ++ return omap_usb_phy_power(phy, false); ++} ++ ++static int omap_usb_power_on(struct phy *x) ++{ ++ struct omap_usb *phy = phy_get_drvdata(x); ++ ++ return omap_usb_phy_power(phy, true); ++} ++ ++static int omap_usb2_disable_clocks(struct omap_usb *phy) ++{ ++ clk_disable(phy->wkupclk); ++ if (!IS_ERR(phy->optclk)) ++ clk_disable(phy->optclk); ++ ++ return 0; ++} ++ ++static int omap_usb2_enable_clocks(struct omap_usb *phy) ++{ ++ int ret; ++ ++ ret = clk_enable(phy->wkupclk); ++ if (ret < 0) { ++ dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); ++ goto err0; ++ } ++ ++ if (!IS_ERR(phy->optclk)) { ++ ret = clk_enable(phy->optclk); ++ if (ret < 0) { ++ dev_err(phy->dev, "Failed to enable optclk %d\n", ret); ++ goto err1; ++ } ++ } ++ ++ return 0; ++ ++err1: ++ clk_disable(phy->wkupclk); ++ ++err0: ++ return ret; ++} ++ ++static int omap_usb_init(struct phy *x) ++{ ++ struct omap_usb *phy = phy_get_drvdata(x); ++ u32 val; ++ ++ omap_usb2_enable_clocks(phy); ++ ++ if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { ++ /* ++ * ++ * Reduce the sensitivity of internal PHY by enabling the ++ * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This ++ * resolves issues with certain devices which can otherwise ++ * be prone to false disconnects. ++ * ++ */ ++ val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1); ++ val |= USB2PHY_DISCON_BYP_LATCH; ++ omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val); ++ } ++ ++ return 0; ++} ++ ++static int omap_usb_exit(struct phy *x) ++{ ++ struct omap_usb *phy = phy_get_drvdata(x); ++ ++ return omap_usb2_disable_clocks(phy); ++} ++ ++static const struct phy_ops ops = { ++ .init = omap_usb_init, ++ .exit = omap_usb_exit, ++ .power_on = omap_usb_power_on, ++ .power_off = omap_usb_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct usb_phy_data omap_usb2_data = { ++ .label = "omap_usb2", ++ .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS, ++ .mask = OMAP_DEV_PHY_PD, ++ .power_off = OMAP_DEV_PHY_PD, ++}; ++ ++static const struct usb_phy_data omap5_usb2_data = { ++ .label = "omap5_usb2", ++ .flags = 0, ++ .mask = OMAP_DEV_PHY_PD, ++ .power_off = OMAP_DEV_PHY_PD, ++}; ++ ++static const struct usb_phy_data dra7x_usb2_data = { ++ .label = "dra7x_usb2", ++ .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, ++ .mask = OMAP_DEV_PHY_PD, ++ .power_off = OMAP_DEV_PHY_PD, ++}; ++ ++static const struct usb_phy_data dra7x_usb2_phy2_data = { ++ .label = "dra7x_usb2_phy2", ++ .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, ++ .mask = OMAP_USB2_PHY_PD, ++ .power_off = OMAP_USB2_PHY_PD, ++}; ++ ++static const struct usb_phy_data am437x_usb2_data = { ++ .label = "am437x_usb2", ++ .flags = 0, ++ .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD | ++ AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, ++ .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, ++ .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, ++}; ++ ++static const struct of_device_id omap_usb2_id_table[] = { ++ { ++ .compatible = "ti,omap-usb2", ++ .data = &omap_usb2_data, ++ }, ++ { ++ .compatible = "ti,omap5-usb2", ++ .data = &omap5_usb2_data, ++ }, ++ { ++ .compatible = "ti,dra7x-usb2", ++ .data = &dra7x_usb2_data, ++ }, ++ { ++ .compatible = "ti,dra7x-usb2-phy2", ++ .data = &dra7x_usb2_phy2_data, ++ }, ++ { ++ .compatible = "ti,am437x-usb2", ++ .data = &am437x_usb2_data, ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, omap_usb2_id_table); ++ ++static int omap_usb2_probe(struct platform_device *pdev) ++{ ++ struct omap_usb *phy; ++ struct phy *generic_phy; ++ struct resource *res; ++ struct phy_provider *phy_provider; ++ struct usb_otg *otg; ++ struct device_node *node = pdev->dev.of_node; ++ struct device_node *control_node; ++ struct platform_device *control_pdev; ++ const struct of_device_id *of_id; ++ struct usb_phy_data *phy_data; ++ ++ of_id = of_match_device(omap_usb2_id_table, &pdev->dev); ++ ++ if (!of_id) ++ return -EINVAL; ++ ++ phy_data = (struct usb_phy_data *)of_id->data; ++ ++ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); ++ if (!otg) ++ return -ENOMEM; ++ ++ phy->dev = &pdev->dev; ++ ++ phy->phy.dev = phy->dev; ++ phy->phy.label = phy_data->label; ++ phy->phy.otg = otg; ++ phy->phy.type = USB_PHY_TYPE_USB2; ++ phy->mask = phy_data->mask; ++ phy->power_on = phy_data->power_on; ++ phy->power_off = phy_data->power_off; ++ ++ if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ phy->phy_base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(phy->phy_base)) ++ return PTR_ERR(phy->phy_base); ++ phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT; ++ } ++ ++ phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node, ++ "syscon-phy-power"); ++ if (IS_ERR(phy->syscon_phy_power)) { ++ dev_dbg(&pdev->dev, ++ "can't get syscon-phy-power, using control device\n"); ++ phy->syscon_phy_power = NULL; ++ ++ control_node = of_parse_phandle(node, "ctrl-module", 0); ++ if (!control_node) { ++ dev_err(&pdev->dev, ++ "Failed to get control device phandle\n"); ++ return -EINVAL; ++ } ++ ++ control_pdev = of_find_device_by_node(control_node); ++ if (!control_pdev) { ++ dev_err(&pdev->dev, "Failed to get control device\n"); ++ return -EINVAL; ++ } ++ phy->control_dev = &control_pdev->dev; ++ } else { ++ if (of_property_read_u32_index(node, ++ "syscon-phy-power", 1, ++ &phy->power_reg)) { ++ dev_err(&pdev->dev, ++ "couldn't get power reg. offset\n"); ++ return -EINVAL; ++ } ++ } ++ ++ otg->set_host = omap_usb_set_host; ++ otg->set_peripheral = omap_usb_set_peripheral; ++ if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) ++ otg->set_vbus = omap_usb_set_vbus; ++ if (phy_data->flags & OMAP_USB2_HAS_START_SRP) ++ otg->start_srp = omap_usb_start_srp; ++ otg->usb_phy = &phy->phy; ++ ++ platform_set_drvdata(pdev, phy); ++ pm_runtime_enable(phy->dev); ++ ++ generic_phy = devm_phy_create(phy->dev, NULL, &ops); ++ if (IS_ERR(generic_phy)) { ++ pm_runtime_disable(phy->dev); ++ return PTR_ERR(generic_phy); ++ } ++ ++ phy_set_drvdata(generic_phy, phy); ++ omap_usb_power_off(generic_phy); ++ ++ phy_provider = devm_of_phy_provider_register(phy->dev, ++ of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) { ++ pm_runtime_disable(phy->dev); ++ return PTR_ERR(phy_provider); ++ } ++ ++ phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); ++ if (IS_ERR(phy->wkupclk)) { ++ dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n"); ++ phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); ++ if (IS_ERR(phy->wkupclk)) { ++ dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); ++ pm_runtime_disable(phy->dev); ++ return PTR_ERR(phy->wkupclk); ++ } else { ++ dev_warn(&pdev->dev, ++ "found usb_phy_cm_clk32k, please fix DTS\n"); ++ } ++ } ++ clk_prepare(phy->wkupclk); ++ ++ phy->optclk = devm_clk_get(phy->dev, "refclk"); ++ if (IS_ERR(phy->optclk)) { ++ dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n"); ++ phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); ++ if (IS_ERR(phy->optclk)) { ++ dev_dbg(&pdev->dev, ++ "unable to get usb_otg_ss_refclk960m\n"); ++ } else { ++ dev_warn(&pdev->dev, ++ "found usb_otg_ss_refclk960m, please fix DTS\n"); ++ } ++ } ++ ++ if (!IS_ERR(phy->optclk)) ++ clk_prepare(phy->optclk); ++ ++ usb_add_phy_dev(&phy->phy); ++ ++ return 0; ++} ++ ++static int omap_usb2_remove(struct platform_device *pdev) ++{ ++ struct omap_usb *phy = platform_get_drvdata(pdev); ++ ++ clk_unprepare(phy->wkupclk); ++ if (!IS_ERR(phy->optclk)) ++ clk_unprepare(phy->optclk); ++ usb_remove_phy(&phy->phy); ++ pm_runtime_disable(phy->dev); ++ ++ return 0; ++} ++ ++static struct platform_driver omap_usb2_driver = { ++ .probe = omap_usb2_probe, ++ .remove = omap_usb2_remove, ++ .driver = { ++ .name = "omap-usb2", ++ .of_match_table = omap_usb2_id_table, ++ }, ++}; ++ ++module_platform_driver(omap_usb2_driver); ++ ++MODULE_ALIAS("platform:omap_usb2"); ++MODULE_AUTHOR("Texas Instruments Inc."); ++MODULE_DESCRIPTION("OMAP USB2 phy driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c +new file mode 100644 +index 000000000000..9c84d32c6f60 +--- /dev/null ++++ b/drivers/phy/ti/phy-ti-pipe3.c +@@ -0,0 +1,697 @@ ++/* ++ * phy-ti-pipe3 - PIPE3 PHY driver. ++ * ++ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Author: Kishon Vijay Abraham I ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define PLL_STATUS 0x00000004 ++#define PLL_GO 0x00000008 ++#define PLL_CONFIGURATION1 0x0000000C ++#define PLL_CONFIGURATION2 0x00000010 ++#define PLL_CONFIGURATION3 0x00000014 ++#define PLL_CONFIGURATION4 0x00000020 ++ ++#define PLL_REGM_MASK 0x001FFE00 ++#define PLL_REGM_SHIFT 0x9 ++#define PLL_REGM_F_MASK 0x0003FFFF ++#define PLL_REGM_F_SHIFT 0x0 ++#define PLL_REGN_MASK 0x000001FE ++#define PLL_REGN_SHIFT 0x1 ++#define PLL_SELFREQDCO_MASK 0x0000000E ++#define PLL_SELFREQDCO_SHIFT 0x1 ++#define PLL_SD_MASK 0x0003FC00 ++#define PLL_SD_SHIFT 10 ++#define SET_PLL_GO 0x1 ++#define PLL_LDOPWDN BIT(15) ++#define PLL_TICOPWDN BIT(16) ++#define PLL_LOCK 0x2 ++#define PLL_IDLE 0x1 ++ ++#define SATA_PLL_SOFT_RESET BIT(18) ++ ++#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 ++#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14 ++ ++#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 ++#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22 ++ ++#define PIPE3_PHY_TX_RX_POWERON 0x3 ++#define PIPE3_PHY_TX_RX_POWEROFF 0x0 ++ ++#define PCIE_PCS_MASK 0xFF0000 ++#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10 ++ ++/* ++ * This is an Empirical value that works, need to confirm the actual ++ * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status ++ * to be correctly reflected in the PIPE3PHY_PLL_STATUS register. ++ */ ++#define PLL_IDLE_TIME 100 /* in milliseconds */ ++#define PLL_LOCK_TIME 100 /* in milliseconds */ ++ ++struct pipe3_dpll_params { ++ u16 m; ++ u8 n; ++ u8 freq:3; ++ u8 sd; ++ u32 mf; ++}; ++ ++struct pipe3_dpll_map { ++ unsigned long rate; ++ struct pipe3_dpll_params params; ++}; ++ ++struct ti_pipe3 { ++ void __iomem *pll_ctrl_base; ++ struct device *dev; ++ struct device *control_dev; ++ struct clk *wkupclk; ++ struct clk *sys_clk; ++ struct clk *refclk; ++ struct clk *div_clk; ++ struct pipe3_dpll_map *dpll_map; ++ struct regmap *phy_power_syscon; /* ctrl. reg. acces */ ++ struct regmap *pcs_syscon; /* ctrl. reg. acces */ ++ struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */ ++ unsigned int dpll_reset_reg; /* reg. index within syscon */ ++ unsigned int power_reg; /* power reg. index within syscon */ ++ unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */ ++ bool sata_refclk_enabled; ++}; ++ ++static struct pipe3_dpll_map dpll_map_usb[] = { ++ {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ ++ {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ ++ {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ ++ {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ ++ {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ ++ {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ ++ { }, /* Terminator */ ++}; ++ ++static struct pipe3_dpll_map dpll_map_sata[] = { ++ {12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */ ++ {16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */ ++ {19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */ ++ {20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */ ++ {26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */ ++ {38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */ ++ { }, /* Terminator */ ++}; ++ ++static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset) ++{ ++ return __raw_readl(addr + offset); ++} ++ ++static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset, ++ u32 data) ++{ ++ __raw_writel(data, addr + offset); ++} ++ ++static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy) ++{ ++ unsigned long rate; ++ struct pipe3_dpll_map *dpll_map = phy->dpll_map; ++ ++ rate = clk_get_rate(phy->sys_clk); ++ ++ for (; dpll_map->rate; dpll_map++) { ++ if (rate == dpll_map->rate) ++ return &dpll_map->params; ++ } ++ ++ dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate); ++ ++ return NULL; ++} ++ ++static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy); ++static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy); ++ ++static int ti_pipe3_power_off(struct phy *x) ++{ ++ u32 val; ++ int ret; ++ struct ti_pipe3 *phy = phy_get_drvdata(x); ++ ++ if (!phy->phy_power_syscon) { ++ omap_control_phy_power(phy->control_dev, 0); ++ return 0; ++ } ++ ++ val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; ++ ++ ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, ++ PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val); ++ return ret; ++} ++ ++static int ti_pipe3_power_on(struct phy *x) ++{ ++ u32 val; ++ u32 mask; ++ int ret; ++ unsigned long rate; ++ struct ti_pipe3 *phy = phy_get_drvdata(x); ++ ++ if (!phy->phy_power_syscon) { ++ omap_control_phy_power(phy->control_dev, 1); ++ return 0; ++ } ++ ++ rate = clk_get_rate(phy->sys_clk); ++ if (!rate) { ++ dev_err(phy->dev, "Invalid clock rate\n"); ++ return -EINVAL; ++ } ++ rate = rate / 1000000; ++ mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | ++ OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; ++ val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; ++ val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; ++ ++ ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, ++ mask, val); ++ return ret; ++} ++ ++static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy) ++{ ++ u32 val; ++ unsigned long timeout; ++ ++ timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME); ++ do { ++ cpu_relax(); ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); ++ if (val & PLL_LOCK) ++ return 0; ++ } while (!time_after(jiffies, timeout)); ++ ++ dev_err(phy->dev, "DPLL failed to lock\n"); ++ return -EBUSY; ++} ++ ++static int ti_pipe3_dpll_program(struct ti_pipe3 *phy) ++{ ++ u32 val; ++ struct pipe3_dpll_params *dpll_params; ++ ++ dpll_params = ti_pipe3_get_dpll_params(phy); ++ if (!dpll_params) ++ return -EINVAL; ++ ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); ++ val &= ~PLL_REGN_MASK; ++ val |= dpll_params->n << PLL_REGN_SHIFT; ++ ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); ++ ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); ++ val &= ~PLL_SELFREQDCO_MASK; ++ val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; ++ ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); ++ ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); ++ val &= ~PLL_REGM_MASK; ++ val |= dpll_params->m << PLL_REGM_SHIFT; ++ ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); ++ ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); ++ val &= ~PLL_REGM_F_MASK; ++ val |= dpll_params->mf << PLL_REGM_F_SHIFT; ++ ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); ++ ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); ++ val &= ~PLL_SD_MASK; ++ val |= dpll_params->sd << PLL_SD_SHIFT; ++ ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); ++ ++ ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); ++ ++ return ti_pipe3_dpll_wait_lock(phy); ++} ++ ++static int ti_pipe3_init(struct phy *x) ++{ ++ struct ti_pipe3 *phy = phy_get_drvdata(x); ++ u32 val; ++ int ret = 0; ++ ++ ti_pipe3_enable_clocks(phy); ++ /* ++ * Set pcie_pcs register to 0x96 for proper functioning of phy ++ * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table ++ * 18-1804. ++ */ ++ if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { ++ if (!phy->pcs_syscon) { ++ omap_control_pcie_pcs(phy->control_dev, 0x96); ++ return 0; ++ } ++ ++ val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT; ++ ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg, ++ PCIE_PCS_MASK, val); ++ return ret; ++ } ++ ++ /* Bring it out of IDLE if it is IDLE */ ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); ++ if (val & PLL_IDLE) { ++ val &= ~PLL_IDLE; ++ ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); ++ ret = ti_pipe3_dpll_wait_lock(phy); ++ } ++ ++ /* SATA has issues if re-programmed when locked */ ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); ++ if ((val & PLL_LOCK) && of_device_is_compatible(phy->dev->of_node, ++ "ti,phy-pipe3-sata")) ++ return ret; ++ ++ /* Program the DPLL */ ++ ret = ti_pipe3_dpll_program(phy); ++ if (ret) { ++ ti_pipe3_disable_clocks(phy); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int ti_pipe3_exit(struct phy *x) ++{ ++ struct ti_pipe3 *phy = phy_get_drvdata(x); ++ u32 val; ++ unsigned long timeout; ++ ++ /* If dpll_reset_syscon is not present we wont power down SATA DPLL ++ * due to Errata i783 ++ */ ++ if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") && ++ !phy->dpll_reset_syscon) ++ return 0; ++ ++ /* PCIe doesn't have internal DPLL */ ++ if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { ++ /* Put DPLL in IDLE mode */ ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); ++ val |= PLL_IDLE; ++ ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); ++ ++ /* wait for LDO and Oscillator to power down */ ++ timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME); ++ do { ++ cpu_relax(); ++ val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); ++ if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) ++ break; ++ } while (!time_after(jiffies, timeout)); ++ ++ if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { ++ dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n", ++ val); ++ return -EBUSY; ++ } ++ } ++ ++ /* i783: SATA needs control bit toggle after PLL unlock */ ++ if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) { ++ regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, ++ SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET); ++ regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, ++ SATA_PLL_SOFT_RESET, 0); ++ } ++ ++ ti_pipe3_disable_clocks(phy); ++ ++ return 0; ++} ++static const struct phy_ops ops = { ++ .init = ti_pipe3_init, ++ .exit = ti_pipe3_exit, ++ .power_on = ti_pipe3_power_on, ++ .power_off = ti_pipe3_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct of_device_id ti_pipe3_id_table[]; ++ ++static int ti_pipe3_get_clk(struct ti_pipe3 *phy) ++{ ++ struct clk *clk; ++ struct device *dev = phy->dev; ++ struct device_node *node = dev->of_node; ++ ++ phy->refclk = devm_clk_get(dev, "refclk"); ++ if (IS_ERR(phy->refclk)) { ++ dev_err(dev, "unable to get refclk\n"); ++ /* older DTBs have missing refclk in SATA PHY ++ * so don't bail out in case of SATA PHY. ++ */ ++ if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) ++ return PTR_ERR(phy->refclk); ++ } ++ ++ if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { ++ phy->wkupclk = devm_clk_get(dev, "wkupclk"); ++ if (IS_ERR(phy->wkupclk)) { ++ dev_err(dev, "unable to get wkupclk\n"); ++ return PTR_ERR(phy->wkupclk); ++ } ++ } else { ++ phy->wkupclk = ERR_PTR(-ENODEV); ++ } ++ ++ if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") || ++ phy->phy_power_syscon) { ++ phy->sys_clk = devm_clk_get(dev, "sysclk"); ++ if (IS_ERR(phy->sys_clk)) { ++ dev_err(dev, "unable to get sysclk\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { ++ clk = devm_clk_get(dev, "dpll_ref"); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "unable to get dpll ref clk\n"); ++ return PTR_ERR(clk); ++ } ++ clk_set_rate(clk, 1500000000); ++ ++ clk = devm_clk_get(dev, "dpll_ref_m2"); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "unable to get dpll ref m2 clk\n"); ++ return PTR_ERR(clk); ++ } ++ clk_set_rate(clk, 100000000); ++ ++ clk = devm_clk_get(dev, "phy-div"); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "unable to get phy-div clk\n"); ++ return PTR_ERR(clk); ++ } ++ clk_set_rate(clk, 100000000); ++ ++ phy->div_clk = devm_clk_get(dev, "div-clk"); ++ if (IS_ERR(phy->div_clk)) { ++ dev_err(dev, "unable to get div-clk\n"); ++ return PTR_ERR(phy->div_clk); ++ } ++ } else { ++ phy->div_clk = ERR_PTR(-ENODEV); ++ } ++ ++ return 0; ++} ++ ++static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) ++{ ++ struct device *dev = phy->dev; ++ struct device_node *node = dev->of_node; ++ struct device_node *control_node; ++ struct platform_device *control_pdev; ++ ++ phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node, ++ "syscon-phy-power"); ++ if (IS_ERR(phy->phy_power_syscon)) { ++ dev_dbg(dev, ++ "can't get syscon-phy-power, using control device\n"); ++ phy->phy_power_syscon = NULL; ++ } else { ++ if (of_property_read_u32_index(node, ++ "syscon-phy-power", 1, ++ &phy->power_reg)) { ++ dev_err(dev, "couldn't get power reg. offset\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (!phy->phy_power_syscon) { ++ control_node = of_parse_phandle(node, "ctrl-module", 0); ++ if (!control_node) { ++ dev_err(dev, "Failed to get control device phandle\n"); ++ return -EINVAL; ++ } ++ ++ control_pdev = of_find_device_by_node(control_node); ++ if (!control_pdev) { ++ dev_err(dev, "Failed to get control device\n"); ++ return -EINVAL; ++ } ++ ++ phy->control_dev = &control_pdev->dev; ++ } ++ ++ if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { ++ phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node, ++ "syscon-pcs"); ++ if (IS_ERR(phy->pcs_syscon)) { ++ dev_dbg(dev, ++ "can't get syscon-pcs, using omap control\n"); ++ phy->pcs_syscon = NULL; ++ } else { ++ if (of_property_read_u32_index(node, ++ "syscon-pcs", 1, ++ &phy->pcie_pcs_reg)) { ++ dev_err(dev, ++ "couldn't get pcie pcs reg. offset\n"); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { ++ phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node, ++ "syscon-pllreset"); ++ if (IS_ERR(phy->dpll_reset_syscon)) { ++ dev_info(dev, ++ "can't get syscon-pllreset, sata dpll won't idle\n"); ++ phy->dpll_reset_syscon = NULL; ++ } else { ++ if (of_property_read_u32_index(node, ++ "syscon-pllreset", 1, ++ &phy->dpll_reset_reg)) { ++ dev_err(dev, ++ "couldn't get pllreset reg. offset\n"); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) ++{ ++ struct resource *res; ++ const struct of_device_id *match; ++ struct device *dev = phy->dev; ++ struct device_node *node = dev->of_node; ++ struct platform_device *pdev = to_platform_device(dev); ++ ++ if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) ++ return 0; ++ ++ match = of_match_device(ti_pipe3_id_table, dev); ++ if (!match) ++ return -EINVAL; ++ ++ phy->dpll_map = (struct pipe3_dpll_map *)match->data; ++ if (!phy->dpll_map) { ++ dev_err(dev, "no DPLL data\n"); ++ return -EINVAL; ++ } ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, ++ "pll_ctrl"); ++ phy->pll_ctrl_base = devm_ioremap_resource(dev, res); ++ return PTR_ERR_OR_ZERO(phy->pll_ctrl_base); ++} ++ ++static int ti_pipe3_probe(struct platform_device *pdev) ++{ ++ struct ti_pipe3 *phy; ++ struct phy *generic_phy; ++ struct phy_provider *phy_provider; ++ struct device_node *node = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; ++ int ret; ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) ++ return -ENOMEM; ++ ++ phy->dev = dev; ++ ++ ret = ti_pipe3_get_pll_base(phy); ++ if (ret) ++ return ret; ++ ++ ret = ti_pipe3_get_sysctrl(phy); ++ if (ret) ++ return ret; ++ ++ ret = ti_pipe3_get_clk(phy); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, phy); ++ pm_runtime_enable(dev); ++ ++ /* ++ * Prevent auto-disable of refclk for SATA PHY due to Errata i783 ++ */ ++ if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { ++ if (!IS_ERR(phy->refclk)) { ++ clk_prepare_enable(phy->refclk); ++ phy->sata_refclk_enabled = true; ++ } ++ } ++ ++ generic_phy = devm_phy_create(dev, NULL, &ops); ++ if (IS_ERR(generic_phy)) ++ return PTR_ERR(generic_phy); ++ ++ phy_set_drvdata(generic_phy, phy); ++ ++ ti_pipe3_power_off(generic_phy); ++ ++ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ return PTR_ERR_OR_ZERO(phy_provider); ++} ++ ++static int ti_pipe3_remove(struct platform_device *pdev) ++{ ++ pm_runtime_disable(&pdev->dev); ++ ++ return 0; ++} ++ ++static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy) ++{ ++ int ret = 0; ++ ++ if (!IS_ERR(phy->refclk)) { ++ ret = clk_prepare_enable(phy->refclk); ++ if (ret) { ++ dev_err(phy->dev, "Failed to enable refclk %d\n", ret); ++ return ret; ++ } ++ } ++ ++ if (!IS_ERR(phy->wkupclk)) { ++ ret = clk_prepare_enable(phy->wkupclk); ++ if (ret) { ++ dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); ++ goto disable_refclk; ++ } ++ } ++ ++ if (!IS_ERR(phy->div_clk)) { ++ ret = clk_prepare_enable(phy->div_clk); ++ if (ret) { ++ dev_err(phy->dev, "Failed to enable div_clk %d\n", ret); ++ goto disable_wkupclk; ++ } ++ } ++ ++ return 0; ++ ++disable_wkupclk: ++ if (!IS_ERR(phy->wkupclk)) ++ clk_disable_unprepare(phy->wkupclk); ++ ++disable_refclk: ++ if (!IS_ERR(phy->refclk)) ++ clk_disable_unprepare(phy->refclk); ++ ++ return ret; ++} ++ ++static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) ++{ ++ if (!IS_ERR(phy->wkupclk)) ++ clk_disable_unprepare(phy->wkupclk); ++ if (!IS_ERR(phy->refclk)) { ++ clk_disable_unprepare(phy->refclk); ++ /* ++ * SATA refclk needs an additional disable as we left it ++ * on in probe to avoid Errata i783 ++ */ ++ if (phy->sata_refclk_enabled) { ++ clk_disable_unprepare(phy->refclk); ++ phy->sata_refclk_enabled = false; ++ } ++ } ++ ++ if (!IS_ERR(phy->div_clk)) ++ clk_disable_unprepare(phy->div_clk); ++} ++ ++static const struct of_device_id ti_pipe3_id_table[] = { ++ { ++ .compatible = "ti,phy-usb3", ++ .data = dpll_map_usb, ++ }, ++ { ++ .compatible = "ti,omap-usb3", ++ .data = dpll_map_usb, ++ }, ++ { ++ .compatible = "ti,phy-pipe3-sata", ++ .data = dpll_map_sata, ++ }, ++ { ++ .compatible = "ti,phy-pipe3-pcie", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); ++ ++static struct platform_driver ti_pipe3_driver = { ++ .probe = ti_pipe3_probe, ++ .remove = ti_pipe3_remove, ++ .driver = { ++ .name = "ti-pipe3", ++ .of_match_table = ti_pipe3_id_table, ++ }, ++}; ++ ++module_platform_driver(ti_pipe3_driver); ++ ++MODULE_ALIAS("platform:ti_pipe3"); ++MODULE_AUTHOR("Texas Instruments Inc."); ++MODULE_DESCRIPTION("TI PIPE3 phy driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c +new file mode 100644 +index 000000000000..bb3fb031c478 +--- /dev/null ++++ b/drivers/phy/ti/phy-tusb1210.c +@@ -0,0 +1,146 @@ ++/** ++ * tusb1210.c - TUSB1210 USB ULPI PHY driver ++ * ++ * Copyright (C) 2015 Intel Corporation ++ * ++ * Author: Heikki Krogerus ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++ ++#define TUSB1210_VENDOR_SPECIFIC2 0x80 ++#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 ++#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 ++#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 ++ ++struct tusb1210 { ++ struct ulpi *ulpi; ++ struct phy *phy; ++ struct gpio_desc *gpio_reset; ++ struct gpio_desc *gpio_cs; ++ u8 vendor_specific2; ++}; ++ ++static int tusb1210_power_on(struct phy *phy) ++{ ++ struct tusb1210 *tusb = phy_get_drvdata(phy); ++ ++ gpiod_set_value_cansleep(tusb->gpio_reset, 1); ++ gpiod_set_value_cansleep(tusb->gpio_cs, 1); ++ ++ /* Restore the optional eye diagram optimization value */ ++ if (tusb->vendor_specific2) ++ ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, ++ tusb->vendor_specific2); ++ ++ return 0; ++} ++ ++static int tusb1210_power_off(struct phy *phy) ++{ ++ struct tusb1210 *tusb = phy_get_drvdata(phy); ++ ++ gpiod_set_value_cansleep(tusb->gpio_reset, 0); ++ gpiod_set_value_cansleep(tusb->gpio_cs, 0); ++ ++ return 0; ++} ++ ++static const struct phy_ops phy_ops = { ++ .power_on = tusb1210_power_on, ++ .power_off = tusb1210_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static int tusb1210_probe(struct ulpi *ulpi) ++{ ++ struct tusb1210 *tusb; ++ u8 val, reg; ++ ++ tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); ++ if (!tusb) ++ return -ENOMEM; ++ ++ tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(tusb->gpio_reset)) ++ return PTR_ERR(tusb->gpio_reset); ++ ++ gpiod_set_value_cansleep(tusb->gpio_reset, 1); ++ ++ tusb->gpio_cs = devm_gpiod_get_optional(&ulpi->dev, "cs", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(tusb->gpio_cs)) ++ return PTR_ERR(tusb->gpio_cs); ++ ++ gpiod_set_value_cansleep(tusb->gpio_cs, 1); ++ ++ /* ++ * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye ++ * diagram optimization and DP/DM swap. ++ */ ++ ++ /* High speed output drive strength configuration */ ++ device_property_read_u8(&ulpi->dev, "ihstx", &val); ++ reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; ++ ++ /* High speed output impedance configuration */ ++ device_property_read_u8(&ulpi->dev, "zhsdrv", &val); ++ reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; ++ ++ /* DP/DM swap control */ ++ device_property_read_u8(&ulpi->dev, "datapolarity", &val); ++ reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; ++ ++ if (reg) { ++ ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); ++ tusb->vendor_specific2 = reg; ++ } ++ ++ tusb->phy = ulpi_phy_create(ulpi, &phy_ops); ++ if (IS_ERR(tusb->phy)) ++ return PTR_ERR(tusb->phy); ++ ++ tusb->ulpi = ulpi; ++ ++ phy_set_drvdata(tusb->phy, tusb); ++ ulpi_set_drvdata(ulpi, tusb); ++ return 0; ++} ++ ++static void tusb1210_remove(struct ulpi *ulpi) ++{ ++ struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); ++ ++ ulpi_phy_destroy(ulpi, tusb->phy); ++} ++ ++#define TI_VENDOR_ID 0x0451 ++ ++static const struct ulpi_device_id tusb1210_ulpi_id[] = { ++ { TI_VENDOR_ID, 0x1507, }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); ++ ++static struct ulpi_driver tusb1210_driver = { ++ .id_table = tusb1210_ulpi_id, ++ .probe = tusb1210_probe, ++ .remove = tusb1210_remove, ++ .driver = { ++ .name = "tusb1210", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_ulpi_driver(tusb1210_driver); ++ ++MODULE_AUTHOR("Intel Corporation"); ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); +diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c +new file mode 100644 +index 000000000000..2990b3965460 +--- /dev/null ++++ b/drivers/phy/ti/phy-twl4030-usb.c +@@ -0,0 +1,839 @@ ++/* ++ * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller ++ * ++ * Copyright (C) 2004-2007 Texas Instruments ++ * Copyright (C) 2008 Nokia Corporation ++ * Contact: Felipe Balbi ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Current status: ++ * - HS USB ULPI mode works. ++ * - 3-pin mode support may be added in future. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Register defines */ ++ ++#define MCPC_CTRL 0x30 ++#define MCPC_CTRL_RTSOL (1 << 7) ++#define MCPC_CTRL_EXTSWR (1 << 6) ++#define MCPC_CTRL_EXTSWC (1 << 5) ++#define MCPC_CTRL_VOICESW (1 << 4) ++#define MCPC_CTRL_OUT64K (1 << 3) ++#define MCPC_CTRL_RTSCTSSW (1 << 2) ++#define MCPC_CTRL_HS_UART (1 << 0) ++ ++#define MCPC_IO_CTRL 0x33 ++#define MCPC_IO_CTRL_MICBIASEN (1 << 5) ++#define MCPC_IO_CTRL_CTS_NPU (1 << 4) ++#define MCPC_IO_CTRL_RXD_PU (1 << 3) ++#define MCPC_IO_CTRL_TXDTYP (1 << 2) ++#define MCPC_IO_CTRL_CTSTYP (1 << 1) ++#define MCPC_IO_CTRL_RTSTYP (1 << 0) ++ ++#define MCPC_CTRL2 0x36 ++#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) ++ ++#define OTHER_FUNC_CTRL 0x80 ++#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) ++#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) ++ ++#define OTHER_IFC_CTRL 0x83 ++#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) ++#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) ++#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) ++#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) ++#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) ++#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) ++ ++#define OTHER_INT_EN_RISE 0x86 ++#define OTHER_INT_EN_FALL 0x89 ++#define OTHER_INT_STS 0x8C ++#define OTHER_INT_LATCH 0x8D ++#define OTHER_INT_VB_SESS_VLD (1 << 7) ++#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ ++#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ ++#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ ++#define OTHER_INT_MANU (1 << 1) ++#define OTHER_INT_ABNORMAL_STRESS (1 << 0) ++ ++#define ID_STATUS 0x96 ++#define ID_RES_FLOAT (1 << 4) ++#define ID_RES_440K (1 << 3) ++#define ID_RES_200K (1 << 2) ++#define ID_RES_102K (1 << 1) ++#define ID_RES_GND (1 << 0) ++ ++#define POWER_CTRL 0xAC ++#define POWER_CTRL_OTG_ENAB (1 << 5) ++ ++#define OTHER_IFC_CTRL2 0xAF ++#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) ++#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) ++#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) ++#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ ++#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) ++#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) ++ ++#define REG_CTRL_EN 0xB2 ++#define REG_CTRL_ERROR 0xB5 ++#define ULPI_I2C_CONFLICT_INTEN (1 << 0) ++ ++#define OTHER_FUNC_CTRL2 0xB8 ++#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) ++ ++/* following registers do not have separate _clr and _set registers */ ++#define VBUS_DEBOUNCE 0xC0 ++#define ID_DEBOUNCE 0xC1 ++#define VBAT_TIMER 0xD3 ++#define PHY_PWR_CTRL 0xFD ++#define PHY_PWR_PHYPWD (1 << 0) ++#define PHY_CLK_CTRL 0xFE ++#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) ++#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) ++#define REQ_PHY_DPLL_CLK (1 << 0) ++#define PHY_CLK_CTRL_STS 0xFF ++#define PHY_DPLL_CLK (1 << 0) ++ ++/* In module TWL_MODULE_PM_MASTER */ ++#define STS_HW_CONDITIONS 0x0F ++ ++/* In module TWL_MODULE_PM_RECEIVER */ ++#define VUSB_DEDICATED1 0x7D ++#define VUSB_DEDICATED2 0x7E ++#define VUSB1V5_DEV_GRP 0x71 ++#define VUSB1V5_TYPE 0x72 ++#define VUSB1V5_REMAP 0x73 ++#define VUSB1V8_DEV_GRP 0x74 ++#define VUSB1V8_TYPE 0x75 ++#define VUSB1V8_REMAP 0x76 ++#define VUSB3V1_DEV_GRP 0x77 ++#define VUSB3V1_TYPE 0x78 ++#define VUSB3V1_REMAP 0x79 ++ ++/* In module TWL4030_MODULE_INTBR */ ++#define PMBR1 0x0D ++#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) ++ ++/* ++ * If VBUS is valid or ID is ground, then we know a ++ * cable is present and we need to be runtime-enabled ++ */ ++static inline bool cable_present(enum musb_vbus_id_status stat) ++{ ++ return stat == MUSB_VBUS_VALID || ++ stat == MUSB_ID_GROUND; ++} ++ ++struct twl4030_usb { ++ struct usb_phy phy; ++ struct device *dev; ++ ++ /* TWL4030 internal USB regulator supplies */ ++ struct regulator *usb1v5; ++ struct regulator *usb1v8; ++ struct regulator *usb3v1; ++ ++ /* for vbus reporting with irqs disabled */ ++ struct mutex lock; ++ ++ /* pin configuration */ ++ enum twl4030_usb_mode usb_mode; ++ ++ int irq; ++ enum musb_vbus_id_status linkstat; ++ bool vbus_supplied; ++ bool musb_mailbox_pending; ++ ++ struct delayed_work id_workaround_work; ++}; ++ ++/* internal define on top of container_of */ ++#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, ++ u8 module, u8 data, u8 address) ++{ ++ u8 check; ++ ++ if ((twl_i2c_write_u8(module, data, address) >= 0) && ++ (twl_i2c_read_u8(module, &check, address) >= 0) && ++ (check == data)) ++ return 0; ++ dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", ++ 1, module, address, check, data); ++ ++ /* Failed once: Try again */ ++ if ((twl_i2c_write_u8(module, data, address) >= 0) && ++ (twl_i2c_read_u8(module, &check, address) >= 0) && ++ (check == data)) ++ return 0; ++ dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", ++ 2, module, address, check, data); ++ ++ /* Failed again: Return error */ ++ return -EBUSY; ++} ++ ++#define twl4030_usb_write_verify(twl, address, data) \ ++ twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) ++ ++static inline int twl4030_usb_write(struct twl4030_usb *twl, ++ u8 address, u8 data) ++{ ++ int ret = 0; ++ ++ ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); ++ if (ret < 0) ++ dev_dbg(twl->dev, ++ "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); ++ return ret; ++} ++ ++static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) ++{ ++ u8 data; ++ int ret = 0; ++ ++ ret = twl_i2c_read_u8(module, &data, address); ++ if (ret >= 0) ++ ret = data; ++ else ++ dev_dbg(twl->dev, ++ "TWL4030:readb[0x%x,0x%x] Error %d\n", ++ module, address, ret); ++ ++ return ret; ++} ++ ++static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) ++{ ++ return twl4030_readb(twl, TWL_MODULE_USB, address); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline int ++twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) ++{ ++ return twl4030_usb_write(twl, ULPI_SET(reg), bits); ++} ++ ++static inline int ++twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) ++{ ++ return twl4030_usb_write(twl, ULPI_CLR(reg), bits); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static bool twl4030_is_driving_vbus(struct twl4030_usb *twl) ++{ ++ int ret; ++ ++ ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS); ++ if (ret < 0 || !(ret & PHY_DPLL_CLK)) ++ /* ++ * if clocks are off, registers are not updated, ++ * but we can assume we don't drive VBUS in this case ++ */ ++ return false; ++ ++ ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); ++ if (ret < 0) ++ return false; ++ ++ return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false; ++} ++ ++static enum musb_vbus_id_status ++ twl4030_usb_linkstat(struct twl4030_usb *twl) ++{ ++ int status; ++ enum musb_vbus_id_status linkstat = MUSB_UNKNOWN; ++ ++ twl->vbus_supplied = false; ++ ++ /* ++ * For ID/VBUS sensing, see manual section 15.4.8 ... ++ * except when using only battery backup power, two ++ * comparators produce VBUS_PRES and ID_PRES signals, ++ * which don't match docs elsewhere. But ... BIT(7) ++ * and BIT(2) of STS_HW_CONDITIONS, respectively, do ++ * seem to match up. If either is true the USB_PRES ++ * signal is active, the OTG module is activated, and ++ * its interrupt may be raised (may wake the system). ++ */ ++ status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); ++ if (status < 0) ++ dev_err(twl->dev, "USB link status err %d\n", status); ++ else if (status & (BIT(7) | BIT(2))) { ++ if (status & BIT(7)) { ++ if (twl4030_is_driving_vbus(twl)) ++ status &= ~BIT(7); ++ else ++ twl->vbus_supplied = true; ++ } ++ ++ if (status & BIT(2)) ++ linkstat = MUSB_ID_GROUND; ++ else if (status & BIT(7)) ++ linkstat = MUSB_VBUS_VALID; ++ else ++ linkstat = MUSB_VBUS_OFF; ++ } else { ++ if (twl->linkstat != MUSB_UNKNOWN) ++ linkstat = MUSB_VBUS_OFF; ++ } ++ ++ kobject_uevent(&twl->dev->kobj, linkstat == MUSB_VBUS_VALID ++ ? KOBJ_ONLINE : KOBJ_OFFLINE); ++ ++ dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", ++ status, status, linkstat); ++ ++ /* REVISIT this assumes host and peripheral controllers ++ * are registered, and that both are active... ++ */ ++ ++ return linkstat; ++} ++ ++static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) ++{ ++ twl->usb_mode = mode; ++ ++ switch (mode) { ++ case T2_USB_MODE_ULPI: ++ twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, ++ ULPI_IFC_CTRL_CARKITMODE); ++ twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); ++ twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, ++ ULPI_FUNC_CTRL_XCVRSEL_MASK | ++ ULPI_FUNC_CTRL_OPMODE_MASK); ++ break; ++ case -1: ++ /* FIXME: power on defaults */ ++ break; ++ default: ++ dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", ++ mode); ++ break; ++ } ++} ++ ++static void twl4030_i2c_access(struct twl4030_usb *twl, int on) ++{ ++ unsigned long timeout; ++ int val = twl4030_usb_read(twl, PHY_CLK_CTRL); ++ ++ if (val >= 0) { ++ if (on) { ++ /* enable DPLL to access PHY registers over I2C */ ++ val |= REQ_PHY_DPLL_CLK; ++ WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, ++ (u8)val) < 0); ++ ++ timeout = jiffies + HZ; ++ while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & ++ PHY_DPLL_CLK) ++ && time_before(jiffies, timeout)) ++ udelay(10); ++ if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & ++ PHY_DPLL_CLK)) ++ dev_err(twl->dev, "Timeout setting T2 HSUSB " ++ "PHY DPLL clock\n"); ++ } else { ++ /* let ULPI control the DPLL clock */ ++ val &= ~REQ_PHY_DPLL_CLK; ++ WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, ++ (u8)val) < 0); ++ } ++ } ++} ++ ++static void __twl4030_phy_power(struct twl4030_usb *twl, int on) ++{ ++ u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); ++ ++ if (on) ++ pwr &= ~PHY_PWR_PHYPWD; ++ else ++ pwr |= PHY_PWR_PHYPWD; ++ ++ WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); ++} ++ ++static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev) ++{ ++ struct twl4030_usb *twl = dev_get_drvdata(dev); ++ ++ dev_dbg(twl->dev, "%s\n", __func__); ++ ++ __twl4030_phy_power(twl, 0); ++ regulator_disable(twl->usb1v5); ++ regulator_disable(twl->usb1v8); ++ regulator_disable(twl->usb3v1); ++ ++ return 0; ++} ++ ++static int __maybe_unused twl4030_usb_runtime_resume(struct device *dev) ++{ ++ struct twl4030_usb *twl = dev_get_drvdata(dev); ++ int res; ++ ++ dev_dbg(twl->dev, "%s\n", __func__); ++ ++ res = regulator_enable(twl->usb3v1); ++ if (res) ++ dev_err(twl->dev, "Failed to enable usb3v1\n"); ++ ++ res = regulator_enable(twl->usb1v8); ++ if (res) ++ dev_err(twl->dev, "Failed to enable usb1v8\n"); ++ ++ /* ++ * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP ++ * in twl4030) resets the VUSB_DEDICATED2 register. This reset ++ * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to ++ * SLEEP. We work around this by clearing the bit after usv3v1 ++ * is re-activated. This ensures that VUSB3V1 is really active. ++ */ ++ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); ++ ++ res = regulator_enable(twl->usb1v5); ++ if (res) ++ dev_err(twl->dev, "Failed to enable usb1v5\n"); ++ ++ __twl4030_phy_power(twl, 1); ++ twl4030_usb_write(twl, PHY_CLK_CTRL, ++ twl4030_usb_read(twl, PHY_CLK_CTRL) | ++ (PHY_CLK_CTRL_CLOCKGATING_EN | ++ PHY_CLK_CTRL_CLK32K_EN)); ++ ++ twl4030_i2c_access(twl, 1); ++ twl4030_usb_set_mode(twl, twl->usb_mode); ++ if (twl->usb_mode == T2_USB_MODE_ULPI) ++ twl4030_i2c_access(twl, 0); ++ /* ++ * According to the TPS65950 TRM, there has to be at least 50ms ++ * delay between setting POWER_CTRL_OTG_ENAB and enabling charging ++ * so wait here so that a fully enabled phy can be expected after ++ * resume ++ */ ++ msleep(50); ++ return 0; ++} ++ ++static int twl4030_phy_power_off(struct phy *phy) ++{ ++ struct twl4030_usb *twl = phy_get_drvdata(phy); ++ ++ dev_dbg(twl->dev, "%s\n", __func__); ++ ++ return 0; ++} ++ ++static int twl4030_phy_power_on(struct phy *phy) ++{ ++ struct twl4030_usb *twl = phy_get_drvdata(phy); ++ ++ dev_dbg(twl->dev, "%s\n", __func__); ++ pm_runtime_get_sync(twl->dev); ++ schedule_delayed_work(&twl->id_workaround_work, HZ); ++ pm_runtime_mark_last_busy(twl->dev); ++ pm_runtime_put_autosuspend(twl->dev); ++ ++ return 0; ++} ++ ++static int twl4030_usb_ldo_init(struct twl4030_usb *twl) ++{ ++ /* Enable writing to power configuration registers */ ++ twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, ++ TWL4030_PM_MASTER_PROTECT_KEY); ++ ++ twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, ++ TWL4030_PM_MASTER_PROTECT_KEY); ++ ++ /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ ++ /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ ++ ++ /* input to VUSB3V1 LDO is from VBAT, not VBUS */ ++ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); ++ ++ /* Initialize 3.1V regulator */ ++ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); ++ ++ twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1"); ++ if (IS_ERR(twl->usb3v1)) ++ return -ENODEV; ++ ++ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); ++ ++ /* Initialize 1.5V regulator */ ++ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); ++ ++ twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5"); ++ if (IS_ERR(twl->usb1v5)) ++ return -ENODEV; ++ ++ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); ++ ++ /* Initialize 1.8V regulator */ ++ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); ++ ++ twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8"); ++ if (IS_ERR(twl->usb1v8)) ++ return -ENODEV; ++ ++ twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); ++ ++ /* disable access to power configuration registers */ ++ twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, ++ TWL4030_PM_MASTER_PROTECT_KEY); ++ ++ return 0; ++} ++ ++static ssize_t twl4030_usb_vbus_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct twl4030_usb *twl = dev_get_drvdata(dev); ++ int ret = -EINVAL; ++ ++ mutex_lock(&twl->lock); ++ ret = sprintf(buf, "%s\n", ++ twl->vbus_supplied ? "on" : "off"); ++ mutex_unlock(&twl->lock); ++ ++ return ret; ++} ++static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); ++ ++static irqreturn_t twl4030_usb_irq(int irq, void *_twl) ++{ ++ struct twl4030_usb *twl = _twl; ++ enum musb_vbus_id_status status; ++ bool status_changed = false; ++ int err; ++ ++ status = twl4030_usb_linkstat(twl); ++ ++ mutex_lock(&twl->lock); ++ if (status >= 0 && status != twl->linkstat) { ++ status_changed = ++ cable_present(twl->linkstat) != ++ cable_present(status); ++ twl->linkstat = status; ++ } ++ mutex_unlock(&twl->lock); ++ ++ if (status_changed) { ++ /* FIXME add a set_power() method so that B-devices can ++ * configure the charger appropriately. It's not always ++ * correct to consume VBUS power, and how much current to ++ * consume is a function of the USB configuration chosen ++ * by the host. ++ * ++ * REVISIT usb_gadget_vbus_connect(...) as needed, ditto ++ * its disconnect() sibling, when changing to/from the ++ * USB_LINK_VBUS state. musb_hdrc won't care until it ++ * starts to handle softconnect right. ++ */ ++ if (cable_present(status)) { ++ pm_runtime_get_sync(twl->dev); ++ } else { ++ pm_runtime_mark_last_busy(twl->dev); ++ pm_runtime_put_autosuspend(twl->dev); ++ } ++ twl->musb_mailbox_pending = true; ++ } ++ if (twl->musb_mailbox_pending) { ++ err = musb_mailbox(status); ++ if (!err) ++ twl->musb_mailbox_pending = false; ++ } ++ ++ /* don't schedule during sleep - irq works right then */ ++ if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) { ++ cancel_delayed_work(&twl->id_workaround_work); ++ schedule_delayed_work(&twl->id_workaround_work, HZ); ++ } ++ ++ if (irq) ++ sysfs_notify(&twl->dev->kobj, NULL, "vbus"); ++ ++ return IRQ_HANDLED; ++} ++ ++static void twl4030_id_workaround_work(struct work_struct *work) ++{ ++ struct twl4030_usb *twl = container_of(work, struct twl4030_usb, ++ id_workaround_work.work); ++ ++ twl4030_usb_irq(0, twl); ++} ++ ++static int twl4030_phy_init(struct phy *phy) ++{ ++ struct twl4030_usb *twl = phy_get_drvdata(phy); ++ ++ pm_runtime_get_sync(twl->dev); ++ twl->linkstat = MUSB_UNKNOWN; ++ schedule_delayed_work(&twl->id_workaround_work, HZ); ++ pm_runtime_mark_last_busy(twl->dev); ++ pm_runtime_put_autosuspend(twl->dev); ++ ++ return 0; ++} ++ ++static int twl4030_set_peripheral(struct usb_otg *otg, ++ struct usb_gadget *gadget) ++{ ++ if (!otg) ++ return -ENODEV; ++ ++ otg->gadget = gadget; ++ if (!gadget) ++ otg->state = OTG_STATE_UNDEFINED; ++ ++ return 0; ++} ++ ++static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) ++{ ++ if (!otg) ++ return -ENODEV; ++ ++ otg->host = host; ++ if (!host) ++ otg->state = OTG_STATE_UNDEFINED; ++ ++ return 0; ++} ++ ++static const struct phy_ops ops = { ++ .init = twl4030_phy_init, ++ .power_on = twl4030_phy_power_on, ++ .power_off = twl4030_phy_power_off, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct dev_pm_ops twl4030_usb_pm_ops = { ++ SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend, ++ twl4030_usb_runtime_resume, NULL) ++}; ++ ++static int twl4030_usb_probe(struct platform_device *pdev) ++{ ++ struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev); ++ struct twl4030_usb *twl; ++ struct phy *phy; ++ int status, err; ++ struct usb_otg *otg; ++ struct device_node *np = pdev->dev.of_node; ++ struct phy_provider *phy_provider; ++ ++ twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); ++ if (!twl) ++ return -ENOMEM; ++ ++ if (np) ++ of_property_read_u32(np, "usb_mode", ++ (enum twl4030_usb_mode *)&twl->usb_mode); ++ else if (pdata) { ++ twl->usb_mode = pdata->usb_mode; ++ } else { ++ dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); ++ return -EINVAL; ++ } ++ ++ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); ++ if (!otg) ++ return -ENOMEM; ++ ++ twl->dev = &pdev->dev; ++ twl->irq = platform_get_irq(pdev, 0); ++ twl->vbus_supplied = false; ++ twl->linkstat = MUSB_UNKNOWN; ++ twl->musb_mailbox_pending = false; ++ ++ twl->phy.dev = twl->dev; ++ twl->phy.label = "twl4030"; ++ twl->phy.otg = otg; ++ twl->phy.type = USB_PHY_TYPE_USB2; ++ ++ otg->usb_phy = &twl->phy; ++ otg->set_host = twl4030_set_host; ++ otg->set_peripheral = twl4030_set_peripheral; ++ ++ phy = devm_phy_create(twl->dev, NULL, &ops); ++ if (IS_ERR(phy)) { ++ dev_dbg(&pdev->dev, "Failed to create PHY\n"); ++ return PTR_ERR(phy); ++ } ++ ++ phy_set_drvdata(phy, twl); ++ ++ phy_provider = devm_of_phy_provider_register(twl->dev, ++ of_phy_simple_xlate); ++ if (IS_ERR(phy_provider)) ++ return PTR_ERR(phy_provider); ++ ++ /* init mutex for workqueue */ ++ mutex_init(&twl->lock); ++ ++ INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); ++ ++ err = twl4030_usb_ldo_init(twl); ++ if (err) { ++ dev_err(&pdev->dev, "ldo init failed\n"); ++ return err; ++ } ++ usb_add_phy_dev(&twl->phy); ++ ++ platform_set_drvdata(pdev, twl); ++ if (device_create_file(&pdev->dev, &dev_attr_vbus)) ++ dev_warn(&pdev->dev, "could not create sysfs file\n"); ++ ++ ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); ++ ++ pm_runtime_use_autosuspend(&pdev->dev); ++ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_sync(&pdev->dev); ++ ++ /* Our job is to use irqs and status from the power module ++ * to keep the transceiver disabled when nothing's connected. ++ * ++ * FIXME we actually shouldn't start enabling it until the ++ * USB controller drivers have said they're ready, by calling ++ * set_host() and/or set_peripheral() ... OTG_capable boards ++ * need both handles, otherwise just one suffices. ++ */ ++ status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, ++ twl4030_usb_irq, IRQF_TRIGGER_FALLING | ++ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); ++ if (status < 0) { ++ dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", ++ twl->irq, status); ++ return status; ++ } ++ ++ if (pdata) ++ err = phy_create_lookup(phy, "usb", "musb-hdrc.0"); ++ if (err) ++ return err; ++ ++ pm_runtime_mark_last_busy(&pdev->dev); ++ pm_runtime_put_autosuspend(twl->dev); ++ ++ dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); ++ return 0; ++} ++ ++static int twl4030_usb_remove(struct platform_device *pdev) ++{ ++ struct twl4030_usb *twl = platform_get_drvdata(pdev); ++ int val; ++ ++ usb_remove_phy(&twl->phy); ++ pm_runtime_get_sync(twl->dev); ++ cancel_delayed_work(&twl->id_workaround_work); ++ device_remove_file(twl->dev, &dev_attr_vbus); ++ ++ /* set transceiver mode to power on defaults */ ++ twl4030_usb_set_mode(twl, -1); ++ ++ /* idle ulpi before powering off */ ++ if (cable_present(twl->linkstat)) ++ pm_runtime_put_noidle(twl->dev); ++ pm_runtime_mark_last_busy(twl->dev); ++ pm_runtime_dont_use_autosuspend(&pdev->dev); ++ pm_runtime_put_sync(twl->dev); ++ pm_runtime_disable(twl->dev); ++ ++ /* autogate 60MHz ULPI clock, ++ * clear dpll clock request for i2c access, ++ * disable 32KHz ++ */ ++ val = twl4030_usb_read(twl, PHY_CLK_CTRL); ++ if (val >= 0) { ++ val |= PHY_CLK_CTRL_CLOCKGATING_EN; ++ val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); ++ twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); ++ } ++ ++ /* disable complete OTG block */ ++ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_OF ++static const struct of_device_id twl4030_usb_id_table[] = { ++ { .compatible = "ti,twl4030-usb" }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); ++#endif ++ ++static struct platform_driver twl4030_usb_driver = { ++ .probe = twl4030_usb_probe, ++ .remove = twl4030_usb_remove, ++ .driver = { ++ .name = "twl4030_usb", ++ .pm = &twl4030_usb_pm_ops, ++ .of_match_table = of_match_ptr(twl4030_usb_id_table), ++ }, ++}; ++ ++static int __init twl4030_usb_init(void) ++{ ++ return platform_driver_register(&twl4030_usb_driver); ++} ++subsys_initcall(twl4030_usb_init); ++ ++static void __exit twl4030_usb_exit(void) ++{ ++ platform_driver_unregister(&twl4030_usb_driver); ++} ++module_exit(twl4030_usb_exit); ++ ++MODULE_ALIAS("platform:twl4030_usb"); ++MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); ++MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); ++MODULE_LICENSE("GPL"); +-- +2.11.0 + diff --git a/patches.drivers/0032-phy-rockchip-inno-usb2-add-a-delay-after-phy-resume.patch b/patches.drivers/0032-phy-rockchip-inno-usb2-add-a-delay-after-phy-resume.patch new file mode 100644 index 0000000..6c8f9ee --- /dev/null +++ b/patches.drivers/0032-phy-rockchip-inno-usb2-add-a-delay-after-phy-resume.patch @@ -0,0 +1,37 @@ +From a8a51b04d084a056bde6d84a988487ad10df9e4e Mon Sep 17 00:00:00 2001 +From: William Wu +Date: Fri, 2 Jun 2017 11:20:23 +0800 +Subject: [PATCH 32/86] phy: rockchip-inno-usb2: add a delay after phy resume + +Git-commit: fbbe98cd44508def1c6b7f98d5dc676d23bc9031 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +When resume phy, it need about 1.5 ~ 2ms to wait for +utmi_clk which used for USB controller to become stable. + +Signed-off-by: William Wu +Signed-off-by: Frank Wang +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 8efe78a49916..f12dc8db5230 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -463,6 +463,9 @@ static int rockchip_usb2phy_power_on(struct phy *phy) + if (ret) + return ret; + ++ /* waiting for the utmi_clk to become stable */ ++ usleep_range(1500, 2000); ++ + rport->suspended = false; + return 0; + } +-- +2.11.0 + diff --git a/patches.drivers/0033-phy-rockchip-inno-usb2-increase-otg-sm-work-first-sc.patch b/patches.drivers/0033-phy-rockchip-inno-usb2-increase-otg-sm-work-first-sc.patch new file mode 100644 index 0000000..439f3c4 --- /dev/null +++ b/patches.drivers/0033-phy-rockchip-inno-usb2-increase-otg-sm-work-first-sc.patch @@ -0,0 +1,60 @@ +From 594d6e7a9f529110fadd72b89edb87518e5856bd Mon Sep 17 00:00:00 2001 +From: William Wu +Date: Fri, 2 Jun 2017 11:20:24 +0800 +Subject: [PATCH 33/86] phy: rockchip-inno-usb2: increase otg sm work first + schedule time + +Git-commit: 5a74a8b74233f0e3c018d4e1c2a52e9393605f47 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +In rockchip-inno-usb2 phy driver, we use otg_sm_work to +dynamically manage power consumption for phy otg-port. +If the otg-port works as peripheral mode and does not +communicate with usb host, we will suspend phy. + +But once suspend phy, the phy no longer has any internal +clock running, include the utmi_clk which supplied for +usb controller. So if we suspend phy before usb controller +init, it will cause usb controller fail to initialize. + +Specifically, without this patch, the observed order is: + 1. unplug usb cable + 2. start system, do dwc2 controller probe + 3. dwc2_lowlevel_hw_enable() + - phy_init() + - rockchip_usb2phy_init() + - schedule otg_sm_work after 2s + put phy in suspend, and close utmi_clk + 4. dwc2_hsotg_udc_start() - fail to initialize the usb core + +Generally, dwc2_hsotg_udc_start() can be called within 5s +after start system on Rockchip platform, so we increase the +the first schedule delay time to 6s for otg_sm_work afer usb +controller calls phy_init(), this can make sure that the usb +controller completes initialization before phy enter suspend. + +Signed-off-by: William Wu +Signed-off-by: Frank Wang +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index f12dc8db5230..d6e459d7094a 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -421,7 +421,7 @@ static int rockchip_usb2phy_init(struct phy *phy) + goto out; + + schedule_delayed_work(&rport->otg_sm_work, +- OTG_SCHEDULE_DELAY); ++ OTG_SCHEDULE_DELAY * 3); + } else { + /* If OTG works in host only mode, do nothing. */ + dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); +-- +2.11.0 + diff --git a/patches.drivers/0034-phy-rockchip-inno-usb2-add-one-phy-comprises-with-tw.patch b/patches.drivers/0034-phy-rockchip-inno-usb2-add-one-phy-comprises-with-tw.patch new file mode 100644 index 0000000..9b68119 --- /dev/null +++ b/patches.drivers/0034-phy-rockchip-inno-usb2-add-one-phy-comprises-with-tw.patch @@ -0,0 +1,63 @@ +From eeb5f169fa3c91a88012087d8d33f8fd06353ddd Mon Sep 17 00:00:00 2001 +From: William Wu +Date: Fri, 2 Jun 2017 11:20:25 +0800 +Subject: [PATCH 34/86] phy: rockchip-inno-usb2: add one phy comprises with two + host-ports support + +Git-commit: 9632781122e5f7adfaf29aeb4387d7b354556593 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +At the current rockchip-inno-usb2 phy driver framework, it can +only support usb2-phy which comprises with one otg-port and one +host-port. + +However, some Rockchip SoCs' (e.g RK3228, RK3229) usb2-phy comprises +with two host-ports, so we use index of otg id for one host-port +configuration, and make it work the same as otg-port host mode. + +Signed-off-by: William Wu +Signed-off-by: Frank Wang +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index d6e459d7094a..d026b4cf7523 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -406,7 +406,8 @@ static int rockchip_usb2phy_init(struct phy *phy) + mutex_lock(&rport->mutex); + + if (rport->port_id == USB2PHY_PORT_OTG) { +- if (rport->mode != USB_DR_MODE_HOST) { ++ if (rport->mode != USB_DR_MODE_HOST && ++ rport->mode != USB_DR_MODE_UNKNOWN) { + /* clear bvalid status and enable bvalid detect irq */ + ret = property_enable(rphy, + &rport->port_cfg->bvalid_det_clr, +@@ -496,7 +497,8 @@ static int rockchip_usb2phy_exit(struct phy *phy) + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); + + if (rport->port_id == USB2PHY_PORT_OTG && +- rport->mode != USB_DR_MODE_HOST) { ++ rport->mode != USB_DR_MODE_HOST && ++ rport->mode != USB_DR_MODE_UNKNOWN) { + cancel_delayed_work_sync(&rport->otg_sm_work); + cancel_delayed_work_sync(&rport->chg_work); + } else if (rport->port_id == USB2PHY_PORT_HOST) +@@ -973,7 +975,8 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, + mutex_init(&rport->mutex); + + rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); +- if (rport->mode == USB_DR_MODE_HOST) { ++ if (rport->mode == USB_DR_MODE_HOST || ++ rport->mode == USB_DR_MODE_UNKNOWN) { + ret = 0; + goto out; + } +-- +2.11.0 + diff --git a/patches.drivers/0035-phy-rockchip-inno-usb2-add-support-of-usb2-phy-for-r.patch b/patches.drivers/0035-phy-rockchip-inno-usb2-add-support-of-usb2-phy-for-r.patch new file mode 100644 index 0000000..b2acdc7 --- /dev/null +++ b/patches.drivers/0035-phy-rockchip-inno-usb2-add-support-of-usb2-phy-for-r.patch @@ -0,0 +1,113 @@ +From c7c6f4d2c0e2ea318ae24a92ed8aa71c8b8e2a5a Mon Sep 17 00:00:00 2001 +From: Frank Wang +Date: Fri, 2 Jun 2017 11:20:26 +0800 +Subject: [PATCH 35/86] phy: rockchip-inno-usb2: add support of usb2-phy for + rk3228 SoCs + +Git-commit: b59b1d390483e3ff3342d7b7cd0ec19e847a6142 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +This adds support usb2-phy for rk3228 SoCs and amend phy Documentation. + +Signed-off-by: Frank Wang +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + .../bindings/phy/phy-rockchip-inno-usb2.txt | 1 + + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 60 ++++++++++++++++++++++ + 2 files changed, 61 insertions(+) + +diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt +index e71a8d23f4a8..84d59b0db8df 100644 +--- a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt ++++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt +@@ -2,6 +2,7 @@ ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK + + Required properties (phy (parent) node): + - compatible : should be one of the listed compatibles: ++ * "rockchip,rk3228-usb2phy" + * "rockchip,rk3328-usb2phy" + * "rockchip,rk3366-usb2phy" + * "rockchip,rk3399-usb2phy" +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index d026b4cf7523..626883d9d176 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -1144,6 +1144,65 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) + return ret; + } + ++static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = { ++ { ++ .reg = 0x760, ++ .num_ports = 2, ++ .clkout_ctl = { 0x0768, 4, 4, 1, 0 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_OTG] = { ++ .phy_sus = { 0x0760, 15, 0, 0, 0x1d1 }, ++ .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, ++ .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, ++ .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, ++ .ls_det_en = { 0x0680, 2, 2, 0, 1 }, ++ .ls_det_st = { 0x0690, 2, 2, 0, 1 }, ++ .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, ++ .utmi_bvalid = { 0x0480, 4, 4, 0, 1 }, ++ .utmi_ls = { 0x0480, 3, 2, 0, 1 }, ++ }, ++ [USB2PHY_PORT_HOST] = { ++ .phy_sus = { 0x0764, 15, 0, 0, 0x1d1 }, ++ .ls_det_en = { 0x0680, 4, 4, 0, 1 }, ++ .ls_det_st = { 0x0690, 4, 4, 0, 1 }, ++ .ls_det_clr = { 0x06a0, 4, 4, 0, 1 } ++ } ++ }, ++ .chg_det = { ++ .opmode = { 0x0760, 3, 0, 5, 1 }, ++ .cp_det = { 0x0884, 4, 4, 0, 1 }, ++ .dcp_det = { 0x0884, 3, 3, 0, 1 }, ++ .dp_det = { 0x0884, 5, 5, 0, 1 }, ++ .idm_sink_en = { 0x0768, 8, 8, 0, 1 }, ++ .idp_sink_en = { 0x0768, 7, 7, 0, 1 }, ++ .idp_src_en = { 0x0768, 9, 9, 0, 1 }, ++ .rdm_pdwn_en = { 0x0768, 10, 10, 0, 1 }, ++ .vdm_src_en = { 0x0768, 12, 12, 0, 1 }, ++ .vdp_src_en = { 0x0768, 11, 11, 0, 1 }, ++ }, ++ }, ++ { ++ .reg = 0x800, ++ .num_ports = 2, ++ .clkout_ctl = { 0x0808, 4, 4, 1, 0 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_OTG] = { ++ .phy_sus = { 0x800, 15, 0, 0, 0x1d1 }, ++ .ls_det_en = { 0x0684, 0, 0, 0, 1 }, ++ .ls_det_st = { 0x0694, 0, 0, 0, 1 }, ++ .ls_det_clr = { 0x06a4, 0, 0, 0, 1 } ++ }, ++ [USB2PHY_PORT_HOST] = { ++ .phy_sus = { 0x804, 15, 0, 0, 0x1d1 }, ++ .ls_det_en = { 0x0684, 1, 1, 0, 1 }, ++ .ls_det_st = { 0x0694, 1, 1, 0, 1 }, ++ .ls_det_clr = { 0x06a4, 1, 1, 0, 1 } ++ } ++ }, ++ }, ++ { /* sentinel */ } ++}; ++ + static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { + { + .reg = 0x100, +@@ -1269,6 +1328,7 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { + }; + + static const struct of_device_id rockchip_usb2phy_dt_match[] = { ++ { .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs }, + { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, + { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, + { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, +-- +2.11.0 + diff --git a/patches.drivers/0036-phy-rockchip-inno-usb2-Replace-the-extcon-API.patch b/patches.drivers/0036-phy-rockchip-inno-usb2-Replace-the-extcon-API.patch new file mode 100644 index 0000000..968d08b --- /dev/null +++ b/patches.drivers/0036-phy-rockchip-inno-usb2-Replace-the-extcon-API.patch @@ -0,0 +1,66 @@ +From acf9ff317787dcd191c4fad33c7ee8915867b178 Mon Sep 17 00:00:00 2001 +From: Chanwoo Choi +Date: Wed, 22 Mar 2017 19:10:31 +0900 +Subject: [PATCH 36/86] phy: rockchip-inno-usb2: Replace the extcon API + +Git-commit: 86f44c88738e015b451cfdafac3e98205e69cb6a +Patch-mainline: v4.14-rc1 +References: fate#323912 + +This patch uses the resource-managed extcon API for extcon_register_notifier() +and replaces the deprecated extcon API as following: +- extcon_get_cable_state_() -> extcon_get_state() +- extcon_set_cable_state_() -> extcon_set_state_sync() + +Acked-by: Kishon Vijay Abraham I +Signed-off-by: Chanwoo Choi +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 626883d9d176..ef033089b7a0 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -545,7 +545,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + rockchip_usb2phy_power_off(rport->phy); + /* fall through */ + case OTG_STATE_B_IDLE: +- if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) { ++ if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) { + dev_dbg(&rport->phy->dev, "usb otg host connect\n"); + rport->state = OTG_STATE_A_HOST; + rockchip_usb2phy_power_on(rport->phy); +@@ -598,7 +598,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + rport->vbus_attached = vbus_attach; + + if (notify_charger && rphy->edev) { +- extcon_set_cable_state_(rphy->edev, ++ extcon_set_state_sync(rphy->edev, + cable, vbus_attach); + if (cable == EXTCON_CHG_USB_SDP) + extcon_set_state_sync(rphy->edev, +@@ -619,7 +619,7 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + sch_work = true; + break; + case OTG_STATE_A_HOST: +- if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) { ++ if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) { + dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); + rport->state = OTG_STATE_B_IDLE; + rockchip_usb2phy_power_off(rport->phy); +@@ -1006,8 +1006,8 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, + if (!IS_ERR(rphy->edev)) { + rport->event_nb.notifier_call = rockchip_otg_event; + +- ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, +- &rport->event_nb); ++ ret = devm_extcon_register_notifier(rphy->dev, rphy->edev, ++ EXTCON_USB_HOST, &rport->event_nb); + if (ret) + dev_err(rphy->dev, "register USB HOST notifier failed\n"); + } +-- +2.11.0 + diff --git a/patches.drivers/0037-phy-rockchip-inno-usb2-add-support-for-rockchip-usbg.patch b/patches.drivers/0037-phy-rockchip-inno-usb2-add-support-for-rockchip-usbg.patch new file mode 100644 index 0000000..1827140 --- /dev/null +++ b/patches.drivers/0037-phy-rockchip-inno-usb2-add-support-for-rockchip-usbg.patch @@ -0,0 +1,361 @@ +From 64745d81fdbf143a94c1f7e94e838e190c4df6f6 Mon Sep 17 00:00:00 2001 +From: Frank Wang +Date: Fri, 11 Aug 2017 16:07:47 +0800 +Subject: [PATCH 37/86] phy: rockchip-inno-usb2: add support for + rockchip,usbgrf property + +Git-commit: 1543645c3119da117d4cc6fb664c74157e66f237 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +The registers of usb-phy are distributed in grf and usbgrf on some +Rockchip SoCs (e.g RV1108), this patch add a new rockchip,usbgrf +property to support this companion grf design. + +Signed-off-by: Frank Wang +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 109 +++++++++++++++++--------- + 1 file changed, 71 insertions(+), 38 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index ef033089b7a0..6796aeea6410 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -202,6 +202,7 @@ struct rockchip_usb2phy_port { + /** + * struct rockchip_usb2phy: usb2.0 phy driver data. + * @grf: General Register Files regmap. ++ * @usbgrf: USB General Register Files regmap. + * @clk: clock struct of phy input clk. + * @clk480m: clock struct of phy output clk. + * @clk_hw: clock struct of phy output clk management. +@@ -216,6 +217,7 @@ struct rockchip_usb2phy_port { + struct rockchip_usb2phy { + struct device *dev; + struct regmap *grf; ++ struct regmap *usbgrf; + struct clk *clk; + struct clk *clk480m; + struct clk_hw clk480m_hw; +@@ -227,7 +229,12 @@ struct rockchip_usb2phy { + struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; + }; + +-static inline int property_enable(struct rockchip_usb2phy *rphy, ++static inline struct regmap *get_reg_base(struct rockchip_usb2phy *rphy) ++{ ++ return rphy->usbgrf == NULL ? rphy->grf : rphy->usbgrf; ++} ++ ++static inline int property_enable(struct regmap *base, + const struct usb2phy_reg *reg, bool en) + { + unsigned int val, mask, tmp; +@@ -236,17 +243,17 @@ static inline int property_enable(struct rockchip_usb2phy *rphy, + mask = GENMASK(reg->bitend, reg->bitstart); + val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); + +- return regmap_write(rphy->grf, reg->offset, val); ++ return regmap_write(base, reg->offset, val); + } + +-static inline bool property_enabled(struct rockchip_usb2phy *rphy, ++static inline bool property_enabled(struct regmap *base, + const struct usb2phy_reg *reg) + { + int ret; + unsigned int tmp, orig; + unsigned int mask = GENMASK(reg->bitend, reg->bitstart); + +- ret = regmap_read(rphy->grf, reg->offset, &orig); ++ ret = regmap_read(base, reg->offset, &orig); + if (ret) + return false; + +@@ -258,11 +265,12 @@ static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) + { + struct rockchip_usb2phy *rphy = + container_of(hw, struct rockchip_usb2phy, clk480m_hw); ++ struct regmap *base = get_reg_base(rphy); + int ret; + + /* turn on 480m clk output if it is off */ +- if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) { +- ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true); ++ if (!property_enabled(base, &rphy->phy_cfg->clkout_ctl)) { ++ ret = property_enable(base, &rphy->phy_cfg->clkout_ctl, true); + if (ret) + return ret; + +@@ -277,17 +285,19 @@ static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) + { + struct rockchip_usb2phy *rphy = + container_of(hw, struct rockchip_usb2phy, clk480m_hw); ++ struct regmap *base = get_reg_base(rphy); + + /* turn off 480m clk output */ +- property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false); ++ property_enable(base, &rphy->phy_cfg->clkout_ctl, false); + } + + static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) + { + struct rockchip_usb2phy *rphy = + container_of(hw, struct rockchip_usb2phy, clk480m_hw); ++ struct regmap *base = get_reg_base(rphy); + +- return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl); ++ return property_enabled(base, &rphy->phy_cfg->clkout_ctl); + } + + static unsigned long +@@ -409,13 +419,13 @@ static int rockchip_usb2phy_init(struct phy *phy) + if (rport->mode != USB_DR_MODE_HOST && + rport->mode != USB_DR_MODE_UNKNOWN) { + /* clear bvalid status and enable bvalid detect irq */ +- ret = property_enable(rphy, ++ ret = property_enable(rphy->grf, + &rport->port_cfg->bvalid_det_clr, + true); + if (ret) + goto out; + +- ret = property_enable(rphy, ++ ret = property_enable(rphy->grf, + &rport->port_cfg->bvalid_det_en, + true); + if (ret) +@@ -429,11 +439,13 @@ static int rockchip_usb2phy_init(struct phy *phy) + } + } else if (rport->port_id == USB2PHY_PORT_HOST) { + /* clear linestate and enable linestate detect irq */ +- ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true); ++ ret = property_enable(rphy->grf, ++ &rport->port_cfg->ls_det_clr, true); + if (ret) + goto out; + +- ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true); ++ ret = property_enable(rphy->grf, ++ &rport->port_cfg->ls_det_en, true); + if (ret) + goto out; + +@@ -449,6 +461,7 @@ static int rockchip_usb2phy_power_on(struct phy *phy) + { + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); + struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); ++ struct regmap *base = get_reg_base(rphy); + int ret; + + dev_dbg(&rport->phy->dev, "port power on\n"); +@@ -460,7 +473,7 @@ static int rockchip_usb2phy_power_on(struct phy *phy) + if (ret) + return ret; + +- ret = property_enable(rphy, &rport->port_cfg->phy_sus, false); ++ ret = property_enable(base, &rport->port_cfg->phy_sus, false); + if (ret) + return ret; + +@@ -475,6 +488,7 @@ static int rockchip_usb2phy_power_off(struct phy *phy) + { + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); + struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); ++ struct regmap *base = get_reg_base(rphy); + int ret; + + dev_dbg(&rport->phy->dev, "port power off\n"); +@@ -482,7 +496,7 @@ static int rockchip_usb2phy_power_off(struct phy *phy) + if (rport->suspended) + return 0; + +- ret = property_enable(rphy, &rport->port_cfg->phy_sus, true); ++ ret = property_enable(base, &rport->port_cfg->phy_sus, true); + if (ret) + return ret; + +@@ -526,11 +540,11 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) + bool vbus_attach, sch_work, notify_charger; + + if (rport->utmi_avalid) +- vbus_attach = +- property_enabled(rphy, &rport->port_cfg->utmi_avalid); ++ vbus_attach = property_enabled(rphy->grf, ++ &rport->port_cfg->utmi_avalid); + else +- vbus_attach = +- property_enabled(rphy, &rport->port_cfg->utmi_bvalid); ++ vbus_attach = property_enabled(rphy->grf, ++ &rport->port_cfg->utmi_bvalid); + + sch_work = false; + notify_charger = false; +@@ -650,22 +664,28 @@ static const char *chg_to_string(enum power_supply_type chg_type) + static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, + bool en) + { +- property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); +- property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en); ++ struct regmap *base = get_reg_base(rphy); ++ ++ property_enable(base, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); ++ property_enable(base, &rphy->phy_cfg->chg_det.idp_src_en, en); + } + + static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, + bool en) + { +- property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en); +- property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en); ++ struct regmap *base = get_reg_base(rphy); ++ ++ property_enable(base, &rphy->phy_cfg->chg_det.vdp_src_en, en); ++ property_enable(base, &rphy->phy_cfg->chg_det.idm_sink_en, en); + } + + static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, + bool en) + { +- property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en); +- property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en); ++ struct regmap *base = get_reg_base(rphy); ++ ++ property_enable(base, &rphy->phy_cfg->chg_det.vdm_src_en, en); ++ property_enable(base, &rphy->phy_cfg->chg_det.idp_sink_en, en); + } + + #define CHG_DCD_POLL_TIME (100 * HZ / 1000) +@@ -677,6 +697,7 @@ static void rockchip_chg_detect_work(struct work_struct *work) + struct rockchip_usb2phy_port *rport = + container_of(work, struct rockchip_usb2phy_port, chg_work.work); + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); ++ struct regmap *base = get_reg_base(rphy); + bool is_dcd, tmout, vout; + unsigned long delay; + +@@ -687,7 +708,7 @@ static void rockchip_chg_detect_work(struct work_struct *work) + if (!rport->suspended) + rockchip_usb2phy_power_off(rport->phy); + /* put the controller in non-driving mode */ +- property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false); ++ property_enable(base, &rphy->phy_cfg->chg_det.opmode, false); + /* Start DCD processing stage 1 */ + rockchip_chg_enable_dcd(rphy, true); + rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; +@@ -696,7 +717,8 @@ static void rockchip_chg_detect_work(struct work_struct *work) + break; + case USB_CHG_STATE_WAIT_FOR_DCD: + /* get data contact detection status */ +- is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det); ++ is_dcd = property_enabled(rphy->grf, ++ &rphy->phy_cfg->chg_det.dp_det); + tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES; + /* stage 2 */ + if (is_dcd || tmout) { +@@ -713,7 +735,8 @@ static void rockchip_chg_detect_work(struct work_struct *work) + } + break; + case USB_CHG_STATE_DCD_DONE: +- vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det); ++ vout = property_enabled(rphy->grf, ++ &rphy->phy_cfg->chg_det.cp_det); + rockchip_chg_enable_primary_det(rphy, false); + if (vout) { + /* Voltage Source on DM, Probe on DP */ +@@ -734,7 +757,8 @@ static void rockchip_chg_detect_work(struct work_struct *work) + } + break; + case USB_CHG_STATE_PRIMARY_DONE: +- vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det); ++ vout = property_enabled(rphy->grf, ++ &rphy->phy_cfg->chg_det.dcp_det); + /* Turn off voltage source */ + rockchip_chg_enable_secondary_det(rphy, false); + if (vout) +@@ -748,7 +772,7 @@ static void rockchip_chg_detect_work(struct work_struct *work) + /* fall through */ + case USB_CHG_STATE_DETECTED: + /* put the controller in normal mode */ +- property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true); ++ property_enable(base, &rphy->phy_cfg->chg_det.opmode, true); + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); + dev_info(&rport->phy->dev, "charger = %s\n", + chg_to_string(rphy->chg_type)); +@@ -790,8 +814,7 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work) + if (ret < 0) + goto next_schedule; + +- ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, +- &uhd); ++ ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd); + if (ret < 0) + goto next_schedule; + +@@ -845,8 +868,8 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work) + * activate the linestate detection to get the next device + * plug-in irq. + */ +- property_enable(rphy, &rport->port_cfg->ls_det_clr, true); +- property_enable(rphy, &rport->port_cfg->ls_det_en, true); ++ property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true); ++ property_enable(rphy->grf, &rport->port_cfg->ls_det_en, true); + + /* + * we don't need to rearm the delayed work when the phy port +@@ -869,14 +892,14 @@ static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) + struct rockchip_usb2phy_port *rport = data; + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + +- if (!property_enabled(rphy, &rport->port_cfg->ls_det_st)) ++ if (!property_enabled(rphy->grf, &rport->port_cfg->ls_det_st)) + return IRQ_NONE; + + mutex_lock(&rport->mutex); + + /* disable linestate detect irq and clear its status */ +- property_enable(rphy, &rport->port_cfg->ls_det_en, false); +- property_enable(rphy, &rport->port_cfg->ls_det_clr, true); ++ property_enable(rphy->grf, &rport->port_cfg->ls_det_en, false); ++ property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true); + + mutex_unlock(&rport->mutex); + +@@ -896,13 +919,13 @@ static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) + struct rockchip_usb2phy_port *rport = data; + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + +- if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st)) ++ if (!property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st)) + return IRQ_NONE; + + mutex_lock(&rport->mutex); + + /* clear bvalid detect irq pending status */ +- property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true); ++ property_enable(rphy->grf, &rport->port_cfg->bvalid_det_clr, true); + + mutex_unlock(&rport->mutex); + +@@ -1045,6 +1068,16 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) + if (IS_ERR(rphy->grf)) + return PTR_ERR(rphy->grf); + ++ if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy")) { ++ rphy->usbgrf = ++ syscon_regmap_lookup_by_phandle(dev->of_node, ++ "rockchip,usbgrf"); ++ if (IS_ERR(rphy->usbgrf)) ++ return PTR_ERR(rphy->usbgrf); ++ } else { ++ rphy->usbgrf = NULL; ++ } ++ + if (of_property_read_u32(np, "reg", ®)) { + dev_err(dev, "the reg property is not assigned in %s node\n", + np->name); +-- +2.11.0 + diff --git a/patches.drivers/0038-phy-rockchip-inno-usb2-add-support-for-otg-mux-inter.patch b/patches.drivers/0038-phy-rockchip-inno-usb2-add-support-for-otg-mux-inter.patch new file mode 100644 index 0000000..4266d88 --- /dev/null +++ b/patches.drivers/0038-phy-rockchip-inno-usb2-add-support-for-otg-mux-inter.patch @@ -0,0 +1,120 @@ +From fb227ba57fad181864500ebf6bf3ab327023e1ae Mon Sep 17 00:00:00 2001 +From: Frank Wang +Date: Fri, 11 Aug 2017 16:07:49 +0800 +Subject: [PATCH 38/86] phy: rockchip-inno-usb2: add support for otg-mux + interrupt + +Git-commit: 0983e2abc8334a0fec1e93a47e712708acaf5ad2 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +The otg-id/otg-bvalid/linestate interrupts are multiplexed together +in otg-port on some Rockchip SoC (e.g RV1108), this patch add support +for it. + +Signed-off-by: Frank Wang +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 63 +++++++++++++++++++++------ + 1 file changed, 50 insertions(+), 13 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 6796aeea6410..5ec671422118 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -172,6 +172,8 @@ struct rockchip_usb2phy_cfg { + * @vbus_attached: otg device vbus status. + * @bvalid_irq: IRQ number assigned for vbus valid rise detection. + * @ls_irq: IRQ number assigned for linestate detection. ++ * @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate ++ * irqs to one irq in otg-port. + * @mutex: for register updating in sm_work. + * @chg_work: charge detect work. + * @otg_sm_work: OTG state machine work. +@@ -189,6 +191,7 @@ struct rockchip_usb2phy_port { + bool vbus_attached; + int bvalid_irq; + int ls_irq; ++ int otg_mux_irq; + struct mutex mutex; + struct delayed_work chg_work; + struct delayed_work otg_sm_work; +@@ -934,6 +937,17 @@ static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) + return IRQ_HANDLED; + } + ++static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) ++{ ++ struct rockchip_usb2phy_port *rport = data; ++ struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); ++ ++ if (property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st)) ++ return rockchip_usb2phy_bvalid_irq(irq, data); ++ else ++ return IRQ_NONE; ++} ++ + static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + struct device_node *child_np) +@@ -1010,20 +1024,43 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, + rport->utmi_avalid = + of_property_read_bool(child_np, "rockchip,utmi-avalid"); + +- rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); +- if (rport->bvalid_irq < 0) { +- dev_err(rphy->dev, "no vbus valid irq provided\n"); +- ret = rport->bvalid_irq; +- goto out; +- } ++ /* ++ * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate ++ * interrupts muxed together, so probe the otg-mux interrupt first, ++ * if not found, then look for the regular interrupts one by one. ++ */ ++ rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux"); ++ if (rport->otg_mux_irq > 0) { ++ ret = devm_request_threaded_irq(rphy->dev, rport->otg_mux_irq, ++ NULL, ++ rockchip_usb2phy_otg_mux_irq, ++ IRQF_ONESHOT, ++ "rockchip_usb2phy_otg", ++ rport); ++ if (ret) { ++ dev_err(rphy->dev, ++ "failed to request otg-mux irq handle\n"); ++ goto out; ++ } ++ } else { ++ rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); ++ if (rport->bvalid_irq < 0) { ++ dev_err(rphy->dev, "no vbus valid irq provided\n"); ++ ret = rport->bvalid_irq; ++ goto out; ++ } + +- ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL, +- rockchip_usb2phy_bvalid_irq, +- IRQF_ONESHOT, +- "rockchip_usb2phy_bvalid", rport); +- if (ret) { +- dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n"); +- goto out; ++ ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, ++ NULL, ++ rockchip_usb2phy_bvalid_irq, ++ IRQF_ONESHOT, ++ "rockchip_usb2phy_bvalid", ++ rport); ++ if (ret) { ++ dev_err(rphy->dev, ++ "failed to request otg-bvalid irq handle\n"); ++ goto out; ++ } + } + + if (!IS_ERR(rphy->edev)) { +-- +2.11.0 + diff --git a/patches.drivers/0039-phy-rockchip-inno-usb2-add-support-of-usb2-phy-for-r.patch b/patches.drivers/0039-phy-rockchip-inno-usb2-add-support-of-usb2-phy-for-r.patch new file mode 100644 index 0000000..793bf0a --- /dev/null +++ b/patches.drivers/0039-phy-rockchip-inno-usb2-add-support-of-usb2-phy-for-r.patch @@ -0,0 +1,95 @@ +From 85b002277614ac4b6081ffca07ae29f645706fc8 Mon Sep 17 00:00:00 2001 +From: Frank Wang +Date: Fri, 11 Aug 2017 16:08:48 +0800 +Subject: [PATCH 39/86] phy: rockchip-inno-usb2: add support of usb2-phy for + rv1108 SoCs + +Git-commit: fc938810d950f4846eb05b9af5614677e05c5a65 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +This adds support usb2-phy for rv1108 SoCs and amend phy Documentation. + +Signed-off-by: Frank Wang +Acked-by: Rob Herring +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + .../bindings/phy/phy-rockchip-inno-usb2.txt | 1 + + drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 43 ++++++++++++++++++++++ + 2 files changed, 44 insertions(+) + +diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt +index 84d59b0db8df..90cf87b3af50 100644 +--- a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt ++++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt +@@ -6,6 +6,7 @@ Required properties (phy (parent) node): + * "rockchip,rk3328-usb2phy" + * "rockchip,rk3366-usb2phy" + * "rockchip,rk3399-usb2phy" ++ * "rockchip,rv1108-usb2phy" + - reg : the address offset of grf for usb-phy configuration. + - #clock-cells : should be 0. + - clock-output-names : specify the 480m output clock name. +diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +index 5ec671422118..ee7ce5ee53f9 100644 +--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c ++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +@@ -1397,11 +1397,54 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { + { /* sentinel */ } + }; + ++static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { ++ { ++ .reg = 0x100, ++ .num_ports = 2, ++ .clkout_ctl = { 0x108, 4, 4, 1, 0 }, ++ .port_cfgs = { ++ [USB2PHY_PORT_OTG] = { ++ .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, ++ .bvalid_det_en = { 0x0680, 3, 3, 0, 1 }, ++ .bvalid_det_st = { 0x0690, 3, 3, 0, 1 }, ++ .bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 }, ++ .ls_det_en = { 0x0680, 2, 2, 0, 1 }, ++ .ls_det_st = { 0x0690, 2, 2, 0, 1 }, ++ .ls_det_clr = { 0x06a0, 2, 2, 0, 1 }, ++ .utmi_bvalid = { 0x0804, 10, 10, 0, 1 }, ++ .utmi_ls = { 0x0804, 13, 12, 0, 1 }, ++ }, ++ [USB2PHY_PORT_HOST] = { ++ .phy_sus = { 0x0104, 15, 0, 0, 0x1d1 }, ++ .ls_det_en = { 0x0680, 4, 4, 0, 1 }, ++ .ls_det_st = { 0x0690, 4, 4, 0, 1 }, ++ .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, ++ .utmi_ls = { 0x0804, 9, 8, 0, 1 }, ++ .utmi_hstdet = { 0x0804, 7, 7, 0, 1 } ++ } ++ }, ++ .chg_det = { ++ .opmode = { 0x0100, 3, 0, 5, 1 }, ++ .cp_det = { 0x0804, 1, 1, 0, 1 }, ++ .dcp_det = { 0x0804, 0, 0, 0, 1 }, ++ .dp_det = { 0x0804, 2, 2, 0, 1 }, ++ .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, ++ .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, ++ .idp_src_en = { 0x0108, 9, 9, 0, 1 }, ++ .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, ++ .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, ++ .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, ++ }, ++ }, ++ { /* sentinel */ } ++}; ++ + static const struct of_device_id rockchip_usb2phy_dt_match[] = { + { .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs }, + { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, + { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, + { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, ++ { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs }, + {} + }; + MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); +-- +2.11.0 + diff --git a/patches.drivers/0040-phy-rockchip-typec-remove-unused-dfp-variable.patch b/patches.drivers/0040-phy-rockchip-typec-remove-unused-dfp-variable.patch new file mode 100644 index 0000000..d692bb4 --- /dev/null +++ b/patches.drivers/0040-phy-rockchip-typec-remove-unused-dfp-variable.patch @@ -0,0 +1,44 @@ +From 4ecdb969e0cbb16c2320043706c23c66562f1837 Mon Sep 17 00:00:00 2001 +From: Shawn Lin +Date: Thu, 3 Aug 2017 09:26:19 +0800 +Subject: [PATCH 40/86] phy: rockchip-typec: remove unused dfp variable + +Git-commit: 5e39c6cf575f0539a3cac86aa066624d17d86816 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +In order to silent the 'W=1' compile warning: + +drivers/phy/rockchip/phy-rockchip-typec.c: In function 'tcphy_get_mode': +drivers/phy/rockchip/phy-rockchip-typec.c:625:7: warning: variable 'dfp' +set but not used [-Wunused-but-set-variable] + +Cc: Chris Zhong +Signed-off-by: Shawn Lin +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index 7cfb0f8995de..4d2c57f21d76 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -622,12 +622,11 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) + struct extcon_dev *edev = tcphy->extcon; + union extcon_property_value property; + unsigned int id; +- bool dfp, ufp, dp; ++ bool ufp, dp; + u8 mode; + int ret; + + ufp = extcon_get_state(edev, EXTCON_USB); +- dfp = extcon_get_state(edev, EXTCON_USB_HOST); + dp = extcon_get_state(edev, EXTCON_DISP_DP); + + mode = MODE_DFP_USB; +-- +2.11.0 + diff --git a/patches.drivers/0041-phy-rockchip-pcie-Reconstruct-driver-to-support-per-.patch b/patches.drivers/0041-phy-rockchip-pcie-Reconstruct-driver-to-support-per-.patch new file mode 100644 index 0000000..b1e6aeb --- /dev/null +++ b/patches.drivers/0041-phy-rockchip-pcie-Reconstruct-driver-to-support-per-.patch @@ -0,0 +1,272 @@ +From 254627498a39b3908f02513667e710169082efb5 Mon Sep 17 00:00:00 2001 +From: Shawn Lin +Date: Wed, 19 Jul 2017 17:55:14 +0800 +Subject: [PATCH 41/86] phy: rockchip-pcie: Reconstruct driver to support + per-lane PHYs + +Git-commit: 90a7612d070d5caf023b34864028ccce5bffd6ce +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Reconstruct the whole driver to support per-lane PHYs. Note that we could +also support the legacy PHY if you don't provide argument to +rockchip_pcie_phy_of_xlate(). + +Tested-by: Jeffy Chen +Signed-off-by: Shawn Lin +[bhelgaas: use postincrement/decrement when order doesn't matter, uninline +to_pcie_phy() so decl fits on one line] +Signed-off-by: Bjorn Helgaas +Reviewed-by: Brian Norris +Acked-by: Kishon Vijay Abraham I + +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-pcie.c | 131 +++++++++++++++++++++++++++---- + 1 file changed, 117 insertions(+), 14 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-pcie.c b/drivers/phy/rockchip/phy-rockchip-pcie.c +index 6904633cad68..7cbdde029c0a 100644 +--- a/drivers/phy/rockchip/phy-rockchip-pcie.c ++++ b/drivers/phy/rockchip/phy-rockchip-pcie.c +@@ -73,10 +73,38 @@ struct rockchip_pcie_data { + struct rockchip_pcie_phy { + struct rockchip_pcie_data *phy_data; + struct regmap *reg_base; ++ struct phy_pcie_instance { ++ struct phy *phy; ++ u32 index; ++ } phys[PHY_MAX_LANE_NUM]; ++ struct mutex pcie_mutex; + struct reset_control *phy_rst; + struct clk *clk_pciephy_ref; ++ int pwr_cnt; ++ int init_cnt; + }; + ++static struct rockchip_pcie_phy *to_pcie_phy(struct phy_pcie_instance *inst) ++{ ++ return container_of(inst, struct rockchip_pcie_phy, ++ phys[inst->index]); ++} ++ ++static struct phy *rockchip_pcie_phy_of_xlate(struct device *dev, ++ struct of_phandle_args *args) ++{ ++ struct rockchip_pcie_phy *rk_phy = dev_get_drvdata(dev); ++ ++ if (args->args_count == 0) ++ return rk_phy->phys[0].phy; ++ ++ if (WARN_ON(args->args[0] >= PHY_MAX_LANE_NUM)) ++ return ERR_PTR(-ENODEV); ++ ++ return rk_phy->phys[args->args[0]].phy; ++} ++ ++ + static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy, + u32 addr, u32 data) + { +@@ -116,29 +144,59 @@ static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy, + + static int rockchip_pcie_phy_power_off(struct phy *phy) + { +- struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); ++ struct phy_pcie_instance *inst = phy_get_drvdata(phy); ++ struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst); + int err = 0; + ++ mutex_lock(&rk_phy->pcie_mutex); ++ ++ regmap_write(rk_phy->reg_base, ++ rk_phy->phy_data->pcie_laneoff, ++ HIWORD_UPDATE(PHY_LANE_IDLE_OFF, ++ PHY_LANE_IDLE_MASK, ++ PHY_LANE_IDLE_A_SHIFT + inst->index)); ++ ++ if (--rk_phy->pwr_cnt) ++ goto err_out; ++ + err = reset_control_assert(rk_phy->phy_rst); + if (err) { + dev_err(&phy->dev, "assert phy_rst err %d\n", err); +- return err; ++ goto err_restore; + } + ++err_out: ++ mutex_unlock(&rk_phy->pcie_mutex); + return 0; ++ ++err_restore: ++ rk_phy->pwr_cnt++; ++ regmap_write(rk_phy->reg_base, ++ rk_phy->phy_data->pcie_laneoff, ++ HIWORD_UPDATE(!PHY_LANE_IDLE_OFF, ++ PHY_LANE_IDLE_MASK, ++ PHY_LANE_IDLE_A_SHIFT + inst->index)); ++ mutex_unlock(&rk_phy->pcie_mutex); ++ return err; + } + + static int rockchip_pcie_phy_power_on(struct phy *phy) + { +- struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); ++ struct phy_pcie_instance *inst = phy_get_drvdata(phy); ++ struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst); + int err = 0; + u32 status; + unsigned long timeout; + ++ mutex_lock(&rk_phy->pcie_mutex); ++ ++ if (rk_phy->pwr_cnt++) ++ goto err_out; ++ + err = reset_control_deassert(rk_phy->phy_rst); + if (err) { + dev_err(&phy->dev, "deassert phy_rst err %d\n", err); +- return err; ++ goto err_pwr_cnt; + } + + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, +@@ -146,6 +204,12 @@ static int rockchip_pcie_phy_power_on(struct phy *phy) + PHY_CFG_ADDR_MASK, + PHY_CFG_ADDR_SHIFT)); + ++ regmap_write(rk_phy->reg_base, ++ rk_phy->phy_data->pcie_laneoff, ++ HIWORD_UPDATE(!PHY_LANE_IDLE_OFF, ++ PHY_LANE_IDLE_MASK, ++ PHY_LANE_IDLE_A_SHIFT + inst->index)); ++ + /* + * No documented timeout value for phy operation below, + * so we make it large enough here. And we use loop-break +@@ -214,18 +278,29 @@ static int rockchip_pcie_phy_power_on(struct phy *phy) + goto err_pll_lock; + } + ++err_out: ++ mutex_unlock(&rk_phy->pcie_mutex); + return 0; + + err_pll_lock: + reset_control_assert(rk_phy->phy_rst); ++err_pwr_cnt: ++ rk_phy->pwr_cnt--; ++ mutex_unlock(&rk_phy->pcie_mutex); + return err; + } + + static int rockchip_pcie_phy_init(struct phy *phy) + { +- struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); ++ struct phy_pcie_instance *inst = phy_get_drvdata(phy); ++ struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst); + int err = 0; + ++ mutex_lock(&rk_phy->pcie_mutex); ++ ++ if (rk_phy->init_cnt++) ++ goto err_out; ++ + err = clk_prepare_enable(rk_phy->clk_pciephy_ref); + if (err) { + dev_err(&phy->dev, "Fail to enable pcie ref clock.\n"); +@@ -238,20 +313,33 @@ static int rockchip_pcie_phy_init(struct phy *phy) + goto err_reset; + } + +- return err; ++err_out: ++ mutex_unlock(&rk_phy->pcie_mutex); ++ return 0; + + err_reset: ++ + clk_disable_unprepare(rk_phy->clk_pciephy_ref); + err_refclk: ++ rk_phy->init_cnt--; ++ mutex_unlock(&rk_phy->pcie_mutex); + return err; + } + + static int rockchip_pcie_phy_exit(struct phy *phy) + { +- struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); ++ struct phy_pcie_instance *inst = phy_get_drvdata(phy); ++ struct rockchip_pcie_phy *rk_phy = to_pcie_phy(inst); ++ ++ mutex_lock(&rk_phy->pcie_mutex); ++ ++ if (--rk_phy->init_cnt) ++ goto err_init_cnt; + + clk_disable_unprepare(rk_phy->clk_pciephy_ref); + ++err_init_cnt: ++ mutex_unlock(&rk_phy->pcie_mutex); + return 0; + } + +@@ -283,10 +371,11 @@ static int rockchip_pcie_phy_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct rockchip_pcie_phy *rk_phy; +- struct phy *generic_phy; + struct phy_provider *phy_provider; + struct regmap *grf; + const struct of_device_id *of_id; ++ int i; ++ u32 phy_num; + + grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(grf)) { +@@ -305,6 +394,8 @@ static int rockchip_pcie_phy_probe(struct platform_device *pdev) + rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data; + rk_phy->reg_base = grf; + ++ mutex_init(&rk_phy->pcie_mutex); ++ + rk_phy->phy_rst = devm_reset_control_get(dev, "phy"); + if (IS_ERR(rk_phy->phy_rst)) { + if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER) +@@ -319,14 +410,26 @@ static int rockchip_pcie_phy_probe(struct platform_device *pdev) + return PTR_ERR(rk_phy->clk_pciephy_ref); + } + +- generic_phy = devm_phy_create(dev, dev->of_node, &ops); +- if (IS_ERR(generic_phy)) { +- dev_err(dev, "failed to create PHY\n"); +- return PTR_ERR(generic_phy); ++ /* parse #phy-cells to see if it's legacy PHY model */ ++ if (of_property_read_u32(dev->of_node, "#phy-cells", &phy_num)) ++ return -ENOENT; ++ ++ phy_num = (phy_num == 0) ? 1 : PHY_MAX_LANE_NUM; ++ dev_dbg(dev, "phy number is %d\n", phy_num); ++ ++ for (i = 0; i < phy_num; i++) { ++ rk_phy->phys[i].phy = devm_phy_create(dev, dev->of_node, &ops); ++ if (IS_ERR(rk_phy->phys[i].phy)) { ++ dev_err(dev, "failed to create PHY%d\n", i); ++ return PTR_ERR(rk_phy->phys[i].phy); ++ } ++ rk_phy->phys[i].index = i; ++ phy_set_drvdata(rk_phy->phys[i].phy, &rk_phy->phys[i]); + } + +- phy_set_drvdata(generic_phy, rk_phy); +- phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); ++ platform_set_drvdata(pdev, rk_phy); ++ phy_provider = devm_of_phy_provider_register(dev, ++ rockchip_pcie_phy_of_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); + } +-- +2.11.0 + diff --git a/patches.drivers/0042-phy-rockchip-typec-Set-the-AUX-channel-flip-state-ea.patch b/patches.drivers/0042-phy-rockchip-typec-Set-the-AUX-channel-flip-state-ea.patch new file mode 100644 index 0000000..16bdf2d --- /dev/null +++ b/patches.drivers/0042-phy-rockchip-typec-Set-the-AUX-channel-flip-state-ea.patch @@ -0,0 +1,174 @@ +From eb4ad55c1e96021f51982e7cb913f93780f84f90 Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +Date: Fri, 22 Sep 2017 09:44:03 -0700 +Subject: [PATCH 42/86] phy: rockchip-typec: Set the AUX channel flip state + earlier + +Git-commit: f98b74387551f6d266c044d90e87e4919b25b970 +Patch-mainline: v4.14-rc6 +References: fate#323912 + +On some DP monitors we found that setting the wrong flip state on the +AUX channel could cause the monitor to stop asserting HotPlug Detect +(HPD). Setting the right flip state caused these monitors to start +asserting HotPlug Detect again. + +Here's what we believe was happening: +* We'd plug in the monitor and we'd see HPD assert +* We'd quickly see HPD deassert +* The kernel would try to init the type C PHY but would init it in USB + mode (because there was a peripheral there but no HPD) +* Because the kernel never set the flip mode properly we'd never see + the HPD come back. + +With this change, we'll still see HPD disappear (we don't think +there's anything we can do about that), but then it will come back. + +Overall we can say that it's sane to set the AUX channel flip state +even when HPD is not asserted. + +NOTE: to make this change possible, I needed to do a bit of cleanup to +the tcphy_dp_aux_calibration() function so that it doesn't ever +clobber the FLIP state. This made it very obvious that a line of code +documented as "setting bit 12" also did a bunch of other magic, +undocumented stuff. For now I'll just break out the bits and add a +comment that this is black magic and we'll try to document +tcphy_dp_aux_calibration() better in a future CL. + +ALSO NOTE: the old function used to write a bunch of hardcoded +values in _some_ cases instead of doing a read-modify-write. One +could possibly assert that these could have had (beneficial) side +effects and thus with this new code (which always does +read-modify-write) we could have a bug. We shouldn't need to worry, +though, since in the old code tcphy_dp_aux_calibration() was always +called following the de-assertion of "reset" the the type C PHY. +...so the type C PHY was always in default state. TX_ANA_CTRL_REG_1 +is documented to be 0x0 after reset. This was also confirmed by +printk. + +Suggested-by: Shawn Nematbakhsh +Reviewed-by: Chris Zhong +Signed-off-by: Douglas Anderson +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 62 +++++++++++++++++++++---------- + 1 file changed, 42 insertions(+), 20 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index 4d2c57f21d76..342a77733207 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -443,14 +443,34 @@ static inline int property_enable(struct rockchip_typec_phy *tcphy, + return regmap_write(tcphy->grf_regs, reg->offset, val | mask); + } + ++static void tcphy_dp_aux_set_flip(struct rockchip_typec_phy *tcphy) ++{ ++ u16 tx_ana_ctrl_reg_1; ++ ++ /* ++ * Select the polarity of the xcvr: ++ * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull ++ * down aux_m) ++ * 0, Normal polarity (if TYPEC, pulls up aux_m and pulls down ++ * aux_p) ++ */ ++ tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); ++ if (!tcphy->flip) ++ tx_ana_ctrl_reg_1 |= BIT(12); ++ else ++ tx_ana_ctrl_reg_1 &= ~BIT(12); ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); ++} ++ + static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + { ++ u16 tx_ana_ctrl_reg_1; + u16 rdata, rdata2, val; + + /* disable txda_cal_latch_en for rewrite the calibration values */ +- rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); +- val = rdata & 0xdfff; +- writel(val, tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 &= ~BIT(13); ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + + /* + * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and +@@ -472,9 +492,8 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + * Activate this signal for 1 clock cycle to sample new calibration + * values. + */ +- rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); +- val = rdata | 0x2000; +- writel(val, tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 |= BIT(13); ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + usleep_range(150, 200); + + /* set TX Voltage Level and TX Deemphasis to 0 */ +@@ -482,8 +501,10 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + /* re-enable decap */ + writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2); + writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2); +- writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1); +- writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 |= BIT(3); ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 |= BIT(4); ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + + writel(0, tcphy->base + TX_ANA_CTRL_REG_5); + +@@ -494,8 +515,10 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4); + + /* re-enables Bandgap reference for LDO */ +- writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1); +- writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 |= BIT(7); ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 |= BIT(8); ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + + /* + * re-enables the transmitter pre-driver, driver data selection MUX, +@@ -505,17 +528,15 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2); + + /* +- * BIT 12: Controls auxda_polarity, which selects the polarity of the +- * xcvr: +- * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull +- * down aux_m) +- * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down +- * aux_p) ++ * Do some magic undocumented stuff, some of which appears to ++ * undo the "re-enables Bandgap reference for LDO" above. + */ +- val = 0xa078; +- if (!tcphy->flip) +- val |= BIT(12); +- writel(val, tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 |= BIT(15); ++ tx_ana_ctrl_reg_1 &= ~BIT(8); ++ tx_ana_ctrl_reg_1 &= ~BIT(7); ++ tx_ana_ctrl_reg_1 |= BIT(6); ++ tx_ana_ctrl_reg_1 |= BIT(5); ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + + writel(0, tcphy->base + TX_ANA_CTRL_REG_3); + writel(0, tcphy->base + TX_ANA_CTRL_REG_4); +@@ -555,6 +576,7 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) + reset_control_deassert(tcphy->tcphy_rst); + + property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip); ++ tcphy_dp_aux_set_flip(tcphy); + + tcphy_cfg_24m(tcphy); + +-- +2.11.0 + diff --git a/patches.drivers/0043-phy-rockchip-typec-Don-t-set-the-aux-voltage-swing-t.patch b/patches.drivers/0043-phy-rockchip-typec-Don-t-set-the-aux-voltage-swing-t.patch new file mode 100644 index 0000000..7459b80 --- /dev/null +++ b/patches.drivers/0043-phy-rockchip-typec-Don-t-set-the-aux-voltage-swing-t.patch @@ -0,0 +1,104 @@ +From 44809695bced033d41f81cd2e5cfceb29a27a7ea Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +Date: Fri, 22 Sep 2017 09:44:04 -0700 +Subject: [PATCH 43/86] phy: rockchip-typec: Don't set the aux voltage swing to + 400 mV + +Git-commit: 26e03d803c8191e906360a0320c05b12d45a37ae +Patch-mainline: v4.14-rc6 +References: fate#323912 + +On rk3399-gru-kevin there are some cases where we're seeing AUX CH +failures when trying to do DisplayPort over type C. Problems are +intermittent and don't reproduce all the time. Problems are often +bursty and failures persist for several seconds before going away. +The failure case I focused on is: +* A particular type C to HDMI adapter. +* One orientation (flip mode) of that adapter. +* Easier to see failures when something is plugged into the _other + type C port at the same time. +* Problems reproduce on both type C ports (left and right side). + +Ironically problems also stop reproducing when I solder wires onto the +AUX CH signals on a port (even if no scope is connected to the +signals). In this case, problems only stop reproducing on the port +with the wires connected. + +From the above it appears that something about the signaling on the +aux channel is marginal and any slight differences can bring us over +the edge to failure. + +It turns out that we can fix our problems by just increasing the +voltage swing of the AUX CH, giving us a bunch of extra margin. In DP +up to version 1.2 the voltage swing on the aux channel was specced as +.29 V to 1.38 V. In DP version 1.3 the aux channel voltage was +tightened to be between .29 V and .40 V, but it clarifies that it +really only needs the lower voltage when operating at the highest +speed (HBR3 mode). So right now we are trying to use a voltage that +technically should be valid for all versions of the spec (including +version 1.3 when transmitting at HBR3). That would be great to do if +it worked reliably. ...but it doesn't seem to. + +It turns out that if you continue to read through the DP part of the +rk3399 TRM and other parts of the type C PHY spec you'll find out that +while the rk3399 does support DP 1.3, it doesn't support HBR3. The +docs specifically say "RBR, HBR and HBR2 data rates only". Thus there +is actually no requirement to support an AUX CH swing of .4 V. + +Even if there is no actual requirement to support the tighter voltage +swing, one could possibly argue that we should support it anyway. The +DP spec clarifies that the lower voltage on the AUX CH will reduce +cross talk in some cases and that seems like it could be beneficial +even at the lower bit rates. At the moment, though, we are seeing +problems with the AUX CH and not on the other lines. Also, checking +another known working and similar laptop shows that the other laptop +runs the AUX channel at a higher voltage. + +Other notes: +* Looking at measurements done on the AUX CH we weren't actually + compliant with the DP 1.3 spec anyway. AUX CH peek-to-peek voltage + was measured on rk3399-gru-kevin as .466 V which is > .4 V. +* With this new patch the AUX channel isn't actually 1.0 V, but it has + been confirmed that the signal is better and has more margin. Eye + diagram passes. +* If someone were truly an expert in the Type C PHY and in DisplayPort + signaling they might be able to make things work and keep the + voltage at < .4 V. The Type C PHY seems to have a plethora of + tuning knobs that could almost certainly improve the signal + integrity. Some of these things (like enabling tx_fcm_full_margin) + even seem to fix my problems. However, lacking expertise I can't + say whether this is a better or worse solution. Tightening signals + to give cleaner waveforms can often have adverse affects, like + increasing EMI or adding noise to other signals. I'd rather not + tune things like this without a healthy application of expertise + that I don't have. + +Signed-off-by: Douglas Anderson +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index 342a77733207..b25c00432f9b 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -543,10 +543,11 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + writel(0, tcphy->base + TX_ANA_CTRL_REG_5); + + /* +- * Controls low_power_swing_en, set the voltage swing of the driver +- * to 400mv. The values below are peak to peak (differential) values. ++ * Controls low_power_swing_en, don't set the voltage swing of the ++ * driver to 400mv. The values below are peak to peak (differential) ++ * values. + */ +- writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL); ++ writel(0, tcphy->base + TXDA_COEFF_CALC_CTRL); + writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA); + + /* Controls tx_high_z_tm_en */ +-- +2.11.0 + diff --git a/patches.drivers/0044-phy-rockchip-typec-Check-for-errors-from-tcphy_phy_i.patch b/patches.drivers/0044-phy-rockchip-typec-Check-for-errors-from-tcphy_phy_i.patch new file mode 100644 index 0000000..2911c96 --- /dev/null +++ b/patches.drivers/0044-phy-rockchip-typec-Check-for-errors-from-tcphy_phy_i.patch @@ -0,0 +1,60 @@ +From 38afcd44575fa34fafbe0bd4e8b0aa005bced57c Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +Date: Fri, 29 Sep 2017 16:58:46 -0700 +Subject: [PATCH 44/86] phy: rockchip-typec: Check for errors from + tcphy_phy_init() + +Git-commit: 2fb850092fd95198a0a4746f07b80077d5a3aa37 +Patch-mainline: v4.14-rc6 +References: fate#323912 + +The function tcphy_phy_init() could return an error but the callers +weren't checking the return value. They should. In at least one case +while testing I saw the message "wait pma ready timeout" which +indicates that tcphy_phy_init() really could return an error and we +should account for it. + +Signed-off-by: Douglas Anderson +Reviewed-by: Guenter Roeck +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index b25c00432f9b..a958c9bced01 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -708,8 +708,11 @@ static int rockchip_usb3_phy_power_on(struct phy *phy) + if (tcphy->mode == new_mode) + goto unlock_ret; + +- if (tcphy->mode == MODE_DISCONNECT) +- tcphy_phy_init(tcphy, new_mode); ++ if (tcphy->mode == MODE_DISCONNECT) { ++ ret = tcphy_phy_init(tcphy, new_mode); ++ if (ret) ++ goto unlock_ret; ++ } + + /* wait TCPHY for pipe ready */ + for (timeout = 0; timeout < 100; timeout++) { +@@ -783,10 +786,12 @@ static int rockchip_dp_phy_power_on(struct phy *phy) + */ + if (new_mode == MODE_DFP_DP && tcphy->mode != MODE_DISCONNECT) { + tcphy_phy_deinit(tcphy); +- tcphy_phy_init(tcphy, new_mode); ++ ret = tcphy_phy_init(tcphy, new_mode); + } else if (tcphy->mode == MODE_DISCONNECT) { +- tcphy_phy_init(tcphy, new_mode); ++ ret = tcphy_phy_init(tcphy, new_mode); + } ++ if (ret) ++ goto unlock_ret; + + ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, + val, val & DP_MODE_A2, 1000, +-- +2.11.0 + diff --git a/patches.drivers/0045-phy-rockchip-typec-Avoid-magic-numbers-add-delays-in.patch b/patches.drivers/0045-phy-rockchip-typec-Avoid-magic-numbers-add-delays-in.patch new file mode 100644 index 0000000..d55a97d --- /dev/null +++ b/patches.drivers/0045-phy-rockchip-typec-Avoid-magic-numbers-add-delays-in.patch @@ -0,0 +1,381 @@ +From 61b6c8636439323274d4aa80855d28d79b4ba3b3 Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +Date: Fri, 22 Sep 2017 09:44:05 -0700 +Subject: [PATCH 45/86] phy: rockchip-typec: Avoid magic numbers + add delays + in aux calib + +Git-commit: f85fd4c909565892a49337ef482090e446c4e5bd +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +NOTE: nothing is known to be fixed by this change, but it does enforce +some delays that are documented to be necessary. Possibly this could +fix some corner cases. + +The function tcphy_dp_aux_calibration(), like most of the functions in +the type C PHY, is mostly undocumented and filled with mysterious, +hardcoded numbers. + +Let's attempt to try to document some of these numbers and clean the +function up a little bit. Here's the actual cleanup that happened +here: + +1. All magic numbers were replaced with bit definitions. + +2. For registers that we modify multiple times I now keep track of the + value of the register rather than randomly doing a + read/modify/write or just hardcoding a new number based on knowing + what the old number was. + +3. Delay 10 ms (vs 1 ms) after writing the calibration code. No idea + if this is important but it matches the example in the docs. + +4. Whenever setting a "delayed" version of a signal always put an + explicit delay in the code. No known problems were seen without + this delay but it seems wise to have it. Whenever a delay of "at + least 100 ns" was specified I used a delay of 1 us. + +5. Added comments to some of the bits of code. + +6. Removed duplicate setting of TX_ANA_CTRL_REG_5 (to 0) + +7. Moved setting of TX_ANA_CTRL_REG_3 to the same place it was in the + sample code. Note that TX_ANA_CTRL_REG_3 ought to be initted to 0 + (and elsewhere we assume that we just got a reset), but it seems + fine to be explicit. + +8. Treats the calibration code as a 7-bit two's complement number. + This isn't strictly required, but seems slightly cleaner. The docs + say "treat this as a two's complement number, but it should never + be negative". If we ever read the "adjustment" codes as documented + then perhaps the two's complement bit will matter more. + +There are still a few weird / mysterious things around aux init and +this doesn't attempt to fix all of them. Mostly it's aimed at doing +changes that should be _very_ safe and add a lot of clarity. Things +specifically not done: + +A) Resolve the fact that some registers are read/modify/write and + others are explicitly initted to a value. We always call + tcphy_dp_aux_calibration() right after resetting the PHY so it's + probably not critical, but it's a little weird that the code is + inconsistent. + +B) Fully resolve the documented init sequence with the current one. + We still have a few mystery steps and we also leave out turning on + TXDA_DRV_LDO_BG_FB_EN and TXDA_DRV_LDO_BG_REF_EN, which is in the + sample code. + +C) Clean things up to read all the bits of the calibration code. This + will hopefully come in a followup change. + +This also doesn't attempt to document any of the other parts of the +PHY--just the aux init which is all I got docs for. + +Reviewed-by: Chris Zhong +Signed-off-by: Douglas Anderson +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 202 ++++++++++++++++++++++++------ + 1 file changed, 163 insertions(+), 39 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index a958c9bced01..e5454eccb57d 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -102,9 +102,40 @@ + #define CMN_PLL1_SS_CTRL1 (0xb8 << 2) + #define CMN_PLL1_SS_CTRL2 (0xb9 << 2) + #define CMN_RXCAL_OVRD (0xd1 << 2) ++ + #define CMN_TXPUCAL_CTRL (0xe0 << 2) + #define CMN_TXPUCAL_OVRD (0xe1 << 2) ++#define CMN_TXPDCAL_CTRL (0xf0 << 2) + #define CMN_TXPDCAL_OVRD (0xf1 << 2) ++ ++/* For CMN_TXPUCAL_CTRL, CMN_TXPDCAL_CTRL */ ++#define CMN_TXPXCAL_START BIT(15) ++#define CMN_TXPXCAL_DONE BIT(14) ++#define CMN_TXPXCAL_NO_RESPONSE BIT(13) ++#define CMN_TXPXCAL_CURRENT_RESPONSE BIT(12) ++ ++#define CMN_TXPU_ADJ_CTRL (0x108 << 2) ++#define CMN_TXPD_ADJ_CTRL (0x10c << 2) ++ ++/* ++ * For CMN_TXPUCAL_CTRL, CMN_TXPDCAL_CTRL, ++ * CMN_TXPU_ADJ_CTRL, CMN_TXPDCAL_CTRL ++ * ++ * NOTE: some of these registers are documented to be 2's complement ++ * signed numbers, but then documented to be always positive. Weird. ++ * In such a case, using CMN_CALIB_CODE_POS() avoids the unnecessary ++ * sign extension. ++ */ ++#define CMN_CALIB_CODE_WIDTH 7 ++#define CMN_CALIB_CODE_OFFSET 0 ++#define CMN_CALIB_CODE_MASK GENMASK(CMN_CALIB_CODE_WIDTH, 0) ++#define CMN_CALIB_CODE(x) \ ++ sign_extend32((x) >> CMN_CALIB_CODE_OFFSET, CMN_CALIB_CODE_WIDTH) ++ ++#define CMN_CALIB_CODE_POS_MASK GENMASK(CMN_CALIB_CODE_WIDTH - 1, 0) ++#define CMN_CALIB_CODE_POS(x) \ ++ (((x) >> CMN_CALIB_CODE_OFFSET) & CMN_CALIB_CODE_POS_MASK) ++ + #define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2) + #define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2) + #define CMN_DIAG_PLL0_OVRD (0x1c2 << 2) +@@ -138,6 +169,15 @@ + #define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2) + #define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2) + #define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNLS_MULT_000(n) ((0x4058 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNLS_MULT_001(n) ((0x4059 | ((n) << 9)) << 2) ++#define TX_TXCC_MGNLS_MULT_010(n) ((0x405a | ((n) << 9)) << 2) ++#define TX_TXCC_MGNLS_MULT_011(n) ((0x405b | ((n) << 9)) << 2) ++#define TX_TXCC_MGNLS_MULT_100(n) ((0x405c | ((n) << 9)) << 2) ++#define TX_TXCC_MGNLS_MULT_101(n) ((0x405d | ((n) << 9)) << 2) ++#define TX_TXCC_MGNLS_MULT_110(n) ((0x405e | ((n) << 9)) << 2) ++#define TX_TXCC_MGNLS_MULT_111(n) ((0x405f | ((n) << 9)) << 2) ++ + #define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2) + #define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2) + #define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2) +@@ -150,10 +190,63 @@ + #define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2) + #define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2) + #define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2) ++ ++/* Use this for "n" in macros like "_MULT_XXX" to target the aux channel */ ++#define AUX_CH_LANE 8 ++ + #define TX_ANA_CTRL_REG_1 (0x5020 << 2) ++ ++#define TXDA_DP_AUX_EN BIT(15) ++#define AUXDA_SE_EN BIT(14) ++#define TXDA_CAL_LATCH_EN BIT(13) ++#define AUXDA_POLARITY BIT(12) ++#define TXDA_DRV_POWER_ISOLATION_EN BIT(11) ++#define TXDA_DRV_POWER_EN_PH_2_N BIT(10) ++#define TXDA_DRV_POWER_EN_PH_1_N BIT(9) ++#define TXDA_BGREF_EN BIT(8) ++#define TXDA_DRV_LDO_EN BIT(7) ++#define TXDA_DECAP_EN_DEL BIT(6) ++#define TXDA_DECAP_EN BIT(5) ++#define TXDA_UPHY_SUPPLY_EN_DEL BIT(4) ++#define TXDA_UPHY_SUPPLY_EN BIT(3) ++#define TXDA_LOW_LEAKAGE_EN BIT(2) ++#define TXDA_DRV_IDLE_LOWI_EN BIT(1) ++#define TXDA_DRV_CMN_MODE_EN BIT(0) ++ + #define TX_ANA_CTRL_REG_2 (0x5021 << 2) ++ ++#define AUXDA_DEBOUNCING_CLK BIT(15) ++#define TXDA_LPBK_RECOVERED_CLK_EN BIT(14) ++#define TXDA_LPBK_ISI_GEN_EN BIT(13) ++#define TXDA_LPBK_SERIAL_EN BIT(12) ++#define TXDA_LPBK_LINE_EN BIT(11) ++#define TXDA_DRV_LDO_REDC_SINKIQ BIT(10) ++#define XCVR_DECAP_EN_DEL BIT(9) ++#define XCVR_DECAP_EN BIT(8) ++#define TXDA_MPHY_ENABLE_HS_NT BIT(7) ++#define TXDA_MPHY_SA_MODE BIT(6) ++#define TXDA_DRV_LDO_RBYR_FB_EN BIT(5) ++#define TXDA_DRV_RST_PULL_DOWN BIT(4) ++#define TXDA_DRV_LDO_BG_FB_EN BIT(3) ++#define TXDA_DRV_LDO_BG_REF_EN BIT(2) ++#define TXDA_DRV_PREDRV_EN_DEL BIT(1) ++#define TXDA_DRV_PREDRV_EN BIT(0) ++ + #define TXDA_COEFF_CALC_CTRL (0x5022 << 2) ++ ++#define TX_HIGH_Z BIT(6) ++#define TX_VMARGIN_OFFSET 3 ++#define TX_VMARGIN_MASK 0x7 ++#define LOW_POWER_SWING_EN BIT(2) ++#define TX_FCM_DRV_MAIN_EN BIT(1) ++#define TX_FCM_FULL_MARGIN BIT(0) ++ + #define TX_DIG_CTRL_REG_2 (0x5024 << 2) ++ ++#define TX_HIGH_Z_TM_EN BIT(15) ++#define TX_RESCAL_CODE_OFFSET 0 ++#define TX_RESCAL_CODE_MASK 0x3f ++ + #define TXDA_CYA_AUXDA_CYA (0x5025 << 2) + #define TX_ANA_CTRL_REG_3 (0x5026 << 2) + #define TX_ANA_CTRL_REG_4 (0x5027 << 2) +@@ -456,54 +549,63 @@ static void tcphy_dp_aux_set_flip(struct rockchip_typec_phy *tcphy) + */ + tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); + if (!tcphy->flip) +- tx_ana_ctrl_reg_1 |= BIT(12); ++ tx_ana_ctrl_reg_1 |= AUXDA_POLARITY; + else +- tx_ana_ctrl_reg_1 &= ~BIT(12); ++ tx_ana_ctrl_reg_1 &= ~AUXDA_POLARITY; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + } + + static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + { ++ u16 val; + u16 tx_ana_ctrl_reg_1; +- u16 rdata, rdata2, val; ++ u16 tx_ana_ctrl_reg_2; ++ s32 pu_calib_code; + + /* disable txda_cal_latch_en for rewrite the calibration values */ + tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); +- tx_ana_ctrl_reg_1 &= ~BIT(13); ++ tx_ana_ctrl_reg_1 &= ~TXDA_CAL_LATCH_EN; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + + /* +- * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and +- * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it +- * works. ++ * read a resistor calibration code from CMN_TXPUCAL_CTRL[5:0] and ++ * write it to TX_DIG_CTRL_REG_2[5:0]. + */ +- rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2); +- rdata = rdata & 0xffc0; +- +- rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL); +- rdata2 = rdata2 & 0x3f; ++ val = readl(tcphy->base + CMN_TXPUCAL_CTRL); ++ pu_calib_code = CMN_CALIB_CODE_POS(val); + +- val = rdata | rdata2; ++ /* write the calibration, then delay 10 ms as sample in docs */ ++ val = readl(tcphy->base + TX_DIG_CTRL_REG_2); ++ val &= ~(TX_RESCAL_CODE_MASK << TX_RESCAL_CODE_OFFSET); ++ val |= pu_calib_code << TX_RESCAL_CODE_OFFSET; + writel(val, tcphy->base + TX_DIG_CTRL_REG_2); +- usleep_range(1000, 1050); ++ usleep_range(10000, 10050); + + /* + * Enable signal for latch that sample and holds calibration values. + * Activate this signal for 1 clock cycle to sample new calibration + * values. + */ +- tx_ana_ctrl_reg_1 |= BIT(13); ++ tx_ana_ctrl_reg_1 |= TXDA_CAL_LATCH_EN; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + usleep_range(150, 200); + + /* set TX Voltage Level and TX Deemphasis to 0 */ + writel(0, tcphy->base + PHY_DP_TX_CTL); ++ + /* re-enable decap */ +- writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2); +- writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2); +- tx_ana_ctrl_reg_1 |= BIT(3); ++ tx_ana_ctrl_reg_2 = XCVR_DECAP_EN; ++ writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2); ++ udelay(1); ++ tx_ana_ctrl_reg_2 |= XCVR_DECAP_EN_DEL; ++ writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2); ++ ++ writel(0, tcphy->base + TX_ANA_CTRL_REG_3); ++ ++ tx_ana_ctrl_reg_1 |= TXDA_UPHY_SUPPLY_EN; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); +- tx_ana_ctrl_reg_1 |= BIT(4); ++ udelay(1); ++ tx_ana_ctrl_reg_1 |= TXDA_UPHY_SUPPLY_EN_DEL; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + + writel(0, tcphy->base + TX_ANA_CTRL_REG_5); +@@ -515,44 +617,66 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4); + + /* re-enables Bandgap reference for LDO */ +- tx_ana_ctrl_reg_1 |= BIT(7); ++ tx_ana_ctrl_reg_1 |= TXDA_DRV_LDO_EN; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); +- tx_ana_ctrl_reg_1 |= BIT(8); ++ udelay(5); ++ tx_ana_ctrl_reg_1 |= TXDA_BGREF_EN; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + + /* + * re-enables the transmitter pre-driver, driver data selection MUX, + * and receiver detect circuits. + */ +- writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2); +- writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2); ++ tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN; ++ writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2); ++ udelay(1); ++ tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN_DEL; ++ writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2); + + /* +- * Do some magic undocumented stuff, some of which appears to +- * undo the "re-enables Bandgap reference for LDO" above. ++ * Do all the undocumented magic: ++ * - Turn on TXDA_DP_AUX_EN, whatever that is, even though sample ++ * never shows this going on. ++ * - Turn on TXDA_DECAP_EN (and TXDA_DECAP_EN_DEL) even though ++ * docs say for aux it's always 0. ++ * - Turn off the LDO and BGREF, which we just spent time turning ++ * on above (???). ++ * ++ * Without this magic, things seem worse. + */ +- tx_ana_ctrl_reg_1 |= BIT(15); +- tx_ana_ctrl_reg_1 &= ~BIT(8); +- tx_ana_ctrl_reg_1 &= ~BIT(7); +- tx_ana_ctrl_reg_1 |= BIT(6); +- tx_ana_ctrl_reg_1 |= BIT(5); ++ tx_ana_ctrl_reg_1 |= TXDA_DP_AUX_EN; ++ tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN; ++ tx_ana_ctrl_reg_1 &= ~TXDA_DRV_LDO_EN; ++ tx_ana_ctrl_reg_1 &= ~TXDA_BGREF_EN; ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); ++ udelay(1); ++ tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN_DEL; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); +- +- writel(0, tcphy->base + TX_ANA_CTRL_REG_3); +- writel(0, tcphy->base + TX_ANA_CTRL_REG_4); +- writel(0, tcphy->base + TX_ANA_CTRL_REG_5); + + /* +- * Controls low_power_swing_en, don't set the voltage swing of the +- * driver to 400mv. The values below are peak to peak (differential) +- * values. ++ * Undo the work we did to set the LDO voltage. ++ * This doesn't seem to help nor hurt, but it kinda goes with the ++ * undocumented magic above. + */ ++ writel(0, tcphy->base + TX_ANA_CTRL_REG_4); ++ ++ /* Don't set voltage swing to 400 mV peak to peak (differential) */ + writel(0, tcphy->base + TXDA_COEFF_CALC_CTRL); ++ ++ /* Init TXDA_CYA_AUXDA_CYA for unknown magic reasons */ + writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA); + +- /* Controls tx_high_z_tm_en */ ++ /* ++ * More undocumented magic, presumably the goal of which is to ++ * make the "auxda_source_aux_oen" be ignored and instead to decide ++ * about "high impedance state" based on what software puts in the ++ * register TXDA_COEFF_CALC_CTRL (see TX_HIGH_Z). Since we only ++ * program that register once and we don't set the bit TX_HIGH_Z, ++ * presumably the goal here is that we should never put the analog ++ * driver in high impedance state. ++ */ + val = readl(tcphy->base + TX_DIG_CTRL_REG_2); +- val |= BIT(15); ++ val |= TX_HIGH_Z_TM_EN; + writel(val, tcphy->base + TX_DIG_CTRL_REG_2); + } + +-- +2.11.0 + diff --git a/patches.drivers/0046-phy-rockchip-typec-Do-the-calibration-more-correctly.patch b/patches.drivers/0046-phy-rockchip-typec-Do-the-calibration-more-correctly.patch new file mode 100644 index 0000000..d5147d3 --- /dev/null +++ b/patches.drivers/0046-phy-rockchip-typec-Do-the-calibration-more-correctly.patch @@ -0,0 +1,77 @@ +From 9fe73155dccde51fca4072700c11145f683dc832 Mon Sep 17 00:00:00 2001 +From: Douglas Anderson +Date: Fri, 22 Sep 2017 09:44:06 -0700 +Subject: [PATCH 46/86] phy: rockchip-typec: Do the calibration more correctly + +Git-commit: e023b1fb5286e23c224f2ae312831a4fce258c02 +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +Calculate the calibration code as per the docs. The docs talk about +reading and averaging the pullup and pulldown calibration codes. They +also talk about adding in some adjustment codes. Let's do what the +docs say. + +In practice this doesn't seem to matter a whole lot. On a device I +tested the pullup and pulldown codes were nearly the same (0x23 and +0x24) and the adjustment codes were 0. + +Reviewed-by: Chris Zhong +Signed-off-by: Douglas Anderson +Signed-off-by: Kishon Vijay Abraham I +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/phy/rockchip/phy-rockchip-typec.c | 27 ++++++++++++++++++--------- + 1 file changed, 18 insertions(+), 9 deletions(-) + +diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c +index e5454eccb57d..ee85fa0ca4b0 100644 +--- a/drivers/phy/rockchip/phy-rockchip-typec.c ++++ b/drivers/phy/rockchip/phy-rockchip-typec.c +@@ -560,24 +560,33 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) + u16 val; + u16 tx_ana_ctrl_reg_1; + u16 tx_ana_ctrl_reg_2; +- s32 pu_calib_code; +- +- /* disable txda_cal_latch_en for rewrite the calibration values */ +- tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); +- tx_ana_ctrl_reg_1 &= ~TXDA_CAL_LATCH_EN; +- writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); ++ s32 pu_calib_code, pd_calib_code; ++ s32 pu_adj, pd_adj; ++ u16 calib; + + /* +- * read a resistor calibration code from CMN_TXPUCAL_CTRL[5:0] and +- * write it to TX_DIG_CTRL_REG_2[5:0]. ++ * Calculate calibration code as per docs: use an average of the ++ * pull down and pull up. Then add in adjustments. + */ + val = readl(tcphy->base + CMN_TXPUCAL_CTRL); + pu_calib_code = CMN_CALIB_CODE_POS(val); ++ val = readl(tcphy->base + CMN_TXPDCAL_CTRL); ++ pd_calib_code = CMN_CALIB_CODE_POS(val); ++ val = readl(tcphy->base + CMN_TXPU_ADJ_CTRL); ++ pu_adj = CMN_CALIB_CODE(val); ++ val = readl(tcphy->base + CMN_TXPD_ADJ_CTRL); ++ pd_adj = CMN_CALIB_CODE(val); ++ calib = (pu_calib_code + pd_calib_code) / 2 + pu_adj + pd_adj; ++ ++ /* disable txda_cal_latch_en for rewrite the calibration values */ ++ tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); ++ tx_ana_ctrl_reg_1 &= ~TXDA_CAL_LATCH_EN; ++ writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + + /* write the calibration, then delay 10 ms as sample in docs */ + val = readl(tcphy->base + TX_DIG_CTRL_REG_2); + val &= ~(TX_RESCAL_CODE_MASK << TX_RESCAL_CODE_OFFSET); +- val |= pu_calib_code << TX_RESCAL_CODE_OFFSET; ++ val |= calib << TX_RESCAL_CODE_OFFSET; + writel(val, tcphy->base + TX_DIG_CTRL_REG_2); + usleep_range(10000, 10050); + +-- +2.11.0 + diff --git a/patches.drivers/0047-pwm-rockchip-Add-APB-and-function-both-clocks-suppor.patch b/patches.drivers/0047-pwm-rockchip-Add-APB-and-function-both-clocks-suppor.patch new file mode 100644 index 0000000..0207abd --- /dev/null +++ b/patches.drivers/0047-pwm-rockchip-Add-APB-and-function-both-clocks-suppor.patch @@ -0,0 +1,177 @@ +From 278265d3fd7b2530195c2b5e976ac2dc6cdbdbb5 Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Tue, 8 Aug 2017 23:38:29 +0800 +Subject: [PATCH 47/86] pwm: rockchip: Add APB and function both clocks support + +Git-commit: 27922ff59893e3445f69e397bcd92ae06fa89ca7 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +New PWM module provides two individual clocks for APB clock and function +clock. + +Signed-off-by: David Wu +Acked-by: Rob Herring +Signed-off-by: Thierry Reding +Signed-off-by: Mian Yousaf Kaukab +--- + .../devicetree/bindings/pwm/pwm-rockchip.txt | 8 ++- + drivers/pwm/pwm-rockchip.c | 58 ++++++++++++++++++---- + 2 files changed, 56 insertions(+), 10 deletions(-) + +diff --git a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt +index b8be3d09ee26..2350ef918bef 100644 +--- a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt ++++ b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt +@@ -6,7 +6,13 @@ Required properties: + "rockchip,rk3288-pwm": found on RK3288 SoC + "rockchip,vop-pwm": found integrated in VOP on RK3288 SoC + - reg: physical base address and length of the controller's registers +- - clocks: phandle and clock specifier of the PWM reference clock ++ - clocks: See ../clock/clock-bindings.txt ++ - For older hardware (rk2928, rk3066, rk3188, rk3228, rk3288, rk3399): ++ - There is one clock that's used both to derive the functional clock ++ for the device and as the bus clock. ++ - For newer hardware (rk3328 and future socs): specified by name ++ - "pwm": This is used to derive the functional clock. ++ - "pclk": This is the APB bus clock. + - #pwm-cells: must be 2 (rk2928) or 3 (rk3288). See pwm.txt in this directory + for a description of the cell format. + +diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c +index 744d56197286..ac3cd5ec5310 100644 +--- a/drivers/pwm/pwm-rockchip.c ++++ b/drivers/pwm/pwm-rockchip.c +@@ -33,6 +33,7 @@ + struct rockchip_pwm_chip { + struct pwm_chip chip; + struct clk *clk; ++ struct clk *pclk; + const struct rockchip_pwm_data *data; + void __iomem *base; + }; +@@ -145,7 +146,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + u64 tmp; + int ret; + +- ret = clk_enable(pc->clk); ++ ret = clk_enable(pc->pclk); + if (ret) + return; + +@@ -161,7 +162,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + + pc->data->get_state(chip, pwm, state); + +- clk_disable(pc->clk); ++ clk_disable(pc->pclk); + } + + static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, +@@ -224,7 +225,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + +- ret = clk_enable(pc->clk); ++ ret = clk_enable(pc->pclk); + if (ret) + return ret; + +@@ -257,7 +258,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + rockchip_pwm_get_state(chip, pwm, state); + + out: +- clk_disable(pc->clk); ++ clk_disable(pc->pclk); + + return ret; + } +@@ -328,7 +329,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + const struct of_device_id *id; + struct rockchip_pwm_chip *pc; + struct resource *r; +- int ret; ++ int ret, count; + + id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); + if (!id) +@@ -343,13 +344,43 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + if (IS_ERR(pc->base)) + return PTR_ERR(pc->base); + +- pc->clk = devm_clk_get(&pdev->dev, NULL); +- if (IS_ERR(pc->clk)) +- return PTR_ERR(pc->clk); ++ pc->clk = devm_clk_get(&pdev->dev, "pwm"); ++ if (IS_ERR(pc->clk)) { ++ pc->clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(pc->clk)) { ++ ret = PTR_ERR(pc->clk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Can't get bus clk: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ count = of_count_phandle_with_args(pdev->dev.of_node, ++ "clocks", "#clock-cells"); ++ if (count == 2) ++ pc->pclk = devm_clk_get(&pdev->dev, "pclk"); ++ else ++ pc->pclk = pc->clk; ++ ++ if (IS_ERR(pc->pclk)) { ++ ret = PTR_ERR(pc->pclk); ++ if (ret != -EPROBE_DEFER) ++ dev_err(&pdev->dev, "Can't get APB clk: %d\n", ret); ++ return ret; ++ } + + ret = clk_prepare_enable(pc->clk); +- if (ret) ++ if (ret) { ++ dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret); + return ret; ++ } ++ ++ ret = clk_prepare(pc->pclk); ++ if (ret) { ++ dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret); ++ goto err_clk; ++ } + + platform_set_drvdata(pdev, pc); + +@@ -368,12 +399,20 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + if (ret < 0) { + clk_unprepare(pc->clk); + dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); ++ goto err_pclk; + } + + /* Keep the PWM clk enabled if the PWM appears to be up and running. */ + if (!pwm_is_enabled(pc->chip.pwms)) + clk_disable(pc->clk); + ++ return 0; ++ ++err_pclk: ++ clk_unprepare(pc->pclk); ++err_clk: ++ clk_disable_unprepare(pc->clk); ++ + return ret; + } + +@@ -395,6 +434,7 @@ static int rockchip_pwm_remove(struct platform_device *pdev) + if (pwm_is_enabled(pc->chip.pwms)) + clk_disable(pc->clk); + ++ clk_unprepare(pc->pclk); + clk_unprepare(pc->clk); + + return pwmchip_remove(&pc->chip); +-- +2.11.0 + diff --git a/patches.drivers/0048-pwm-rockchip-Remove-the-judge-from-return-value-of-p.patch b/patches.drivers/0048-pwm-rockchip-Remove-the-judge-from-return-value-of-p.patch new file mode 100644 index 0000000..50b586a --- /dev/null +++ b/patches.drivers/0048-pwm-rockchip-Remove-the-judge-from-return-value-of-p.patch @@ -0,0 +1,61 @@ +From 9e1cdff825e92f6d9a2668f68527c2bdd68da945 Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Tue, 8 Aug 2017 23:38:30 +0800 +Subject: [PATCH 48/86] pwm: rockchip: Remove the judge from return value of + pwm_config() + +Git-commit: f90df9cda68d774c600860e32602b04c741bf95c +Patch-mainline: v4.14-rc1 +References: fate#323912 + +It seems the rockchip_pwm_config() always returns the result 0, so +remove the judge. + +Signed-off-by: David Wu +Acked-by: Boris Brezillon +Signed-off-by: Thierry Reding +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pwm/pwm-rockchip.c | 12 ++---------- + 1 file changed, 2 insertions(+), 10 deletions(-) + +diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c +index ac3cd5ec5310..33bbb5a97b72 100644 +--- a/drivers/pwm/pwm-rockchip.c ++++ b/drivers/pwm/pwm-rockchip.c +@@ -165,7 +165,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + clk_disable(pc->pclk); + } + +-static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ++static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); +@@ -188,8 +188,6 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + + writel(period, pc->base + pc->data->regs.period); + writel(duty, pc->base + pc->data->regs.duty); +- +- return 0; + } + + static int rockchip_pwm_enable(struct pwm_chip *chip, +@@ -236,13 +234,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + enabled = false; + } + +- ret = rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); +- if (ret) { +- if (enabled != curstate.enabled) +- rockchip_pwm_enable(chip, pwm, !enabled, +- state->polarity); +- goto out; +- } ++ rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); + + if (state->enabled != enabled) { + ret = rockchip_pwm_enable(chip, pwm, state->enabled, +-- +2.11.0 + diff --git a/patches.drivers/0049-pwm-rockchip-Use-pwm_apply-instead-of-pwm_enable.patch b/patches.drivers/0049-pwm-rockchip-Use-pwm_apply-instead-of-pwm_enable.patch new file mode 100644 index 0000000..52eaeb4 --- /dev/null +++ b/patches.drivers/0049-pwm-rockchip-Use-pwm_apply-instead-of-pwm_enable.patch @@ -0,0 +1,256 @@ +From 51068d7794fcff3392f0d1364795553cc09b6ba2 Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Tue, 8 Aug 2017 23:38:31 +0800 +Subject: [PATCH 49/86] pwm: rockchip: Use pwm_apply() instead of pwm_enable() + +Git-commit: ed054693d77f9c98da18f7c3ff19dfa41692520f +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Drop the custom hook of pwm_enable() and implement pwm_apply_v1() and +pwm_apply_v2() instead. + +Signed-off-by: David Wu +Signed-off-by: Thierry Reding +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pwm/pwm-rockchip.c | 141 +++++++++++++++++++++++++-------------------- + 1 file changed, 78 insertions(+), 63 deletions(-) + +diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c +index 33bbb5a97b72..4f7ebe132ee9 100644 +--- a/drivers/pwm/pwm-rockchip.c ++++ b/drivers/pwm/pwm-rockchip.c +@@ -51,11 +51,10 @@ struct rockchip_pwm_data { + bool supports_polarity; + const struct pwm_ops *ops; + +- void (*set_enable)(struct pwm_chip *chip, +- struct pwm_device *pwm, bool enable, +- enum pwm_polarity polarity); + void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state); ++ int (*pwm_apply)(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state); + }; + + static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) +@@ -63,24 +62,6 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) + return container_of(c, struct rockchip_pwm_chip, chip); + } + +-static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, +- struct pwm_device *pwm, bool enable, +- enum pwm_polarity polarity) +-{ +- struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); +- u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; +- u32 val; +- +- val = readl_relaxed(pc->base + pc->data->regs.ctrl); +- +- if (enable) +- val |= enable_conf; +- else +- val &= ~enable_conf; +- +- writel_relaxed(val, pc->base + pc->data->regs.ctrl); +-} +- + static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) +@@ -94,30 +75,6 @@ static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, + state->enabled = true; + } + +-static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, +- struct pwm_device *pwm, bool enable, +- enum pwm_polarity polarity) +-{ +- struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); +- u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | +- PWM_CONTINUOUS; +- u32 val; +- +- if (polarity == PWM_POLARITY_INVERSED) +- enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; +- else +- enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; +- +- val = readl_relaxed(pc->base + pc->data->regs.ctrl); +- +- if (enable) +- val |= enable_conf; +- else +- val &= ~enable_conf; +- +- writel_relaxed(val, pc->base + pc->data->regs.ctrl); +-} +- + static void rockchip_pwm_get_state_v2(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) +@@ -193,10 +150,12 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + static int rockchip_pwm_enable(struct pwm_chip *chip, + struct pwm_device *pwm, + bool enable, +- enum pwm_polarity polarity) ++ enum pwm_polarity polarity, ++ u32 enable_conf) + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + int ret; ++ u32 val; + + if (enable) { + ret = clk_enable(pc->clk); +@@ -204,7 +163,23 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, + return ret; + } + +- pc->data->set_enable(chip, pwm, enable, polarity); ++ if (pc->data->supports_polarity) { ++ if (polarity == PWM_POLARITY_INVERSED) ++ enable_conf |= PWM_DUTY_NEGATIVE | ++ PWM_INACTIVE_POSITIVE; ++ else ++ enable_conf |= PWM_DUTY_POSITIVE | ++ PWM_INACTIVE_NEGATIVE; ++ } ++ ++ val = readl_relaxed(pc->base + pc->data->regs.ctrl); ++ ++ if (enable) ++ val |= enable_conf; ++ else ++ val &= ~enable_conf; ++ ++ writel_relaxed(val, pc->base + pc->data->regs.ctrl); + + if (!enable) + clk_disable(pc->clk); +@@ -212,37 +187,77 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, + return 0; + } + +-static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, +- struct pwm_state *state) ++static int rockchip_pwm_apply_v1(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) + { +- struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); ++ u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; + struct pwm_state curstate; + bool enabled; +- int ret; ++ int ret = 0; + + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + +- ret = clk_enable(pc->pclk); +- if (ret) +- return ret; +- + if (state->polarity != curstate.polarity && enabled) { +- ret = rockchip_pwm_enable(chip, pwm, false, state->polarity); ++ ret = rockchip_pwm_enable(chip, pwm, false, state->polarity, ++ enable_conf); + if (ret) +- goto out; ++ return ret; + enabled = false; + } + + rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); + +- if (state->enabled != enabled) { ++ if (state->enabled != enabled) + ret = rockchip_pwm_enable(chip, pwm, state->enabled, +- state->polarity); ++ state->polarity, enable_conf); ++ ++ return ret; ++} ++ ++static int rockchip_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | ++ PWM_CONTINUOUS; ++ struct pwm_state curstate; ++ bool enabled; ++ int ret = 0; ++ ++ pwm_get_state(pwm, &curstate); ++ enabled = curstate.enabled; ++ ++ if (state->polarity != curstate.polarity && enabled) { ++ ret = rockchip_pwm_enable(chip, pwm, false, state->polarity, ++ enable_conf); + if (ret) +- goto out; ++ return ret; ++ enabled = false; + } + ++ rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); ++ ++ if (state->enabled != enabled) ++ ret = rockchip_pwm_enable(chip, pwm, state->enabled, ++ state->polarity, enable_conf); ++ ++ return ret; ++} ++ ++static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) ++{ ++ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); ++ int ret; ++ ++ ret = clk_enable(pc->pclk); ++ if (ret) ++ return ret; ++ ++ ret = pc->data->pwm_apply(chip, pwm, state); ++ if (ret) ++ goto out; ++ + /* + * Update the state with the real hardware, which can differ a bit + * because of period/duty_cycle approximation. +@@ -276,8 +291,8 @@ static const struct rockchip_pwm_data pwm_data_v1 = { + }, + .prescaler = 2, + .ops = &rockchip_pwm_ops_v1, +- .set_enable = rockchip_pwm_set_enable_v1, + .get_state = rockchip_pwm_get_state_v1, ++ .pwm_apply = rockchip_pwm_apply_v1, + }; + + static const struct rockchip_pwm_data pwm_data_v2 = { +@@ -290,8 +305,8 @@ static const struct rockchip_pwm_data pwm_data_v2 = { + .prescaler = 1, + .supports_polarity = true, + .ops = &rockchip_pwm_ops_v2, +- .set_enable = rockchip_pwm_set_enable_v2, + .get_state = rockchip_pwm_get_state_v2, ++ .pwm_apply = rockchip_pwm_apply_v2, + }; + + static const struct rockchip_pwm_data pwm_data_vop = { +@@ -304,8 +319,8 @@ static const struct rockchip_pwm_data pwm_data_vop = { + .prescaler = 1, + .supports_polarity = true, + .ops = &rockchip_pwm_ops_v2, +- .set_enable = rockchip_pwm_set_enable_v2, + .get_state = rockchip_pwm_get_state_v2, ++ .pwm_apply = rockchip_pwm_apply_v2, + }; + + static const struct of_device_id rockchip_pwm_dt_ids[] = { +-- +2.11.0 + diff --git a/patches.drivers/0050-pwm-rockchip-Move-the-configuration-of-polarity.patch b/patches.drivers/0050-pwm-rockchip-Move-the-configuration-of-polarity.patch new file mode 100644 index 0000000..b2ced63 --- /dev/null +++ b/patches.drivers/0050-pwm-rockchip-Move-the-configuration-of-polarity.patch @@ -0,0 +1,148 @@ +From 80cbc39a996ef5c62e198c24c0cd920c55896675 Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Tue, 8 Aug 2017 23:38:32 +0800 +Subject: [PATCH 50/86] pwm: rockchip: Move the configuration of polarity + +Git-commit: bc834d7b07b4e57c89607c929dcb5eabb17b47a7 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +It is usually possible to configure the polarity, cycle and duty all at +once, so that the polarity and cycle and duty are applied atomically. +Move it from rockchip_pwm_set_enable() into rockchip_pwm_config(), as +well as prepare for the next atomic update commit. + +Signed-off-by: David Wu +Signed-off-by: Thierry Reding +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pwm/pwm-rockchip.c | 48 +++++++++++++++++++++++----------------------- + 1 file changed, 24 insertions(+), 24 deletions(-) + +diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c +index 4f7ebe132ee9..911329a15da0 100644 +--- a/drivers/pwm/pwm-rockchip.c ++++ b/drivers/pwm/pwm-rockchip.c +@@ -27,6 +27,7 @@ + #define PWM_DUTY_NEGATIVE (0 << 3) + #define PWM_INACTIVE_NEGATIVE (0 << 4) + #define PWM_INACTIVE_POSITIVE (1 << 4) ++#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) + #define PWM_OUTPUT_LEFT (0 << 5) + #define PWM_LP_DISABLE (0 << 8) + +@@ -123,11 +124,12 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + } + + static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, +- int duty_ns, int period_ns) ++ struct pwm_state *state) + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + unsigned long period, duty; + u64 clk_rate, div; ++ u32 ctrl; + + clk_rate = clk_get_rate(pc->clk); + +@@ -136,22 +138,31 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + * bits, every possible input period can be obtained using the + * default prescaler value for all practical clock rate values. + */ +- div = clk_rate * period_ns; ++ div = clk_rate * state->period; + period = DIV_ROUND_CLOSEST_ULL(div, + pc->data->prescaler * NSEC_PER_SEC); + +- div = clk_rate * duty_ns; ++ div = clk_rate * state->duty_cycle; + duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); + + writel(period, pc->base + pc->data->regs.period); + writel(duty, pc->base + pc->data->regs.duty); ++ ++ ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); ++ if (pc->data->supports_polarity) { ++ ctrl &= ~PWM_POLARITY_MASK; ++ if (state->polarity == PWM_POLARITY_INVERSED) ++ ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; ++ else ++ ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; ++ } ++ writel(ctrl, pc->base + pc->data->regs.ctrl); + } + + static int rockchip_pwm_enable(struct pwm_chip *chip, +- struct pwm_device *pwm, +- bool enable, +- enum pwm_polarity polarity, +- u32 enable_conf) ++ struct pwm_device *pwm, ++ bool enable, ++ u32 enable_conf) + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + int ret; +@@ -163,15 +174,6 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, + return ret; + } + +- if (pc->data->supports_polarity) { +- if (polarity == PWM_POLARITY_INVERSED) +- enable_conf |= PWM_DUTY_NEGATIVE | +- PWM_INACTIVE_POSITIVE; +- else +- enable_conf |= PWM_DUTY_POSITIVE | +- PWM_INACTIVE_NEGATIVE; +- } +- + val = readl_relaxed(pc->base + pc->data->regs.ctrl); + + if (enable) +@@ -199,18 +201,17 @@ static int rockchip_pwm_apply_v1(struct pwm_chip *chip, struct pwm_device *pwm, + enabled = curstate.enabled; + + if (state->polarity != curstate.polarity && enabled) { +- ret = rockchip_pwm_enable(chip, pwm, false, state->polarity, +- enable_conf); ++ ret = rockchip_pwm_enable(chip, pwm, false, enable_conf); + if (ret) + return ret; + enabled = false; + } + +- rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); ++ rockchip_pwm_config(chip, pwm, state); + + if (state->enabled != enabled) + ret = rockchip_pwm_enable(chip, pwm, state->enabled, +- state->polarity, enable_conf); ++ enable_conf); + + return ret; + } +@@ -228,18 +229,17 @@ static int rockchip_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, + enabled = curstate.enabled; + + if (state->polarity != curstate.polarity && enabled) { +- ret = rockchip_pwm_enable(chip, pwm, false, state->polarity, +- enable_conf); ++ ret = rockchip_pwm_enable(chip, pwm, false, enable_conf); + if (ret) + return ret; + enabled = false; + } + +- rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); ++ rockchip_pwm_config(chip, pwm, state); + + if (state->enabled != enabled) + ret = rockchip_pwm_enable(chip, pwm, state->enabled, +- state->polarity, enable_conf); ++ enable_conf); + + return ret; + } +-- +2.11.0 + diff --git a/patches.drivers/0051-pwm-rockchip-Use-same-PWM-ops-for-each-IP.patch b/patches.drivers/0051-pwm-rockchip-Use-same-PWM-ops-for-each-IP.patch new file mode 100644 index 0000000..b3c2c47 --- /dev/null +++ b/patches.drivers/0051-pwm-rockchip-Use-same-PWM-ops-for-each-IP.patch @@ -0,0 +1,273 @@ +From d5cabdf7a275dbd5d31dc5d83c8b954a996ccd32 Mon Sep 17 00:00:00 2001 +From: David Wu +Date: Tue, 8 Aug 2017 23:41:28 +0800 +Subject: [PATCH 51/86] pwm: rockchip: Use same PWM ops for each IP + +Git-commit: 831b2790507b3aac3213e9f39c714d85b0220098 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Just use the same PWM ops for each IP, and get rid of the ops in struct +rockchip_pwm_data, but still define the three different instances of the +struct to use common interface for each IP. + +Signed-off-by: David Wu +Signed-off-by: Thierry Reding +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/pwm/pwm-rockchip.c | 149 ++++++++++++--------------------------------- + 1 file changed, 38 insertions(+), 111 deletions(-) + +diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c +index 911329a15da0..a3fcb404036d 100644 +--- a/drivers/pwm/pwm-rockchip.c ++++ b/drivers/pwm/pwm-rockchip.c +@@ -50,12 +50,7 @@ struct rockchip_pwm_data { + struct rockchip_pwm_regs regs; + unsigned int prescaler; + bool supports_polarity; +- const struct pwm_ops *ops; +- +- void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, +- struct pwm_state *state); +- int (*pwm_apply)(struct pwm_chip *chip, struct pwm_device *pwm, +- struct pwm_state *state); ++ u32 enable_conf; + }; + + static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) +@@ -63,45 +58,15 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) + return container_of(c, struct rockchip_pwm_chip, chip); + } + +-static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, +- struct pwm_device *pwm, +- struct pwm_state *state) +-{ +- struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); +- u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; +- u32 val; +- +- val = readl_relaxed(pc->base + pc->data->regs.ctrl); +- if ((val & enable_conf) == enable_conf) +- state->enabled = true; +-} +- +-static void rockchip_pwm_get_state_v2(struct pwm_chip *chip, +- struct pwm_device *pwm, +- struct pwm_state *state) +-{ +- struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); +- u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | +- PWM_CONTINUOUS; +- u32 val; +- +- val = readl_relaxed(pc->base + pc->data->regs.ctrl); +- if ((val & enable_conf) != enable_conf) +- return; +- +- state->enabled = true; +- +- if (!(val & PWM_DUTY_POSITIVE)) +- state->polarity = PWM_POLARITY_INVERSED; +-} +- + static void rockchip_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, + struct pwm_state *state) + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); ++ u32 enable_conf = pc->data->enable_conf; + unsigned long clk_rate; + u64 tmp; ++ u32 val; + int ret; + + ret = clk_enable(pc->pclk); +@@ -116,9 +81,20 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, + + tmp = readl_relaxed(pc->base + pc->data->regs.duty); + tmp *= pc->data->prescaler * NSEC_PER_SEC; +- state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); ++ state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + +- pc->data->get_state(chip, pwm, state); ++ val = readl_relaxed(pc->base + pc->data->regs.ctrl); ++ if (pc->data->supports_polarity) ++ state->enabled = ((val & enable_conf) != enable_conf) ? ++ false : true; ++ else ++ state->enabled = ((val & enable_conf) == enable_conf) ? ++ true : false; ++ ++ if (pc->data->supports_polarity) { ++ if (!(val & PWM_DUTY_POSITIVE)) ++ state->polarity = PWM_POLARITY_INVERSED; ++ } + + clk_disable(pc->pclk); + } +@@ -161,10 +137,10 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + + static int rockchip_pwm_enable(struct pwm_chip *chip, + struct pwm_device *pwm, +- bool enable, +- u32 enable_conf) ++ bool enable) + { + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); ++ u32 enable_conf = pc->data->enable_conf; + int ret; + u32 val; + +@@ -189,75 +165,35 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, + return 0; + } + +-static int rockchip_pwm_apply_v1(struct pwm_chip *chip, struct pwm_device *pwm, +- struct pwm_state *state) ++static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ++ struct pwm_state *state) + { +- u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; ++ struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + struct pwm_state curstate; + bool enabled; + int ret = 0; + ++ ret = clk_enable(pc->pclk); ++ if (ret) ++ return ret; ++ + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + + if (state->polarity != curstate.polarity && enabled) { +- ret = rockchip_pwm_enable(chip, pwm, false, enable_conf); ++ ret = rockchip_pwm_enable(chip, pwm, false); + if (ret) +- return ret; ++ goto out; + enabled = false; + } + + rockchip_pwm_config(chip, pwm, state); +- +- if (state->enabled != enabled) +- ret = rockchip_pwm_enable(chip, pwm, state->enabled, +- enable_conf); +- +- return ret; +-} +- +-static int rockchip_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, +- struct pwm_state *state) +-{ +- u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | +- PWM_CONTINUOUS; +- struct pwm_state curstate; +- bool enabled; +- int ret = 0; +- +- pwm_get_state(pwm, &curstate); +- enabled = curstate.enabled; +- +- if (state->polarity != curstate.polarity && enabled) { +- ret = rockchip_pwm_enable(chip, pwm, false, enable_conf); ++ if (state->enabled != enabled) { ++ ret = rockchip_pwm_enable(chip, pwm, state->enabled); + if (ret) +- return ret; +- enabled = false; ++ goto out; + } + +- rockchip_pwm_config(chip, pwm, state); +- +- if (state->enabled != enabled) +- ret = rockchip_pwm_enable(chip, pwm, state->enabled, +- enable_conf); +- +- return ret; +-} +- +-static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, +- struct pwm_state *state) +-{ +- struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); +- int ret; +- +- ret = clk_enable(pc->pclk); +- if (ret) +- return ret; +- +- ret = pc->data->pwm_apply(chip, pwm, state); +- if (ret) +- goto out; +- + /* + * Update the state with the real hardware, which can differ a bit + * because of period/duty_cycle approximation. +@@ -270,13 +206,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + return ret; + } + +-static const struct pwm_ops rockchip_pwm_ops_v1 = { +- .get_state = rockchip_pwm_get_state, +- .apply = rockchip_pwm_apply, +- .owner = THIS_MODULE, +-}; +- +-static const struct pwm_ops rockchip_pwm_ops_v2 = { ++static const struct pwm_ops rockchip_pwm_ops = { + .get_state = rockchip_pwm_get_state, + .apply = rockchip_pwm_apply, + .owner = THIS_MODULE, +@@ -290,9 +220,8 @@ static const struct rockchip_pwm_data pwm_data_v1 = { + .ctrl = 0x0c, + }, + .prescaler = 2, +- .ops = &rockchip_pwm_ops_v1, +- .get_state = rockchip_pwm_get_state_v1, +- .pwm_apply = rockchip_pwm_apply_v1, ++ .supports_polarity = false, ++ .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, + }; + + static const struct rockchip_pwm_data pwm_data_v2 = { +@@ -304,9 +233,8 @@ static const struct rockchip_pwm_data pwm_data_v2 = { + }, + .prescaler = 1, + .supports_polarity = true, +- .ops = &rockchip_pwm_ops_v2, +- .get_state = rockchip_pwm_get_state_v2, +- .pwm_apply = rockchip_pwm_apply_v2, ++ .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | ++ PWM_CONTINUOUS, + }; + + static const struct rockchip_pwm_data pwm_data_vop = { +@@ -318,9 +246,8 @@ static const struct rockchip_pwm_data pwm_data_vop = { + }, + .prescaler = 1, + .supports_polarity = true, +- .ops = &rockchip_pwm_ops_v2, +- .get_state = rockchip_pwm_get_state_v2, +- .pwm_apply = rockchip_pwm_apply_v2, ++ .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | ++ PWM_CONTINUOUS, + }; + + static const struct of_device_id rockchip_pwm_dt_ids[] = { +@@ -393,7 +320,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) + + pc->data = id->data; + pc->chip.dev = &pdev->dev; +- pc->chip.ops = pc->data->ops; ++ pc->chip.ops = &rockchip_pwm_ops; + pc->chip.base = -1; + pc->chip.npwm = 1; + +-- +2.11.0 + diff --git a/patches.drivers/0052-mfd-rk808-Fix-up-the-chip-id-get-failed.patch b/patches.drivers/0052-mfd-rk808-Fix-up-the-chip-id-get-failed.patch new file mode 100644 index 0000000..4caeca4 --- /dev/null +++ b/patches.drivers/0052-mfd-rk808-Fix-up-the-chip-id-get-failed.patch @@ -0,0 +1,79 @@ +From 94676c1796cf140daeaefee247eb427cbcb3c630 Mon Sep 17 00:00:00 2001 +From: Elaine Zhang +Date: Mon, 21 Aug 2017 03:28:34 +0200 +Subject: [PATCH 52/86] mfd: rk808: Fix up the chip id get failed + +Git-commit: 9d6105e19f617406aea46dd19281080c7c5ae0d8 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +the rk8xx chip id is: +((MSB << 8) | LSB) & 0xfff0 + +Signed-off-by: Elaine Zhang +Signed-off-by: Joseph Chen +Signed-off-by: Heiko Stuebner +Signed-off-by: Lee Jones +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/mfd/rk808.c | 21 +++++++++++++++------ + include/linux/mfd/rk808.h | 1 + + 2 files changed, 16 insertions(+), 6 deletions(-) + +diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c +index fd087cbb0bde..8e60ebaeaadb 100644 +--- a/drivers/mfd/rk808.c ++++ b/drivers/mfd/rk808.c +@@ -325,7 +325,7 @@ static int rk808_probe(struct i2c_client *client, + void (*pm_pwroff_fn)(void); + int nr_pre_init_regs; + int nr_cells; +- int pm_off = 0; ++ int pm_off = 0, msb, lsb; + int ret; + int i; + +@@ -333,14 +333,23 @@ static int rk808_probe(struct i2c_client *client, + if (!rk808) + return -ENOMEM; + +- rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB); +- if (rk808->variant < 0) { +- dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n", ++ /* Read chip variant */ ++ msb = i2c_smbus_read_byte_data(client, RK808_ID_MSB); ++ if (msb < 0) { ++ dev_err(&client->dev, "failed to read the chip id at 0x%x\n", + RK808_ID_MSB); +- return rk808->variant; ++ return msb; + } + +- dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant); ++ lsb = i2c_smbus_read_byte_data(client, RK808_ID_LSB); ++ if (lsb < 0) { ++ dev_err(&client->dev, "failed to read the chip id at 0x%x\n", ++ RK808_ID_LSB); ++ return lsb; ++ } ++ ++ rk808->variant = ((msb << 8) | lsb) & RK8XX_ID_MSK; ++ dev_info(&client->dev, "chip id: 0x%x\n", (unsigned int)rk808->variant); + + switch (rk808->variant) { + case RK808_ID: +diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h +index 83701ef7d3c7..54feb140c210 100644 +--- a/include/linux/mfd/rk808.h ++++ b/include/linux/mfd/rk808.h +@@ -298,6 +298,7 @@ enum rk818_reg { + #define VOUT_LO_INT BIT(0) + #define CLK32KOUT2_EN BIT(0) + ++#define RK8XX_ID_MSK 0xfff0 + enum { + BUCK_ILMIN_50MA, + BUCK_ILMIN_100MA, +-- +2.11.0 + diff --git a/patches.drivers/0053-drm-rockchip-Set-line-flag-config-register-in-vop_cr.patch b/patches.drivers/0053-drm-rockchip-Set-line-flag-config-register-in-vop_cr.patch new file mode 100644 index 0000000..cc64260 --- /dev/null +++ b/patches.drivers/0053-drm-rockchip-Set-line-flag-config-register-in-vop_cr.patch @@ -0,0 +1,138 @@ +From f7ccce9fd63af9f8850de8e1d5c83e87d89e0d7f Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Thu, 27 Apr 2017 14:54:17 +0800 +Subject: [PATCH 53/86] drm/rockchip: Set line flag config register in + vop_crtc_enable + +Git-commit: 459b086d878b855a7d09a074cf0dbd0717dc50ec +Patch-mainline: v4.13-rc1 +References: fate#323912 + +We need to set vop config done after update line flag config, it's a +new requirement for chips newer than rk3368. + +Since we would only use line flag irq for vact_end, let's move it to +vop_crtc_enable. + +Signed-off-by: Jeffy Chen +Acked-by: Mark Yao +Link: http://patchwork.freedesktop.org/patch/msgid/1493276057-4516-1-git-send-email-jeffy.chen@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 4 ++-- + drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 3 +-- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 20 +++++++++----------- + 3 files changed, 12 insertions(+), 15 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +index ce5f2d1f9994..092f4a4f4642 100644 +--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c ++++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +@@ -115,8 +115,8 @@ static void analogix_dp_psr_work(struct work_struct *work) + + vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay; + +- ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, +- PSR_WAIT_LINE_FLAG_TIMEOUT_MS); ++ ret = rockchip_drm_wait_vact_end(dp->encoder.crtc, ++ PSR_WAIT_LINE_FLAG_TIMEOUT_MS); + if (ret) { + dev_err(dp->dev, "line flag interrupt did not arrive\n"); + return; +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +index a48fcce3f5f6..47905faf5586 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +@@ -62,8 +62,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, + struct device *dev); + void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, + struct device *dev); +-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num, +- unsigned int mstimeout); ++int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout); + + extern struct platform_driver cdn_dp_driver; + extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index 45589d6ce65e..c83f481a21c8 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -468,7 +468,7 @@ static bool vop_line_flag_irq_is_enabled(struct vop *vop) + return !!line_flag_irq; + } + +-static void vop_line_flag_irq_enable(struct vop *vop, int line_num) ++static void vop_line_flag_irq_enable(struct vop *vop) + { + unsigned long flags; + +@@ -477,7 +477,6 @@ static void vop_line_flag_irq_enable(struct vop *vop, int line_num) + + spin_lock_irqsave(&vop->irq_lock, flags); + +- VOP_CTRL_SET(vop, line_flag_num[0], line_num); + VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1); + VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1); + +@@ -989,6 +988,8 @@ static void vop_crtc_enable(struct drm_crtc *crtc) + VOP_CTRL_SET(vop, vact_st_end, val); + VOP_CTRL_SET(vop, vpost_st_end, val); + ++ VOP_CTRL_SET(vop, line_flag_num[0], vact_end); ++ + clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); + + VOP_CTRL_SET(vop, standby, 0); +@@ -1515,19 +1516,16 @@ static void vop_win_init(struct vop *vop) + } + + /** +- * rockchip_drm_wait_line_flag - acqiure the give line flag event ++ * rockchip_drm_wait_vact_end + * @crtc: CRTC to enable line flag +- * @line_num: interested line number + * @mstimeout: millisecond for timeout + * +- * Driver would hold here until the interested line flag interrupt have +- * happened or timeout to wait. ++ * Wait for vact_end line flag irq or timeout. + * + * Returns: + * Zero on success, negative errno on failure. + */ +-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num, +- unsigned int mstimeout) ++int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout) + { + struct vop *vop = to_vop(crtc); + unsigned long jiffies_left; +@@ -1535,14 +1533,14 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num, + if (!crtc || !vop->is_enabled) + return -ENODEV; + +- if (line_num > crtc->mode.vtotal || mstimeout <= 0) ++ if (mstimeout <= 0) + return -EINVAL; + + if (vop_line_flag_irq_is_enabled(vop)) + return -EBUSY; + + reinit_completion(&vop->line_flag_completion); +- vop_line_flag_irq_enable(vop, line_num); ++ vop_line_flag_irq_enable(vop); + + jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion, + msecs_to_jiffies(mstimeout)); +@@ -1555,7 +1553,7 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num, + + return 0; + } +-EXPORT_SYMBOL(rockchip_drm_wait_line_flag); ++EXPORT_SYMBOL(rockchip_drm_wait_vact_end); + + static int vop_bind(struct device *dev, struct device *master, void *data) + { +-- +2.11.0 + diff --git a/patches.drivers/0054-drm-rockchip-analogix_dp-Remove-unused-check-and-var.patch b/patches.drivers/0054-drm-rockchip-analogix_dp-Remove-unused-check-and-var.patch new file mode 100644 index 0000000..683ee90 --- /dev/null +++ b/patches.drivers/0054-drm-rockchip-analogix_dp-Remove-unused-check-and-var.patch @@ -0,0 +1,55 @@ +From da0b90d1a706f2e7ddac294a639efb20bf71e1d8 Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Sat, 29 Apr 2017 20:39:07 +0800 +Subject: [PATCH 54/86] drm/rockchip: analogix_dp: Remove unused check and + variables + +Git-commit: aa5bfa405c87a82d0b0374bb47c8e05d0fb69c04 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +Remove unused check and variables after: +drm/rockchip: Set line flag config register in vop_crtc_enable + +Signed-off-by: Jeffy Chen +Signed-off-by: Sean Paul +Link: http://patchwork.freedesktop.org/patch/msgid/1493469547-2121-1-git-send-email-jeffy.chen@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 10 +--------- + 1 file changed, 1 insertion(+), 9 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +index 092f4a4f4642..9606121fa185 100644 +--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c ++++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +@@ -104,17 +104,9 @@ static void analogix_dp_psr_work(struct work_struct *work) + { + struct rockchip_dp_device *dp = + container_of(work, typeof(*dp), psr_work); +- struct drm_crtc *crtc = dp->encoder.crtc; +- int psr_state = dp->psr_state; +- int vact_end; + int ret; + unsigned long flags; + +- if (!crtc) +- return; +- +- vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay; +- + ret = rockchip_drm_wait_vact_end(dp->encoder.crtc, + PSR_WAIT_LINE_FLAG_TIMEOUT_MS); + if (ret) { +@@ -123,7 +115,7 @@ static void analogix_dp_psr_work(struct work_struct *work) + } + + spin_lock_irqsave(&dp->psr_lock, flags); +- if (psr_state == EDP_VSC_PSR_STATE_ACTIVE) ++ if (dp->psr_state == EDP_VSC_PSR_STATE_ACTIVE) + analogix_dp_enable_psr(dp->dev); + else + analogix_dp_disable_psr(dp->dev); +-- +2.11.0 + diff --git a/patches.drivers/0055-drm-rockchip-use-drm_for_each_connector_iter.patch b/patches.drivers/0055-drm-rockchip-use-drm_for_each_connector_iter.patch new file mode 100644 index 0000000..7002513 --- /dev/null +++ b/patches.drivers/0055-drm-rockchip-use-drm_for_each_connector_iter.patch @@ -0,0 +1,52 @@ +From d49c874b80fe1cd5c437df7390f300131e7c8398 Mon Sep 17 00:00:00 2001 +From: Gustavo Padovan +Date: Mon, 15 May 2017 10:43:30 -0300 +Subject: [PATCH 55/86] drm/rockchip: use drm_for_each_connector_iter() + +Git-commit: 2cbeb64f6c709de67e2ba5b97ff45d2b6147b286 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +Drop legacy drm_for_each_connector() in favor of the race-free +drm_for_each_connector_iter() + +Cc: Mark Yao +Signed-off-by: Gustavo Padovan +Reviewed-by: Daniel Vetter +Reviewed-by: Sean Paul +Link: http://patchwork.freedesktop.org/patch/msgid/20170515134330.3275-1-gustavo@padovan.org +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index c83f481a21c8..5d450332c2fd 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -1126,16 +1126,17 @@ static void vop_crtc_destroy_state(struct drm_crtc *crtc, + #ifdef CONFIG_DRM_ANALOGIX_DP + static struct drm_connector *vop_get_edp_connector(struct vop *vop) + { +- struct drm_crtc *crtc = &vop->crtc; + struct drm_connector *connector; ++ struct drm_connector_list_iter conn_iter; + +- mutex_lock(&crtc->dev->mode_config.mutex); +- drm_for_each_connector(connector, crtc->dev) ++ drm_connector_list_iter_begin(vop->drm_dev, &conn_iter); ++ drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { +- mutex_unlock(&crtc->dev->mode_config.mutex); ++ drm_connector_list_iter_end(&conn_iter); + return connector; + } +- mutex_unlock(&crtc->dev->mode_config.mutex); ++ } ++ drm_connector_list_iter_end(&conn_iter); + + return NULL; + } +-- +2.11.0 + diff --git a/patches.drivers/0056-drm-rockchip-gem-add-the-lacks-lock-and-trivial-chan.patch b/patches.drivers/0056-drm-rockchip-gem-add-the-lacks-lock-and-trivial-chan.patch new file mode 100644 index 0000000..aaefbc2 --- /dev/null +++ b/patches.drivers/0056-drm-rockchip-gem-add-the-lacks-lock-and-trivial-chan.patch @@ -0,0 +1,75 @@ +From 7cabd850995c1f9846d352d89d79940878f04d6f Mon Sep 17 00:00:00 2001 +From: Caesar Wang +Date: Wed, 31 May 2017 10:14:23 +0800 +Subject: [PATCH 56/86] drm/rockchip: gem: add the lacks lock and trivial + changes + +Git-commit: 2d7b56378d32b0cf006f8944cbba4046df45dd25 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +As the allocation and free buffer that need to add mutex lock for drm mm, +but it lacks the locking on error path in rockchip_gem_iommu_map(). +Also, the trivial changes like The comment should be placed in the +kerneldoc and unused blank line. + +Signed-off-by: Caesar Wang +Reviewed-by: Mark Yao +Signed-off-by: Mark Yao +Link: http://patchwork.freedesktop.org/patch/msgid/1496196863-25738-1-git-send-email-wxt@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 2 +- + drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +index 47905faf5586..c7e96b82cf63 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +@@ -45,13 +45,13 @@ struct rockchip_crtc_state { + * + * @crtc: array of enabled CRTCs, used to map from "pipe" to drm_crtc. + * @num_pipe: number of pipes for this device. ++ * @mm_lock: protect drm_mm on multi-threads. + */ + struct rockchip_drm_private { + struct drm_fb_helper fbdev_helper; + struct drm_gem_object *fbdev_bo; + struct drm_atomic_state *state; + struct iommu_domain *domain; +- /* protect drm_mm on multi-threads */ + struct mutex mm_lock; + struct drm_mm mm; + struct list_head psr_list; +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +index df9e57064f19..b74ac717e56a 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +@@ -29,12 +29,11 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj) + ssize_t ret; + + mutex_lock(&private->mm_lock); +- + ret = drm_mm_insert_node_generic(&private->mm, &rk_obj->mm, + rk_obj->base.size, PAGE_SIZE, + 0, 0); +- + mutex_unlock(&private->mm_lock); ++ + if (ret < 0) { + DRM_ERROR("out of I/O virtual memory: %zd\n", ret); + return ret; +@@ -56,7 +55,9 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj) + return 0; + + err_remove_node: ++ mutex_lock(&private->mm_lock); + drm_mm_remove_node(&rk_obj->mm); ++ mutex_unlock(&private->mm_lock); + + return ret; + } +-- +2.11.0 + diff --git a/patches.drivers/0057-drm-rockchip-Remove-unnecessary-NULL-check.patch b/patches.drivers/0057-drm-rockchip-Remove-unnecessary-NULL-check.patch new file mode 100644 index 0000000..f9ddacc --- /dev/null +++ b/patches.drivers/0057-drm-rockchip-Remove-unnecessary-NULL-check.patch @@ -0,0 +1,40 @@ +From 8144d051256090b6f43904854154f2b4908060f1 Mon Sep 17 00:00:00 2001 +From: Thierry Reding +Date: Wed, 29 Mar 2017 16:44:01 +0200 +Subject: [PATCH 57/86] drm/rockchip: Remove unnecessary NULL check + +Git-commit: 5a1535b110d73ed8d29c504eeb5fa92ac5b47cd9 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +The expression &private->fbdev_helper can never be NULL, so the check is +completely unnecessary. + +Reviewed-by: Daniel Vetter +Signed-off-by: Thierry Reding +Signed-off-by: Daniel Vetter +Link: http://patchwork.freedesktop.org/patch/msgid/20170329144401.1804-12-thierry.reding@gmail.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +index 81f9548672b0..df6bceabeca8 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +@@ -168,10 +168,8 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, + static void rockchip_drm_output_poll_changed(struct drm_device *dev) + { + struct rockchip_drm_private *private = dev->dev_private; +- struct drm_fb_helper *fb_helper = &private->fbdev_helper; + +- if (fb_helper) +- drm_fb_helper_hotplug_event(fb_helper); ++ drm_fb_helper_hotplug_event(&private->fbdev_helper); + } + + static void +-- +2.11.0 + diff --git a/patches.drivers/0058-drm-rockchip-dw_hdmi-add-RK3399-HDMI-support.patch b/patches.drivers/0058-drm-rockchip-dw_hdmi-add-RK3399-HDMI-support.patch new file mode 100644 index 0000000..11ca393 --- /dev/null +++ b/patches.drivers/0058-drm-rockchip-dw_hdmi-add-RK3399-HDMI-support.patch @@ -0,0 +1,155 @@ +From da94e78e9c12a17a4e225c987180168f205930a6 Mon Sep 17 00:00:00 2001 +From: Mark Yao +Date: Thu, 22 Jun 2017 15:17:24 +0800 +Subject: [PATCH 58/86] drm/rockchip: dw_hdmi: add RK3399 HDMI support + +Git-commit: 6445e394c54b61974491fccbf2e4fcd990d2fa8b +Patch-mainline: v4.14-rc1 +References: fate#323912 + +RK3399 and RK3288 shared the same HDMI IP controller, only some light +difference with GRF configure. + +Signed-off-by: Yakir Yang +Signed-off-by: Mark Yao +Acked-by: Rob Herring +Signed-off-by: Mian Yousaf Kaukab +--- + .../bindings/display/rockchip/dw_hdmi-rockchip.txt | 4 +- + drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 67 ++++++++++++++++++---- + 2 files changed, 59 insertions(+), 12 deletions(-) + +diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +index 046076c6b277..7039a15b0d04 100644 +--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt ++++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +@@ -11,7 +11,9 @@ following device-specific properties. + + Required properties: + +-- compatible: Shall contain "rockchip,rk3288-dw-hdmi". ++- compatible: should be one of the following: ++ "rockchip,rk3288-dw-hdmi" ++ "rockchip,rk3399-dw-hdmi" + - reg: See dw_hdmi.txt. + - reg-io-width: See dw_hdmi.txt. Shall be 4. + - interrupts: HDMI interrupt number +diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +index 63dab6f1b191..90aaaf4d4f41 100644 +--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +@@ -20,13 +20,30 @@ + #include "rockchip_drm_drv.h" + #include "rockchip_drm_vop.h" + +-#define GRF_SOC_CON6 0x025c +-#define HDMI_SEL_VOP_LIT (1 << 4) ++#define RK3288_GRF_SOC_CON6 0x025C ++#define RK3288_HDMI_LCDC_SEL BIT(4) ++#define RK3399_GRF_SOC_CON20 0x6250 ++#define RK3399_HDMI_LCDC_SEL BIT(6) ++ ++#define HIWORD_UPDATE(val, mask) (val | (mask) << 16) ++ ++/** ++ * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips ++ * @lcdsel_grf_reg: grf register offset of lcdc select ++ * @lcdsel_big: reg value of selecting vop big for HDMI ++ * @lcdsel_lit: reg value of selecting vop little for HDMI ++ */ ++struct rockchip_hdmi_chip_data { ++ u32 lcdsel_grf_reg; ++ u32 lcdsel_big; ++ u32 lcdsel_lit; ++}; + + struct rockchip_hdmi { + struct device *dev; + struct regmap *regmap; + struct drm_encoder encoder; ++ const struct rockchip_hdmi_chip_data *chip_data; + }; + + #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) +@@ -198,17 +215,20 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + { + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); + u32 val; +- int mux; ++ int ret; + +- mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); +- if (mux) +- val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16); ++ ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); ++ if (ret) ++ val = hdmi->chip_data->lcdsel_lit; + else +- val = HDMI_SEL_VOP_LIT << 16; ++ val = hdmi->chip_data->lcdsel_big; ++ ++ ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val); ++ if (ret != 0) ++ dev_err(hdmi->dev, "Could not write to GRF: %d\n", ret); + +- regmap_write(hdmi->regmap, GRF_SOC_CON6, val); + dev_dbg(hdmi->dev, "vop %s output to hdmi\n", +- (mux) ? "LIT" : "BIG"); ++ ret ? "LIT" : "BIG"); + } + + static int +@@ -232,16 +252,40 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun + .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, + }; + +-static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = { ++static struct rockchip_hdmi_chip_data rk3288_chip_data = { ++ .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, ++ .lcdsel_big = HIWORD_UPDATE(0, RK3288_HDMI_LCDC_SEL), ++ .lcdsel_lit = HIWORD_UPDATE(RK3288_HDMI_LCDC_SEL, RK3288_HDMI_LCDC_SEL), ++}; ++ ++static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = { ++ .mode_valid = dw_hdmi_rockchip_mode_valid, ++ .mpll_cfg = rockchip_mpll_cfg, ++ .cur_ctr = rockchip_cur_ctr, ++ .phy_config = rockchip_phy_config, ++ .phy_data = &rk3288_chip_data, ++}; ++ ++static struct rockchip_hdmi_chip_data rk3399_chip_data = { ++ .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, ++ .lcdsel_big = HIWORD_UPDATE(0, RK3399_HDMI_LCDC_SEL), ++ .lcdsel_lit = HIWORD_UPDATE(RK3399_HDMI_LCDC_SEL, RK3399_HDMI_LCDC_SEL), ++}; ++ ++static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = { + .mode_valid = dw_hdmi_rockchip_mode_valid, + .mpll_cfg = rockchip_mpll_cfg, + .cur_ctr = rockchip_cur_ctr, + .phy_config = rockchip_phy_config, ++ .phy_data = &rk3399_chip_data, + }; + + static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { + { .compatible = "rockchip,rk3288-dw-hdmi", +- .data = &rockchip_hdmi_drv_data ++ .data = &rk3288_hdmi_drv_data ++ }, ++ { .compatible = "rockchip,rk3399-dw-hdmi", ++ .data = &rk3399_hdmi_drv_data + }, + {}, + }; +@@ -268,6 +312,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); + plat_data = match->data; + hdmi->dev = &pdev->dev; ++ hdmi->chip_data = plat_data->phy_data; + encoder = &hdmi->encoder; + + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); +-- +2.11.0 + diff --git a/patches.drivers/0059-drm-rockchip-dw_hdmi-introduce-the-VPLL-clock-settin.patch b/patches.drivers/0059-drm-rockchip-dw_hdmi-introduce-the-VPLL-clock-settin.patch new file mode 100644 index 0000000..ec5fcb0 --- /dev/null +++ b/patches.drivers/0059-drm-rockchip-dw_hdmi-introduce-the-VPLL-clock-settin.patch @@ -0,0 +1,108 @@ +From 13ba1aca5c69c8de88413732bd80a2aa93334306 Mon Sep 17 00:00:00 2001 +From: Mark Yao +Date: Fri, 9 Jun 2017 15:10:41 +0800 +Subject: [PATCH 59/86] drm/rockchip: dw_hdmi: introduce the VPLL clock setting + +Git-commit: 5e3bc6d1ab48a20cbce3d59d42937bc977ec61cf +Patch-mainline: v4.14-rc1 +References: fate#323912 + +For RK3399 HDMI, there is an external clock need for HDMI PHY, +and it should keep the same clock rate with VOP DCLK. + +VPLL have supported the clock for HDMI PHY, but there is no +clock divider bewteen VPLL and HDMI PHY. So we need to set the +VPLL rate manually in HDMI driver. + +Signed-off-by: Yakir Yang +Signed-off-by: Mark Yao +Acked-by: Rob Herring +Signed-off-by: Mian Yousaf Kaukab +--- + .../bindings/display/rockchip/dw_hdmi-rockchip.txt | 2 +- + drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 25 +++++++++++++++++++++- + 2 files changed, 25 insertions(+), 2 deletions(-) + +diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +index 7039a15b0d04..122d4e8b879b 100644 +--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt ++++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +@@ -32,7 +32,7 @@ Optional properties + I2C master controller. + - clock-names: See dw_hdmi.txt. The "cec" clock is optional. + - clock-names: May contain "cec" as defined in dw_hdmi.txt. +- ++- clock-names: May contain "vpll", external clock for some hdmi phy. + + Example: + +diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +index 90aaaf4d4f41..aaa0f3ec67d4 100644 +--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +@@ -7,10 +7,12 @@ + * (at your option) any later version. + */ + ++#include ++#include + #include + #include +-#include + #include ++ + #include + #include + #include +@@ -44,6 +46,7 @@ struct rockchip_hdmi { + struct regmap *regmap; + struct drm_encoder encoder; + const struct rockchip_hdmi_chip_data *chip_data; ++ struct clk *vpll_clk; + }; + + #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) +@@ -160,6 +163,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = { + static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + { + struct device_node *np = hdmi->dev->of_node; ++ int ret; + + hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(hdmi->regmap)) { +@@ -167,6 +171,22 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + return PTR_ERR(hdmi->regmap); + } + ++ hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll"); ++ if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) { ++ hdmi->vpll_clk = NULL; ++ } else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->vpll_clk)) { ++ dev_err(hdmi->dev, "failed to get grf clock\n"); ++ return PTR_ERR(hdmi->vpll_clk); ++ } ++ ++ ret = clk_prepare_enable(hdmi->vpll_clk); ++ if (ret) { ++ dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret); ++ return ret; ++ } ++ + return 0; + } + +@@ -209,6 +229,9 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) + { ++ struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); ++ ++ clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000); + } + + static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) +-- +2.11.0 + diff --git a/patches.drivers/0060-drm-rockchip-dw_hdmi-introduce-the-pclk-for-grf.patch b/patches.drivers/0060-drm-rockchip-dw_hdmi-introduce-the-pclk-for-grf.patch new file mode 100644 index 0000000..0826ffa --- /dev/null +++ b/patches.drivers/0060-drm-rockchip-dw_hdmi-introduce-the-pclk-for-grf.patch @@ -0,0 +1,84 @@ +From c03ffa50638084df227625b06c80bc569ef14f79 Mon Sep 17 00:00:00 2001 +From: Mark Yao +Date: Fri, 9 Jun 2017 15:10:46 +0800 +Subject: [PATCH 60/86] drm/rockchip: dw_hdmi: introduce the pclk for grf + +Git-commit: 8814b40bf6b2293eede832d35957b4e9ba495ae3 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +For RK3399's GRF module, if we want to operate the graphic related grf +registers, we need to enable the pclk_vio_grf which supply power for VIO +GRF IOs, so it's better to introduce an optional grf clock in driver. + +Signed-off-by: Yakir Yang +Signed-off-by: Mark Yao +Acked-by: Rob Herring +Signed-off-by: Mian Yousaf Kaukab +--- + .../bindings/display/rockchip/dw_hdmi-rockchip.txt | 1 + + drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 18 ++++++++++++++++++ + 2 files changed, 19 insertions(+) + +diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +index 122d4e8b879b..fad8b7619647 100644 +--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt ++++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt +@@ -32,6 +32,7 @@ Optional properties + I2C master controller. + - clock-names: See dw_hdmi.txt. The "cec" clock is optional. + - clock-names: May contain "cec" as defined in dw_hdmi.txt. ++- clock-names: May contain "grf", power for grf io. + - clock-names: May contain "vpll", external clock for some hdmi phy. + + Example: +diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +index aaa0f3ec67d4..2b9cb47114c4 100644 +--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +@@ -47,6 +47,7 @@ struct rockchip_hdmi { + struct drm_encoder encoder; + const struct rockchip_hdmi_chip_data *chip_data; + struct clk *vpll_clk; ++ struct clk *grf_clk; + }; + + #define to_rockchip_hdmi(x) container_of(x, struct rockchip_hdmi, x) +@@ -181,6 +182,16 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + return PTR_ERR(hdmi->vpll_clk); + } + ++ hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf"); ++ if (PTR_ERR(hdmi->grf_clk) == -ENOENT) { ++ hdmi->grf_clk = NULL; ++ } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) { ++ return -EPROBE_DEFER; ++ } else if (IS_ERR(hdmi->grf_clk)) { ++ dev_err(hdmi->dev, "failed to get grf clock\n"); ++ return PTR_ERR(hdmi->grf_clk); ++ } ++ + ret = clk_prepare_enable(hdmi->vpll_clk); + if (ret) { + dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret); +@@ -246,10 +257,17 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + else + val = hdmi->chip_data->lcdsel_big; + ++ ret = clk_prepare_enable(hdmi->grf_clk); ++ if (ret < 0) { ++ dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret); ++ return; ++ } ++ + ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val); + if (ret != 0) + dev_err(hdmi->dev, "Could not write to GRF: %d\n", ret); + ++ clk_disable_unprepare(hdmi->grf_clk); + dev_dbg(hdmi->dev, "vop %s output to hdmi\n", + ret ? "LIT" : "BIG"); + } +-- +2.11.0 + diff --git a/patches.drivers/0061-drm-rockchip-Drop-drm_vblank_cleanup.patch b/patches.drivers/0061-drm-rockchip-Drop-drm_vblank_cleanup.patch new file mode 100644 index 0000000..03c326b --- /dev/null +++ b/patches.drivers/0061-drm-rockchip-Drop-drm_vblank_cleanup.patch @@ -0,0 +1,44 @@ +From 9a2ca8207846fa724678f9cf3434d8dd52b360db Mon Sep 17 00:00:00 2001 +From: Daniel Vetter +Date: Wed, 21 Jun 2017 10:28:45 +0200 +Subject: [PATCH 61/86] drm/rockchip: Drop drm_vblank_cleanup + +Git-commit: 3f5857fc6284da371868c2216a96d6d392185b5e +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Either not relevant (in the load error paths) or done better already +(in the unload code, by calling drm_atomic_helper_shutdown). Drop it. + +Cc: Mark Yao +Reviewed-by: Sean Paul +Signed-off-by: Daniel Vetter +Link: http://patchwork.freedesktop.org/patch/msgid/20170621082850.13224-9-daniel.vetter@ffwll.ch +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index c6b1b7f3a2a3..b9fbf4b1e8f0 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -177,7 +177,6 @@ static int rockchip_drm_bind(struct device *dev) + rockchip_drm_fbdev_fini(drm_dev); + err_kms_helper_poll_fini: + drm_kms_helper_poll_fini(drm_dev); +- drm_vblank_cleanup(drm_dev); + err_unbind_all: + component_unbind_all(dev, drm_dev); + err_mode_config_cleanup: +@@ -200,7 +199,6 @@ static void rockchip_drm_unbind(struct device *dev) + drm_kms_helper_poll_fini(drm_dev); + + drm_atomic_helper_shutdown(drm_dev); +- drm_vblank_cleanup(drm_dev); + component_unbind_all(dev, drm_dev); + drm_mode_config_cleanup(drm_dev); + rockchip_iommu_cleanup(drm_dev); +-- +2.11.0 + diff --git a/patches.drivers/0062-drm-rockchip-fix-NULL-check-on-devm_kzalloc-return-v.patch b/patches.drivers/0062-drm-rockchip-fix-NULL-check-on-devm_kzalloc-return-v.patch new file mode 100644 index 0000000..ec4450f --- /dev/null +++ b/patches.drivers/0062-drm-rockchip-fix-NULL-check-on-devm_kzalloc-return-v.patch @@ -0,0 +1,48 @@ +From 184b39e6bb5de0b90c4eade83f11ec3bb3baec9a Mon Sep 17 00:00:00 2001 +From: "Gustavo A. R. Silva" +Date: Thu, 6 Jul 2017 16:58:33 -0500 +Subject: [PATCH 62/86] drm/rockchip: fix NULL check on devm_kzalloc() return + value + +Git-commit: 6f6e0b217a93011f8e11b9a2d5521a08fcf36990 +Patch-mainline: v4.13-rc1 +References: fate#323912 + +The right variable to check here is port, not dp. + +This issue was detected using Coccinelle and the following semantic patch: + +@@ +expression x; +identifier fld; +@@ + +* x = devm_kzalloc(...); + ... when != x == NULL + x->fld + +Signed-off-by: Gustavo A. R. Silva +Acked-by: Mark Yao +Signed-off-by: Sean Paul +Link: http://patchwork.freedesktop.org/patch/msgid/20170706215833.GA25411@embeddedgus +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/cdn-dp-core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c +index 14fa1f8351e8..9b0b0588bbed 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c +@@ -1195,7 +1195,7 @@ static int cdn_dp_probe(struct platform_device *pdev) + continue; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); +- if (!dp) ++ if (!port) + return -ENOMEM; + + port->extcon = extcon; +-- +2.11.0 + diff --git a/patches.drivers/0063-drm-rockchip-Use-for_each_oldnew_plane_in_state-in-v.patch b/patches.drivers/0063-drm-rockchip-Use-for_each_oldnew_plane_in_state-in-v.patch new file mode 100644 index 0000000..1fd7a26 --- /dev/null +++ b/patches.drivers/0063-drm-rockchip-Use-for_each_oldnew_plane_in_state-in-v.patch @@ -0,0 +1,57 @@ +From 3ecf9da0fde94e24108d76bf47588ed500a36e38 Mon Sep 17 00:00:00 2001 +From: Maarten Lankhorst +Date: Wed, 12 Jul 2017 10:13:37 +0200 +Subject: [PATCH 63/86] drm/rockchip: Use for_each_oldnew_plane_in_state in + vop_crtc_atomic_flush + +Git-commit: e741f2b182e6d6203dfbf294affdfb9eb1009ddf +Patch-mainline: v4.14-rc1 +References: fate#323912 + +for_each_obj_in_state is about to be removed, so use the new atomic +iterator macros. + +Signed-off-by: Maarten Lankhorst +Cc: Mark Yao +Cc: Heiko Stuebner +Cc: linux-arm-kernel@lists.infradead.org +Cc: linux-rockchip@lists.infradead.org +Link: http://patchwork.freedesktop.org/patch/msgid/20170712081344.25495-10-maarten.lankhorst@linux.intel.com +Reviewed-by: Daniel Vetter +Acked-by: Mark Yao +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index 5d450332c2fd..bc95e0385cdb 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -1027,7 +1027,7 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) + { + struct drm_atomic_state *old_state = old_crtc_state->state; +- struct drm_plane_state *old_plane_state; ++ struct drm_plane_state *old_plane_state, *new_plane_state; + struct vop *vop = to_vop(crtc); + struct drm_plane *plane; + int i; +@@ -1058,11 +1058,12 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, + } + spin_unlock_irq(&crtc->dev->event_lock); + +- for_each_plane_in_state(old_state, plane, old_plane_state, i) { ++ for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, ++ new_plane_state, i) { + if (!old_plane_state->fb) + continue; + +- if (old_plane_state->fb == plane->state->fb) ++ if (old_plane_state->fb == new_plane_state->fb) + continue; + + drm_framebuffer_reference(old_plane_state->fb); +-- +2.11.0 + diff --git a/patches.drivers/0064-drm-rockchip-Use-.dumb_map_offset-and-.dumb_destroy-.patch b/patches.drivers/0064-drm-rockchip-Use-.dumb_map_offset-and-.dumb_destroy-.patch new file mode 100644 index 0000000..c5b16ec --- /dev/null +++ b/patches.drivers/0064-drm-rockchip-Use-.dumb_map_offset-and-.dumb_destroy-.patch @@ -0,0 +1,92 @@ +From bc266eead840399ec6ae9b6811308dd05bd3762a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Sun, 23 Jul 2017 21:16:40 +0200 +Subject: [PATCH 64/86] drm/rockchip: Use .dumb_map_offset and .dumb_destroy + defaults +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Git-commit: 8cfd4f5de4968daeb8f4535e72da9240f8661e20 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +This driver can use the drm_driver.dumb_destroy and +drm_driver.dumb_map_offset defaults, so no need to set them. + +Cc: Mark Yao +Signed-off-by: Noralf Trønnes +Reviewed-by: Sean Paul +Link: https://patchwork.freedesktop.org/patch/msgid/1500837417-40580-25-git-send-email-noralf@tronnes.org +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 -- + drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 26 -------------------------- + drivers/gpu/drm/rockchip/rockchip_drm_gem.h | 3 --- + 3 files changed, 31 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index b9fbf4b1e8f0..488fc2191baa 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -233,8 +233,6 @@ static struct drm_driver rockchip_drm_driver = { + .gem_vm_ops = &drm_gem_cma_vm_ops, + .gem_free_object_unlocked = rockchip_gem_free_object, + .dumb_create = rockchip_gem_dumb_create, +- .dumb_map_offset = rockchip_gem_dumb_map_offset, +- .dumb_destroy = drm_gem_dumb_destroy, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +index b74ac717e56a..f74333efe4bb 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +@@ -393,32 +393,6 @@ rockchip_gem_create_with_handle(struct drm_file *file_priv, + return ERR_PTR(ret); + } + +-int rockchip_gem_dumb_map_offset(struct drm_file *file_priv, +- struct drm_device *dev, uint32_t handle, +- uint64_t *offset) +-{ +- struct drm_gem_object *obj; +- int ret; +- +- obj = drm_gem_object_lookup(file_priv, handle); +- if (!obj) { +- DRM_ERROR("failed to lookup gem object.\n"); +- return -EINVAL; +- } +- +- ret = drm_gem_create_mmap_offset(obj); +- if (ret) +- goto out; +- +- *offset = drm_vma_node_offset_addr(&obj->vma_node); +- DRM_DEBUG_KMS("offset = 0x%llx\n", *offset); +- +-out: +- drm_gem_object_unreference_unlocked(obj); +- +- return 0; +-} +- + /* + * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback + * function +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +index 3f6ea4d18a5c..f237375582fb 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h +@@ -57,7 +57,4 @@ void rockchip_gem_free_object(struct drm_gem_object *obj); + int rockchip_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +-int rockchip_gem_dumb_map_offset(struct drm_file *file_priv, +- struct drm_device *dev, uint32_t handle, +- uint64_t *offset); + #endif /* _ROCKCHIP_DRM_GEM_H */ +-- +2.11.0 + diff --git a/patches.drivers/0065-drm-rockchip-vop-initialize-registers-directly.patch b/patches.drivers/0065-drm-rockchip-vop-initialize-registers-directly.patch new file mode 100644 index 0000000..c88468f --- /dev/null +++ b/patches.drivers/0065-drm-rockchip-vop-initialize-registers-directly.patch @@ -0,0 +1,219 @@ +From 625d16dbda5171e9bc8b118f11c96c011190cecc Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Wed, 26 Jul 2017 14:19:05 +0800 +Subject: [PATCH 65/86] drm/rockchip: vop: initialize registers directly + +Git-commit: 60b7ae7fa2f650b9faefff34aa295c28b65bb607 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +At present we are using init_table to initialize some +registers, but the Register init table use un-document define, +it is unreadable, and sometimes we only want to update tiny +bits, init table method is not friendly, it's diffcult to +reuse for difference chips. + +To make it clean, initialize registers directly, and drops +init_table mechanism out. + +Signed-off-by: Mark Yao +Tested-by: Heiko Stuebner +Reviewed-by: Jeffy Chen +Link: https://patchwork.freedesktop.org/patch/msgid/1501049946-5877-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 6 ++-- + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 10 ++----- + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 44 ++++------------------------- + 3 files changed, 11 insertions(+), 49 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index bc95e0385cdb..b44cb7c80eda 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -1396,7 +1396,6 @@ static void vop_destroy_crtc(struct vop *vop) + static int vop_initial(struct vop *vop) + { + const struct vop_data *vop_data = vop->data; +- const struct vop_reg_data *init_table = vop_data->init_table; + struct reset_control *ahb_rst; + int i, ret; + +@@ -1456,13 +1455,14 @@ static int vop_initial(struct vop *vop) + + memcpy(vop->regsbak, vop->regs, vop->len); + +- for (i = 0; i < vop_data->table_size; i++) +- vop_writel(vop, init_table[i].offset, init_table[i].value); ++ VOP_CTRL_SET(vop, global_regdone_en, 1); ++ VOP_CTRL_SET(vop, dsp_blank, 0); + + for (i = 0; i < vop_data->win_size; i++) { + const struct vop_win_data *win = &vop_data->win[i]; + + VOP_WIN_SET(vop, win, enable, 0); ++ VOP_WIN_SET(vop, win, gate, 1); + } + + vop_cfg_done(vop); +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 9979fd0c2282..084d3b25a120 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -24,11 +24,6 @@ enum vop_data_format { + VOP_FMT_YUV444SP, + }; + +-struct vop_reg_data { +- uint32_t offset; +- uint32_t value; +-}; +- + struct vop_reg { + uint32_t offset; + uint32_t shift; +@@ -46,6 +41,7 @@ struct vop_ctrl { + struct vop_reg hdmi_en; + struct vop_reg mipi_en; + struct vop_reg dp_en; ++ struct vop_reg dsp_blank; + struct vop_reg out_mode; + struct vop_reg dither_down; + struct vop_reg dither_up; +@@ -65,6 +61,7 @@ struct vop_ctrl { + + struct vop_reg line_flag_num[2]; + ++ struct vop_reg global_regdone_en; + struct vop_reg cfg_done; + }; + +@@ -115,6 +112,7 @@ struct vop_win_phy { + uint32_t nformats; + + struct vop_reg enable; ++ struct vop_reg gate; + struct vop_reg format; + struct vop_reg rb_swap; + struct vop_reg act_info; +@@ -136,8 +134,6 @@ struct vop_win_data { + }; + + struct vop_data { +- const struct vop_reg_data *init_table; +- unsigned int table_size; + const struct vop_ctrl *ctrl; + const struct vop_intr *intr; + const struct vop_win_data *win; +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +index bafd698a28b1..58da8559dc89 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +@@ -119,6 +119,7 @@ static const struct vop_ctrl rk3036_ctrl_data = { + .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30), + .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), + .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), ++ .dsp_blank = VOP_REG(RK3036_DSP_CTRL1, 0x1, 24), + .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), +@@ -127,13 +128,7 @@ static const struct vop_ctrl rk3036_ctrl_data = { + .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0), + }; + +-static const struct vop_reg_data rk3036_vop_init_reg_table[] = { +- {RK3036_DSP_CTRL1, 0x00000000}, +-}; +- + static const struct vop_data rk3036_vop = { +- .init_table = rk3036_vop_init_reg_table, +- .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table), + .ctrl = &rk3036_ctrl_data, + .intr = &rk3036_intr, + .win = rk3036_vop_win_data, +@@ -193,7 +188,8 @@ static const struct vop_win_phy rk3288_win01_data = { + static const struct vop_win_phy rk3288_win23_data = { + .data_formats = formats_win_lite, + .nformats = ARRAY_SIZE(formats_win_lite), +- .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0), ++ .enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4), ++ .gate = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0), + .format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1), + .rb_swap = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 12), + .dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO0, 0x0fff0fff, 0), +@@ -215,6 +211,7 @@ static const struct vop_ctrl rk3288_ctrl_data = { + .dither_down = VOP_REG(RK3288_DSP_CTRL1, 0xf, 1), + .dither_up = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6), + .data_blank = VOP_REG(RK3288_DSP_CTRL0, 0x1, 19), ++ .dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18), + .out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0), + .pin_pol = VOP_REG(RK3288_DSP_CTRL0, 0xf, 4), + .htotal_pw = VOP_REG(RK3288_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), +@@ -224,22 +221,10 @@ static const struct vop_ctrl rk3288_ctrl_data = { + .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0), + .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12), ++ .global_regdone_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 11), + .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0), + }; + +-static const struct vop_reg_data rk3288_init_reg_table[] = { +- {RK3288_SYS_CTRL, 0x00c00000}, +- {RK3288_DSP_CTRL0, 0x00000000}, +- {RK3288_WIN0_CTRL0, 0x00000080}, +- {RK3288_WIN1_CTRL0, 0x00000080}, +- /* TODO: Win2/3 support multiple area function, but we haven't found +- * a suitable way to use it yet, so let's just use them as other windows +- * with only area 0 enabled. +- */ +- {RK3288_WIN2_CTRL0, 0x00000010}, +- {RK3288_WIN3_CTRL0, 0x00000010}, +-}; +- + /* + * Note: rk3288 has a dedicated 'cursor' window, however, that window requires + * special support to get alpha blending working. For now, just use overlay +@@ -273,8 +258,6 @@ static const struct vop_intr rk3288_vop_intr = { + }; + + static const struct vop_data rk3288_vop = { +- .init_table = rk3288_init_reg_table, +- .table_size = ARRAY_SIZE(rk3288_init_reg_table), + .feature = VOP_FEATURE_OUTPUT_RGB10, + .intr = &rk3288_vop_intr, + .ctrl = &rk3288_ctrl_data, +@@ -328,22 +311,7 @@ static const struct vop_intr rk3399_vop_intr = { + .clear = VOP_REG_MASK(RK3399_INTR_CLEAR0, 0xffff, 0), + }; + +-static const struct vop_reg_data rk3399_init_reg_table[] = { +- {RK3399_SYS_CTRL, 0x2000f800}, +- {RK3399_DSP_CTRL0, 0x00000000}, +- {RK3399_WIN0_CTRL0, 0x00000080}, +- {RK3399_WIN1_CTRL0, 0x00000080}, +- /* TODO: Win2/3 support multiple area function, but we haven't found +- * a suitable way to use it yet, so let's just use them as other windows +- * with only area 0 enabled. +- */ +- {RK3399_WIN2_CTRL0, 0x00000010}, +- {RK3399_WIN3_CTRL0, 0x00000010}, +-}; +- + static const struct vop_data rk3399_vop_big = { +- .init_table = rk3399_init_reg_table, +- .table_size = ARRAY_SIZE(rk3399_init_reg_table), + .feature = VOP_FEATURE_OUTPUT_RGB10, + .intr = &rk3399_vop_intr, + .ctrl = &rk3399_ctrl_data, +@@ -362,8 +330,6 @@ static const struct vop_win_data rk3399_vop_lit_win_data[] = { + }; + + static const struct vop_data rk3399_vop_lit = { +- .init_table = rk3399_init_reg_table, +- .table_size = ARRAY_SIZE(rk3399_init_reg_table), + .intr = &rk3399_vop_intr, + .ctrl = &rk3399_ctrl_data, + /* +-- +2.11.0 + diff --git a/patches.drivers/0066-drm-rockchip-vop-move-write_relaxed-flags-to-vop-reg.patch b/patches.drivers/0066-drm-rockchip-vop-move-write_relaxed-flags-to-vop-reg.patch new file mode 100644 index 0000000..65bf0da --- /dev/null +++ b/patches.drivers/0066-drm-rockchip-vop-move-write_relaxed-flags-to-vop-reg.patch @@ -0,0 +1,198 @@ +From 432035d93eff90749504bee67bd9c231a49bdb90 Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Wed, 26 Jul 2017 14:19:12 +0800 +Subject: [PATCH 66/86] drm/rockchip: vop: move write_relaxed flags to vop + register + +Git-commit: 9548e1b49a0cb2eb0cec1cf2560c920fe2954608 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Since the drm atomic framework, only a small part of the vop +register needs sync write, Currently seems only following registers +need sync write: + cfg_done, standby and interrupt related register. + +All ctrl registers are using the sync write method that is +inefficient, hardcode the write_relaxed flags to vop registers, +then can only do synchronize write for those actual needed register. + +Signed-off-by: Mark Yao +Tested-by: Heiko Stuebner +Reviewed-by: Jeffy Chen +Link: https://patchwork.freedesktop.org/patch/msgid/1501049953-5946-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 28 ++++++++----------- + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 + + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 42 ++++++++++++++++------------- + 3 files changed, 36 insertions(+), 35 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index b44cb7c80eda..d30bd3a85e99 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -42,33 +42,27 @@ + #include "rockchip_drm_psr.h" + #include "rockchip_drm_vop.h" + +-#define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \ +- vop_mask_write(x, off, mask, shift, v, write_mask, true) +- +-#define __REG_SET_NORMAL(x, off, mask, shift, v, write_mask) \ +- vop_mask_write(x, off, mask, shift, v, write_mask, false) +- +-#define REG_SET(x, base, reg, v, mode) \ +- __REG_SET_##mode(x, base + reg.offset, \ +- reg.mask, reg.shift, v, reg.write_mask) +-#define REG_SET_MASK(x, base, reg, mask, v, mode) \ +- __REG_SET_##mode(x, base + reg.offset, \ +- mask, reg.shift, v, reg.write_mask) ++#define REG_SET(x, base, reg, v) \ ++ vop_mask_write(x, base + reg.offset, reg.mask, reg.shift, \ ++ v, reg.write_mask, reg.relaxed) ++#define REG_SET_MASK(x, base, reg, mask, v) \ ++ vop_mask_write(x, base + reg.offset, \ ++ mask, reg.shift, v, reg.write_mask, reg.relaxed) + + #define VOP_WIN_SET(x, win, name, v) \ +- REG_SET(x, win->base, win->phy->name, v, RELAXED) ++ REG_SET(x, win->base, win->phy->name, v) + #define VOP_SCL_SET(x, win, name, v) \ +- REG_SET(x, win->base, win->phy->scl->name, v, RELAXED) ++ REG_SET(x, win->base, win->phy->scl->name, v) + #define VOP_SCL_SET_EXT(x, win, name, v) \ +- REG_SET(x, win->base, win->phy->scl->ext->name, v, RELAXED) ++ REG_SET(x, win->base, win->phy->scl->ext->name, v) + #define VOP_CTRL_SET(x, name, v) \ +- REG_SET(x, 0, (x)->data->ctrl->name, v, NORMAL) ++ REG_SET(x, 0, (x)->data->ctrl->name, v) + + #define VOP_INTR_GET(vop, name) \ + vop_read_reg(vop, 0, &vop->data->ctrl->name) + + #define VOP_INTR_SET(vop, name, mask, v) \ +- REG_SET_MASK(vop, 0, vop->data->intr->name, mask, v, NORMAL) ++ REG_SET_MASK(vop, 0, vop->data->intr->name, mask, v) + #define VOP_INTR_SET_TYPE(vop, name, type, v) \ + do { \ + int i, reg = 0, mask = 0; \ +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 084d3b25a120..056b9744802a 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -29,6 +29,7 @@ struct vop_reg { + uint32_t shift; + uint32_t mask; + bool write_mask; ++ bool relaxed; + }; + + struct vop_ctrl { +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +index 58da8559dc89..d7974da29f61 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +@@ -20,17 +20,23 @@ + #include "rockchip_drm_vop.h" + #include "rockchip_vop_reg.h" + +-#define VOP_REG(off, _mask, s) \ +- {.offset = off, \ ++#define _VOP_REG(off, _mask, _shift, _write_mask, _relaxed) \ ++ { \ ++ .offset = off, \ + .mask = _mask, \ +- .shift = s, \ +- .write_mask = false,} ++ .shift = _shift, \ ++ .write_mask = _write_mask, \ ++ .relaxed = _relaxed, \ ++ } + +-#define VOP_REG_MASK(off, _mask, s) \ +- {.offset = off, \ +- .mask = _mask, \ +- .shift = s, \ +- .write_mask = true,} ++#define VOP_REG(off, _mask, _shift) \ ++ _VOP_REG(off, _mask, _shift, false, true) ++ ++#define VOP_REG_SYNC(off, _mask, _shift) \ ++ _VOP_REG(off, _mask, _shift, false, false) ++ ++#define VOP_REG_MASK_SYNC(off, _mask, _shift) \ ++ _VOP_REG(off, _mask, _shift, true, false) + + static const uint32_t formats_win_full[] = { + DRM_FORMAT_XRGB8888, +@@ -116,7 +122,7 @@ static const struct vop_intr rk3036_intr = { + }; + + static const struct vop_ctrl rk3036_ctrl_data = { +- .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30), ++ .standby = VOP_REG_SYNC(RK3036_SYS_CTRL, 0x1, 30), + .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), + .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), + .dsp_blank = VOP_REG(RK3036_DSP_CTRL1, 0x1, 24), +@@ -125,7 +131,7 @@ static const struct vop_ctrl rk3036_ctrl_data = { + .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), + .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12), +- .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0), ++ .cfg_done = VOP_REG_SYNC(RK3036_REG_CFG_DONE, 0x1, 0), + }; + + static const struct vop_data rk3036_vop = { +@@ -201,7 +207,7 @@ static const struct vop_win_phy rk3288_win23_data = { + }; + + static const struct vop_ctrl rk3288_ctrl_data = { +- .standby = VOP_REG(RK3288_SYS_CTRL, 0x1, 22), ++ .standby = VOP_REG_SYNC(RK3288_SYS_CTRL, 0x1, 22), + .gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23), + .mmu_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 20), + .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), +@@ -222,7 +228,7 @@ static const struct vop_ctrl rk3288_ctrl_data = { + .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12), + .global_regdone_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 11), +- .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0), ++ .cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0), + }; + + /* +@@ -266,7 +272,7 @@ static const struct vop_data rk3288_vop = { + }; + + static const struct vop_ctrl rk3399_ctrl_data = { +- .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22), ++ .standby = VOP_REG_SYNC(RK3399_SYS_CTRL, 0x1, 22), + .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23), + .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11), + .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12), +@@ -290,7 +296,7 @@ static const struct vop_ctrl rk3399_ctrl_data = { + .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0), + .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0), + .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16), +- .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0), ++ .cfg_done = VOP_REG_MASK_SYNC(RK3399_REG_CFG_DONE, 0x1, 0), + }; + + static const int rk3399_vop_intrs[] = { +@@ -306,9 +312,9 @@ static const int rk3399_vop_intrs[] = { + static const struct vop_intr rk3399_vop_intr = { + .intrs = rk3399_vop_intrs, + .nintrs = ARRAY_SIZE(rk3399_vop_intrs), +- .status = VOP_REG_MASK(RK3399_INTR_STATUS0, 0xffff, 0), +- .enable = VOP_REG_MASK(RK3399_INTR_EN0, 0xffff, 0), +- .clear = VOP_REG_MASK(RK3399_INTR_CLEAR0, 0xffff, 0), ++ .status = VOP_REG_MASK_SYNC(RK3399_INTR_STATUS0, 0xffff, 0), ++ .enable = VOP_REG_MASK_SYNC(RK3399_INTR_EN0, 0xffff, 0), ++ .clear = VOP_REG_MASK_SYNC(RK3399_INTR_CLEAR0, 0xffff, 0), + }; + + static const struct vop_data rk3399_vop_big = { +-- +2.11.0 + diff --git a/patches.drivers/0067-drm-rockchip-vop-move-line_flag_num-to-interrupt-reg.patch b/patches.drivers/0067-drm-rockchip-vop-move-line_flag_num-to-interrupt-reg.patch new file mode 100644 index 0000000..bfbc19c --- /dev/null +++ b/patches.drivers/0067-drm-rockchip-vop-move-line_flag_num-to-interrupt-reg.patch @@ -0,0 +1,141 @@ +From adaca390178a294ca328273e662fd4be30c39128 Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Wed, 26 Jul 2017 14:19:19 +0800 +Subject: [PATCH 67/86] drm/rockchip: vop: move line_flag_num to interrupt + registers + +Git-commit: ac6560dfc8ab5dd57220ed21653d84481e13a6d2 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +In the hardware design process, the design of line flags +register is associated with the interrupt register, +placing the line flags in the interrupt definition is +more reasonable, and it would make multi-vop define easilier. + +Signed-off-by: Mark Yao +Reviewed-by: Sean Paul +Tested-by: Heiko Stuebner +Link: https://patchwork.freedesktop.org/patch/msgid/1501049960-6006-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 10 +++++++--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 4 ++-- + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 8 ++++---- + 3 files changed, 13 insertions(+), 9 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index d30bd3a85e99..ae1bcd642df3 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -61,8 +61,12 @@ + #define VOP_INTR_GET(vop, name) \ + vop_read_reg(vop, 0, &vop->data->ctrl->name) + +-#define VOP_INTR_SET(vop, name, mask, v) \ ++#define VOP_INTR_SET(vop, name, v) \ ++ REG_SET(vop, 0, vop->data->intr->name, v) ++ ++#define VOP_INTR_SET_MASK(vop, name, mask, v) \ + REG_SET_MASK(vop, 0, vop->data->intr->name, mask, v) ++ + #define VOP_INTR_SET_TYPE(vop, name, type, v) \ + do { \ + int i, reg = 0, mask = 0; \ +@@ -72,7 +76,7 @@ + mask |= 1 << i; \ + } \ + } \ +- VOP_INTR_SET(vop, name, mask, reg); \ ++ VOP_INTR_SET_MASK(vop, name, mask, reg); \ + } while (0) + #define VOP_INTR_GET_TYPE(vop, name, type) \ + vop_get_intr_type(vop, &vop->data->intr->name, type) +@@ -982,7 +986,7 @@ static void vop_crtc_enable(struct drm_crtc *crtc) + VOP_CTRL_SET(vop, vact_st_end, val); + VOP_CTRL_SET(vop, vpost_st_end, val); + +- VOP_CTRL_SET(vop, line_flag_num[0], vact_end); ++ VOP_INTR_SET(vop, line_flag_num[0], vact_end); + + clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 056b9744802a..850f8e4598e9 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -60,8 +60,6 @@ struct vop_ctrl { + struct vop_reg hpost_st_end; + struct vop_reg vpost_st_end; + +- struct vop_reg line_flag_num[2]; +- + struct vop_reg global_regdone_en; + struct vop_reg cfg_done; + }; +@@ -69,6 +67,8 @@ struct vop_ctrl { + struct vop_intr { + const int *intrs; + uint32_t nintrs; ++ ++ struct vop_reg line_flag_num[2]; + struct vop_reg enable; + struct vop_reg clear; + struct vop_reg status; +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +index d7974da29f61..0a5f0d2b43d1 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +@@ -116,6 +116,7 @@ static const int rk3036_vop_intrs[] = { + static const struct vop_intr rk3036_intr = { + .intrs = rk3036_vop_intrs, + .nintrs = ARRAY_SIZE(rk3036_vop_intrs), ++ .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12), + .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0), + .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4), + .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8), +@@ -130,7 +131,6 @@ static const struct vop_ctrl rk3036_ctrl_data = { + .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), +- .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12), + .cfg_done = VOP_REG_SYNC(RK3036_REG_CFG_DONE, 0x1, 0), + }; + +@@ -226,7 +226,6 @@ static const struct vop_ctrl rk3288_ctrl_data = { + .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0), + .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0), + .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), +- .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12), + .global_regdone_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 11), + .cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0), + }; +@@ -258,6 +257,7 @@ static const int rk3288_vop_intrs[] = { + static const struct vop_intr rk3288_vop_intr = { + .intrs = rk3288_vop_intrs, + .nintrs = ARRAY_SIZE(rk3288_vop_intrs), ++ .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12), + .status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0), + .enable = VOP_REG(RK3288_INTR_CTRL0, 0xf, 4), + .clear = VOP_REG(RK3288_INTR_CTRL0, 0xf, 8), +@@ -294,8 +294,6 @@ static const struct vop_ctrl rk3399_ctrl_data = { + .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0), + .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0), + .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0), +- .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0), +- .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16), + .cfg_done = VOP_REG_MASK_SYNC(RK3399_REG_CFG_DONE, 0x1, 0), + }; + +@@ -312,6 +310,8 @@ static const int rk3399_vop_intrs[] = { + static const struct vop_intr rk3399_vop_intr = { + .intrs = rk3399_vop_intrs, + .nintrs = ARRAY_SIZE(rk3399_vop_intrs), ++ .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0), ++ .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16), + .status = VOP_REG_MASK_SYNC(RK3399_INTR_STATUS0, 0xffff, 0), + .enable = VOP_REG_MASK_SYNC(RK3399_INTR_EN0, 0xffff, 0), + .clear = VOP_REG_MASK_SYNC(RK3399_INTR_CLEAR0, 0xffff, 0), +-- +2.11.0 + diff --git a/patches.drivers/0068-drm-rockchip-vop-group-vop-registers.patch b/patches.drivers/0068-drm-rockchip-vop-group-vop-registers.patch new file mode 100644 index 0000000..a935280 --- /dev/null +++ b/patches.drivers/0068-drm-rockchip-vop-group-vop-registers.patch @@ -0,0 +1,509 @@ +From aa976995ac6978fe583a5b3ba04d767e6eaf1a9b Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Fri, 28 Jul 2017 14:06:25 +0800 +Subject: [PATCH 68/86] drm/rockchip: vop: group vop registers + +Git-commit: 9a61c54b9bff88e692ac7b1245546539ac5274a1 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Grouping the vop registers facilitates make register +definition clearer, and also is useful for different vop +reuse the same group register. + +Signed-off-by: Mark Yao +Reviewed-by: Jeffy Chen +Tested-by: Heiko Stuebner +Link: https://patchwork.freedesktop.org/patch/msgid/1501221986-29722-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 101 ++++++++++++------------- + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 60 ++++++++------- + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 112 +++++++++++++++------------- + 3 files changed, 146 insertions(+), 127 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index ae1bcd642df3..b80d581e05fd 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -42,30 +42,19 @@ + #include "rockchip_drm_psr.h" + #include "rockchip_drm_vop.h" + +-#define REG_SET(x, base, reg, v) \ +- vop_mask_write(x, base + reg.offset, reg.mask, reg.shift, \ +- v, reg.write_mask, reg.relaxed) +-#define REG_SET_MASK(x, base, reg, mask, v) \ +- vop_mask_write(x, base + reg.offset, \ +- mask, reg.shift, v, reg.write_mask, reg.relaxed) +- + #define VOP_WIN_SET(x, win, name, v) \ +- REG_SET(x, win->base, win->phy->name, v) ++ vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name) + #define VOP_SCL_SET(x, win, name, v) \ +- REG_SET(x, win->base, win->phy->scl->name, v) ++ vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name) + #define VOP_SCL_SET_EXT(x, win, name, v) \ +- REG_SET(x, win->base, win->phy->scl->ext->name, v) +-#define VOP_CTRL_SET(x, name, v) \ +- REG_SET(x, 0, (x)->data->ctrl->name, v) +- +-#define VOP_INTR_GET(vop, name) \ +- vop_read_reg(vop, 0, &vop->data->ctrl->name) +- +-#define VOP_INTR_SET(vop, name, v) \ +- REG_SET(vop, 0, vop->data->intr->name, v) ++ vop_reg_set(vop, &win->phy->scl->ext->name, \ ++ win->base, ~0, v, #name) + + #define VOP_INTR_SET_MASK(vop, name, mask, v) \ +- REG_SET_MASK(vop, 0, vop->data->intr->name, mask, v) ++ vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name) ++ ++#define VOP_REG_SET(vop, group, name, v) \ ++ vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name) + + #define VOP_INTR_SET_TYPE(vop, name, type, v) \ + do { \ +@@ -82,7 +71,7 @@ + vop_get_intr_type(vop, &vop->data->intr->name, type) + + #define VOP_WIN_GET(x, win, name) \ +- vop_read_reg(x, win->base, &win->phy->name) ++ vop_read_reg(x, win->offset, win->phy->name) + + #define VOP_WIN_GET_YRGBADDR(vop, win) \ + vop_readl(vop, win->base + win->phy->yrgb_mst.offset) +@@ -164,14 +153,22 @@ static inline uint32_t vop_read_reg(struct vop *vop, uint32_t base, + return (vop_readl(vop, base + reg->offset) >> reg->shift) & reg->mask; + } + +-static inline void vop_mask_write(struct vop *vop, uint32_t offset, +- uint32_t mask, uint32_t shift, uint32_t v, +- bool write_mask, bool relaxed) ++static void vop_reg_set(struct vop *vop, const struct vop_reg *reg, ++ uint32_t _offset, uint32_t _mask, uint32_t v, ++ const char *reg_name) + { +- if (!mask) ++ int offset, mask, shift; ++ ++ if (!reg || !reg->mask) { ++ dev_dbg(vop->dev, "Warning: not support %s\n", reg_name); + return; ++ } ++ ++ offset = reg->offset + _offset; ++ mask = reg->mask & _mask; ++ shift = reg->shift; + +- if (write_mask) { ++ if (reg->write_mask) { + v = ((v << shift) & 0xffff) | (mask << (shift + 16)); + } else { + uint32_t cached_val = vop->regsbak[offset >> 2]; +@@ -180,7 +177,7 @@ static inline void vop_mask_write(struct vop *vop, uint32_t offset, + vop->regsbak[offset >> 2] = v; + } + +- if (relaxed) ++ if (reg->relaxed) + writel_relaxed(v, vop->regs + offset); + else + writel(v, vop->regs + offset); +@@ -202,7 +199,7 @@ static inline uint32_t vop_get_intr_type(struct vop *vop, + + static inline void vop_cfg_done(struct vop *vop) + { +- VOP_CTRL_SET(vop, cfg_done, 1); ++ VOP_REG_SET(vop, common, cfg_done, 1); + } + + static bool has_rb_swapped(uint32_t format) +@@ -540,7 +537,7 @@ static int vop_enable(struct drm_crtc *crtc) + + spin_lock(&vop->reg_lock); + +- VOP_CTRL_SET(vop, standby, 0); ++ VOP_REG_SET(vop, common, standby, 1); + + spin_unlock(&vop->reg_lock); + +@@ -600,7 +597,7 @@ static void vop_crtc_disable(struct drm_crtc *crtc) + + spin_lock(&vop->reg_lock); + +- VOP_CTRL_SET(vop, standby, 1); ++ VOP_REG_SET(vop, common, standby, 1); + + spin_unlock(&vop->reg_lock); + +@@ -923,7 +920,7 @@ static void vop_crtc_enable(struct drm_crtc *crtc) + + spin_lock(&vop->reg_lock); + +- VOP_CTRL_SET(vop, standby, 1); ++ VOP_REG_SET(vop, common, standby, 1); + + spin_unlock(&vop->reg_lock); + +@@ -937,29 +934,29 @@ static void vop_crtc_enable(struct drm_crtc *crtc) + BIT(HSYNC_POSITIVE) : 0; + pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ? + BIT(VSYNC_POSITIVE) : 0; +- VOP_CTRL_SET(vop, pin_pol, pin_pol); ++ VOP_REG_SET(vop, output, pin_pol, pin_pol); + + switch (s->output_type) { + case DRM_MODE_CONNECTOR_LVDS: +- VOP_CTRL_SET(vop, rgb_en, 1); +- VOP_CTRL_SET(vop, rgb_pin_pol, pin_pol); ++ VOP_REG_SET(vop, output, rgb_en, 1); ++ VOP_REG_SET(vop, output, rgb_pin_pol, pin_pol); + break; + case DRM_MODE_CONNECTOR_eDP: +- VOP_CTRL_SET(vop, edp_pin_pol, pin_pol); +- VOP_CTRL_SET(vop, edp_en, 1); ++ VOP_REG_SET(vop, output, edp_pin_pol, pin_pol); ++ VOP_REG_SET(vop, output, edp_en, 1); + break; + case DRM_MODE_CONNECTOR_HDMIA: +- VOP_CTRL_SET(vop, hdmi_pin_pol, pin_pol); +- VOP_CTRL_SET(vop, hdmi_en, 1); ++ VOP_REG_SET(vop, output, hdmi_pin_pol, pin_pol); ++ VOP_REG_SET(vop, output, hdmi_en, 1); + break; + case DRM_MODE_CONNECTOR_DSI: +- VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol); +- VOP_CTRL_SET(vop, mipi_en, 1); ++ VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol); ++ VOP_REG_SET(vop, output, mipi_en, 1); + break; + case DRM_MODE_CONNECTOR_DisplayPort: + pin_pol &= ~BIT(DCLK_INVERT); +- VOP_CTRL_SET(vop, dp_pin_pol, pin_pol); +- VOP_CTRL_SET(vop, dp_en, 1); ++ VOP_REG_SET(vop, output, dp_pin_pol, pin_pol); ++ VOP_REG_SET(vop, output, dp_en, 1); + break; + default: + DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n", +@@ -972,25 +969,25 @@ static void vop_crtc_enable(struct drm_crtc *crtc) + if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && + !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10)) + s->output_mode = ROCKCHIP_OUT_MODE_P888; +- VOP_CTRL_SET(vop, out_mode, s->output_mode); ++ VOP_REG_SET(vop, common, out_mode, s->output_mode); + +- VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len); ++ VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len); + val = hact_st << 16; + val |= hact_end; +- VOP_CTRL_SET(vop, hact_st_end, val); +- VOP_CTRL_SET(vop, hpost_st_end, val); ++ VOP_REG_SET(vop, modeset, hact_st_end, val); ++ VOP_REG_SET(vop, modeset, hpost_st_end, val); + +- VOP_CTRL_SET(vop, vtotal_pw, (vtotal << 16) | vsync_len); ++ VOP_REG_SET(vop, modeset, vtotal_pw, (vtotal << 16) | vsync_len); + val = vact_st << 16; + val |= vact_end; +- VOP_CTRL_SET(vop, vact_st_end, val); +- VOP_CTRL_SET(vop, vpost_st_end, val); ++ VOP_REG_SET(vop, modeset, vact_st_end, val); ++ VOP_REG_SET(vop, modeset, vpost_st_end, val); + +- VOP_INTR_SET(vop, line_flag_num[0], vact_end); ++ VOP_REG_SET(vop, intr, line_flag_num[0], vact_end); + + clk_set_rate(vop->dclk, adjusted_mode->clock * 1000); + +- VOP_CTRL_SET(vop, standby, 0); ++ VOP_REG_SET(vop, common, standby, 0); + + rockchip_drm_psr_activate(&vop->crtc); + } +@@ -1453,8 +1450,8 @@ static int vop_initial(struct vop *vop) + + memcpy(vop->regsbak, vop->regs, vop->len); + +- VOP_CTRL_SET(vop, global_regdone_en, 1); +- VOP_CTRL_SET(vop, dsp_blank, 0); ++ VOP_REG_SET(vop, misc, global_regdone_en, 1); ++ VOP_REG_SET(vop, common, dsp_blank, 0); + + for (i = 0; i < vop_data->win_size; i++) { + const struct vop_win_data *win = &vop_data->win[i]; +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 850f8e4598e9..3ba962c72c07 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -25,43 +25,50 @@ enum vop_data_format { + }; + + struct vop_reg { +- uint32_t offset; +- uint32_t shift; + uint32_t mask; ++ uint16_t offset; ++ uint8_t shift; + bool write_mask; + bool relaxed; + }; + +-struct vop_ctrl { +- struct vop_reg standby; +- struct vop_reg data_blank; +- struct vop_reg gate_en; +- struct vop_reg mmu_en; +- struct vop_reg rgb_en; ++struct vop_modeset { ++ struct vop_reg htotal_pw; ++ struct vop_reg hact_st_end; ++ struct vop_reg hpost_st_end; ++ struct vop_reg vtotal_pw; ++ struct vop_reg vact_st_end; ++ struct vop_reg vpost_st_end; ++}; ++ ++struct vop_output { ++ struct vop_reg pin_pol; ++ struct vop_reg dp_pin_pol; ++ struct vop_reg edp_pin_pol; ++ struct vop_reg hdmi_pin_pol; ++ struct vop_reg mipi_pin_pol; ++ struct vop_reg rgb_pin_pol; ++ struct vop_reg dp_en; + struct vop_reg edp_en; + struct vop_reg hdmi_en; + struct vop_reg mipi_en; +- struct vop_reg dp_en; ++ struct vop_reg rgb_en; ++}; ++ ++struct vop_common { ++ struct vop_reg cfg_done; + struct vop_reg dsp_blank; +- struct vop_reg out_mode; ++ struct vop_reg data_blank; + struct vop_reg dither_down; + struct vop_reg dither_up; +- struct vop_reg pin_pol; +- struct vop_reg rgb_pin_pol; +- struct vop_reg hdmi_pin_pol; +- struct vop_reg edp_pin_pol; +- struct vop_reg mipi_pin_pol; +- struct vop_reg dp_pin_pol; +- +- struct vop_reg htotal_pw; +- struct vop_reg hact_st_end; +- struct vop_reg vtotal_pw; +- struct vop_reg vact_st_end; +- struct vop_reg hpost_st_end; +- struct vop_reg vpost_st_end; ++ struct vop_reg gate_en; ++ struct vop_reg mmu_en; ++ struct vop_reg out_mode; ++ struct vop_reg standby; ++}; + ++struct vop_misc { + struct vop_reg global_regdone_en; +- struct vop_reg cfg_done; + }; + + struct vop_intr { +@@ -135,8 +142,11 @@ struct vop_win_data { + }; + + struct vop_data { +- const struct vop_ctrl *ctrl; + const struct vop_intr *intr; ++ const struct vop_common *common; ++ const struct vop_misc *misc; ++ const struct vop_modeset *modeset; ++ const struct vop_output *output; + const struct vop_win_data *win; + unsigned int win_size; + +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +index 0a5f0d2b43d1..20607a86e250 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +@@ -117,26 +117,34 @@ static const struct vop_intr rk3036_intr = { + .intrs = rk3036_vop_intrs, + .nintrs = ARRAY_SIZE(rk3036_vop_intrs), + .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12), +- .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0), +- .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4), +- .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8), ++ .status = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 0), ++ .enable = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 4), ++ .clear = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 8), + }; + +-static const struct vop_ctrl rk3036_ctrl_data = { +- .standby = VOP_REG_SYNC(RK3036_SYS_CTRL, 0x1, 30), +- .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), +- .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), +- .dsp_blank = VOP_REG(RK3036_DSP_CTRL1, 0x1, 24), ++static const struct vop_modeset rk3036_modeset = { + .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), + .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0), + .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), + .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0), ++}; ++ ++static const struct vop_output rk3036_output = { ++ .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4), ++}; ++ ++static const struct vop_common rk3036_common = { ++ .standby = VOP_REG_SYNC(RK3036_SYS_CTRL, 0x1, 30), ++ .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0), ++ .dsp_blank = VOP_REG(RK3036_DSP_CTRL1, 0x1, 24), + .cfg_done = VOP_REG_SYNC(RK3036_REG_CFG_DONE, 0x1, 0), + }; + + static const struct vop_data rk3036_vop = { +- .ctrl = &rk3036_ctrl_data, + .intr = &rk3036_intr, ++ .common = &rk3036_common, ++ .modeset = &rk3036_modeset, ++ .output = &rk3036_output, + .win = rk3036_vop_win_data, + .win_size = ARRAY_SIZE(rk3036_vop_win_data), + }; +@@ -206,27 +214,32 @@ static const struct vop_win_phy rk3288_win23_data = { + .dst_alpha_ctl = VOP_REG(RK3288_WIN2_DST_ALPHA_CTRL, 0xff, 0), + }; + +-static const struct vop_ctrl rk3288_ctrl_data = { +- .standby = VOP_REG_SYNC(RK3288_SYS_CTRL, 0x1, 22), +- .gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23), +- .mmu_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 20), ++static const struct vop_modeset rk3288_modeset = { ++ .htotal_pw = VOP_REG(RK3288_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), ++ .hact_st_end = VOP_REG(RK3288_DSP_HACT_ST_END, 0x1fff1fff, 0), ++ .vtotal_pw = VOP_REG(RK3288_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), ++ .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0), ++ .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0), ++ .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), ++}; ++ ++static const struct vop_output rk3288_output = { ++ .pin_pol = VOP_REG(RK3288_DSP_CTRL0, 0xf, 4), + .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), + .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), + .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), + .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), ++}; ++ ++static const struct vop_common rk3288_common = { ++ .standby = VOP_REG_SYNC(RK3288_SYS_CTRL, 0x1, 22), ++ .gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23), ++ .mmu_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 20), + .dither_down = VOP_REG(RK3288_DSP_CTRL1, 0xf, 1), + .dither_up = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6), + .data_blank = VOP_REG(RK3288_DSP_CTRL0, 0x1, 19), + .dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18), + .out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0), +- .pin_pol = VOP_REG(RK3288_DSP_CTRL0, 0xf, 4), +- .htotal_pw = VOP_REG(RK3288_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), +- .hact_st_end = VOP_REG(RK3288_DSP_HACT_ST_END, 0x1fff1fff, 0), +- .vtotal_pw = VOP_REG(RK3288_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), +- .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0), +- .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0), +- .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0), +- .global_regdone_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 11), + .cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0), + }; + +@@ -266,37 +279,13 @@ static const struct vop_intr rk3288_vop_intr = { + static const struct vop_data rk3288_vop = { + .feature = VOP_FEATURE_OUTPUT_RGB10, + .intr = &rk3288_vop_intr, +- .ctrl = &rk3288_ctrl_data, ++ .common = &rk3288_common, ++ .modeset = &rk3288_modeset, ++ .output = &rk3288_output, + .win = rk3288_vop_win_data, + .win_size = ARRAY_SIZE(rk3288_vop_win_data), + }; + +-static const struct vop_ctrl rk3399_ctrl_data = { +- .standby = VOP_REG_SYNC(RK3399_SYS_CTRL, 0x1, 22), +- .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23), +- .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11), +- .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12), +- .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13), +- .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14), +- .mipi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 15), +- .dither_down = VOP_REG(RK3399_DSP_CTRL1, 0xf, 1), +- .dither_up = VOP_REG(RK3399_DSP_CTRL1, 0x1, 6), +- .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19), +- .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0), +- .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), +- .dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), +- .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20), +- .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24), +- .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28), +- .htotal_pw = VOP_REG(RK3399_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), +- .hact_st_end = VOP_REG(RK3399_DSP_HACT_ST_END, 0x1fff1fff, 0), +- .vtotal_pw = VOP_REG(RK3399_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), +- .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0), +- .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0), +- .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0), +- .cfg_done = VOP_REG_MASK_SYNC(RK3399_REG_CFG_DONE, 0x1, 0), +-}; +- + static const int rk3399_vop_intrs[] = { + FS_INTR, + 0, 0, +@@ -317,10 +306,30 @@ static const struct vop_intr rk3399_vop_intr = { + .clear = VOP_REG_MASK_SYNC(RK3399_INTR_CLEAR0, 0xffff, 0), + }; + ++static const struct vop_output rk3399_output = { ++ .dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), ++ .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), ++ .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20), ++ .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24), ++ .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28), ++ .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11), ++ .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), ++ .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), ++ .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), ++ .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), ++}; ++ ++static const struct vop_misc rk3399_misc = { ++ .global_regdone_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11), ++}; ++ + static const struct vop_data rk3399_vop_big = { + .feature = VOP_FEATURE_OUTPUT_RGB10, + .intr = &rk3399_vop_intr, +- .ctrl = &rk3399_ctrl_data, ++ .common = &rk3288_common, ++ .modeset = &rk3288_modeset, ++ .output = &rk3399_output, ++ .misc = &rk3399_misc, + /* + * rk3399 vop big windows register layout is same as rk3288. + */ +@@ -337,7 +346,10 @@ static const struct vop_win_data rk3399_vop_lit_win_data[] = { + + static const struct vop_data rk3399_vop_lit = { + .intr = &rk3399_vop_intr, +- .ctrl = &rk3399_ctrl_data, ++ .common = &rk3288_common, ++ .modeset = &rk3288_modeset, ++ .output = &rk3399_output, ++ .misc = &rk3399_misc, + /* + * rk3399 vop lit windows register layout is same as rk3288, + * but cut off the win1 and win3 windows. +-- +2.11.0 + diff --git a/patches.drivers/0069-drm-rockchip-vop-add-a-series-of-vop-support.patch b/patches.drivers/0069-drm-rockchip-vop-add-a-series-of-vop-support.patch new file mode 100644 index 0000000..8ffc6ec --- /dev/null +++ b/patches.drivers/0069-drm-rockchip-vop-add-a-series-of-vop-support.patch @@ -0,0 +1,1281 @@ +From 4dccf6251993b6bc83dad18c5f7b918d04a126ed Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Wed, 26 Jul 2017 14:19:30 +0800 +Subject: [PATCH 69/86] drm/rockchip: vop: add a series of vop support + +Git-commit: eb5cb6aa9a72a7c23b733d858469963067551b33 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Vop Full framework now has following vops: +IP version chipname + 3.1 rk3288 + 3.2 rk3368 + 3.4 rk3366 + 3.5 rk3399 big + 3.6 rk3399 lit + 3.7 rk3228 + 3.8 rk3328 + +The above IP version is from H/W define, some of vop support get +the IP version from VERSION_INFO register, some are not. +hardcode the IP version for each vop to identify them. + +major version: used for IP structure, Vop full framework is 3, + vop little framework is 2. +minor version: on same structure, newer design vop will bigger + then old one. + +Signed-off-by: Mark Yao +Reviewed-by: Jeffy Chen +Tested-by: Heiko Stuebner +Link: https://patchwork.freedesktop.org/patch/msgid/1501049971-6131-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 9 + + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 218 ++++++- + drivers/gpu/drm/rockchip/rockchip_vop_reg.h | 905 ++++++++++++++++++++++------ + 3 files changed, 908 insertions(+), 224 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 3ba962c72c07..43d08c88f1f8 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -15,6 +15,14 @@ + #ifndef _ROCKCHIP_DRM_VOP_H + #define _ROCKCHIP_DRM_VOP_H + ++/* ++ * major: IP major version, used for IP structure ++ * minor: big feature change under same structure ++ */ ++#define VOP_VERSION(major, minor) ((major) << 8 | (minor)) ++#define VOP_MAJOR(version) ((version) >> 8) ++#define VOP_MINOR(version) ((version) & 0xff) ++ + enum vop_data_format { + VOP_FMT_ARGB8888 = 0, + VOP_FMT_RGB888, +@@ -142,6 +150,7 @@ struct vop_win_data { + }; + + struct vop_data { ++ uint32_t version; + const struct vop_intr *intr; + const struct vop_common *common; + const struct vop_misc *misc; +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +index 20607a86e250..bc7b2d086109 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +@@ -277,6 +277,7 @@ static const struct vop_intr rk3288_vop_intr = { + }; + + static const struct vop_data rk3288_vop = { ++ .version = VOP_VERSION(3, 1), + .feature = VOP_FEATURE_OUTPUT_RGB10, + .intr = &rk3288_vop_intr, + .common = &rk3288_common, +@@ -286,7 +287,7 @@ static const struct vop_data rk3288_vop = { + .win_size = ARRAY_SIZE(rk3288_vop_win_data), + }; + +-static const int rk3399_vop_intrs[] = { ++static const int rk3368_vop_intrs[] = { + FS_INTR, + 0, 0, + LINE_FLAG_INTR, +@@ -296,22 +297,95 @@ static const int rk3399_vop_intrs[] = { + DSP_HOLD_VALID_INTR, + }; + +-static const struct vop_intr rk3399_vop_intr = { +- .intrs = rk3399_vop_intrs, +- .nintrs = ARRAY_SIZE(rk3399_vop_intrs), +- .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0), +- .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16), +- .status = VOP_REG_MASK_SYNC(RK3399_INTR_STATUS0, 0xffff, 0), +- .enable = VOP_REG_MASK_SYNC(RK3399_INTR_EN0, 0xffff, 0), +- .clear = VOP_REG_MASK_SYNC(RK3399_INTR_CLEAR0, 0xffff, 0), ++static const struct vop_intr rk3368_vop_intr = { ++ .intrs = rk3368_vop_intrs, ++ .nintrs = ARRAY_SIZE(rk3368_vop_intrs), ++ .line_flag_num[0] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 0), ++ .line_flag_num[1] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 16), ++ .status = VOP_REG_MASK_SYNC(RK3368_INTR_STATUS, 0x3fff, 0), ++ .enable = VOP_REG_MASK_SYNC(RK3368_INTR_EN, 0x3fff, 0), ++ .clear = VOP_REG_MASK_SYNC(RK3368_INTR_CLEAR, 0x3fff, 0), ++}; ++ ++static const struct vop_win_phy rk3368_win23_data = { ++ .data_formats = formats_win_lite, ++ .nformats = ARRAY_SIZE(formats_win_lite), ++ .gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0), ++ .enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4), ++ .format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5), ++ .rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 20), ++ .dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO0, 0x0fff0fff, 0), ++ .dsp_st = VOP_REG(RK3368_WIN2_DSP_ST0, 0x1fff1fff, 0), ++ .yrgb_mst = VOP_REG(RK3368_WIN2_MST0, 0xffffffff, 0), ++ .yrgb_vir = VOP_REG(RK3368_WIN2_VIR0_1, 0x1fff, 0), ++ .src_alpha_ctl = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xff, 0), ++ .dst_alpha_ctl = VOP_REG(RK3368_WIN2_DST_ALPHA_CTRL, 0xff, 0), ++}; ++ ++static const struct vop_win_data rk3368_vop_win_data[] = { ++ { .base = 0x00, .phy = &rk3288_win01_data, ++ .type = DRM_PLANE_TYPE_PRIMARY }, ++ { .base = 0x40, .phy = &rk3288_win01_data, ++ .type = DRM_PLANE_TYPE_OVERLAY }, ++ { .base = 0x00, .phy = &rk3368_win23_data, ++ .type = DRM_PLANE_TYPE_OVERLAY }, ++ { .base = 0x50, .phy = &rk3368_win23_data, ++ .type = DRM_PLANE_TYPE_CURSOR }, ++}; ++ ++static const struct vop_output rk3368_output = { ++ .rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 16), ++ .hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 20), ++ .edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 24), ++ .mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 28), ++ .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), ++ .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), ++ .edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14), ++ .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), ++}; ++ ++static const struct vop_misc rk3368_misc = { ++ .global_regdone_en = VOP_REG(RK3368_SYS_CTRL, 0x1, 11), ++}; ++ ++static const struct vop_data rk3368_vop = { ++ .version = VOP_VERSION(3, 2), ++ .intr = &rk3368_vop_intr, ++ .common = &rk3288_common, ++ .modeset = &rk3288_modeset, ++ .output = &rk3368_output, ++ .misc = &rk3368_misc, ++ .win = rk3368_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3368_vop_win_data), ++}; ++ ++static const struct vop_intr rk3366_vop_intr = { ++ .intrs = rk3368_vop_intrs, ++ .nintrs = ARRAY_SIZE(rk3368_vop_intrs), ++ .line_flag_num[0] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 0), ++ .line_flag_num[1] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 16), ++ .status = VOP_REG_MASK_SYNC(RK3366_INTR_STATUS0, 0xffff, 0), ++ .enable = VOP_REG_MASK_SYNC(RK3366_INTR_EN0, 0xffff, 0), ++ .clear = VOP_REG_MASK_SYNC(RK3366_INTR_CLEAR0, 0xffff, 0), ++}; ++ ++static const struct vop_data rk3366_vop = { ++ .version = VOP_VERSION(3, 4), ++ .intr = &rk3366_vop_intr, ++ .common = &rk3288_common, ++ .modeset = &rk3288_modeset, ++ .output = &rk3368_output, ++ .misc = &rk3368_misc, ++ .win = rk3368_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3368_vop_win_data), + }; + + static const struct vop_output rk3399_output = { + .dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), +- .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16), +- .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20), +- .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24), +- .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28), ++ .rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 16), ++ .hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 20), ++ .edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 24), ++ .mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0xf, 28), + .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11), + .rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12), + .hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13), +@@ -319,54 +393,136 @@ static const struct vop_output rk3399_output = { + .mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15), + }; + +-static const struct vop_misc rk3399_misc = { +- .global_regdone_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11), +-}; +- + static const struct vop_data rk3399_vop_big = { ++ .version = VOP_VERSION(3, 5), + .feature = VOP_FEATURE_OUTPUT_RGB10, +- .intr = &rk3399_vop_intr, ++ .intr = &rk3366_vop_intr, + .common = &rk3288_common, + .modeset = &rk3288_modeset, + .output = &rk3399_output, +- .misc = &rk3399_misc, +- /* +- * rk3399 vop big windows register layout is same as rk3288. +- */ +- .win = rk3288_vop_win_data, +- .win_size = ARRAY_SIZE(rk3288_vop_win_data), ++ .misc = &rk3368_misc, ++ .win = rk3368_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3368_vop_win_data), + }; + + static const struct vop_win_data rk3399_vop_lit_win_data[] = { + { .base = 0x00, .phy = &rk3288_win01_data, + .type = DRM_PLANE_TYPE_PRIMARY }, +- { .base = 0x00, .phy = &rk3288_win23_data, ++ { .base = 0x00, .phy = &rk3368_win23_data, + .type = DRM_PLANE_TYPE_CURSOR}, + }; + + static const struct vop_data rk3399_vop_lit = { +- .intr = &rk3399_vop_intr, ++ .version = VOP_VERSION(3, 6), ++ .intr = &rk3366_vop_intr, + .common = &rk3288_common, + .modeset = &rk3288_modeset, + .output = &rk3399_output, +- .misc = &rk3399_misc, +- /* +- * rk3399 vop lit windows register layout is same as rk3288, +- * but cut off the win1 and win3 windows. +- */ ++ .misc = &rk3368_misc, + .win = rk3399_vop_lit_win_data, + .win_size = ARRAY_SIZE(rk3399_vop_lit_win_data), + }; + ++static const struct vop_win_data rk3228_vop_win_data[] = { ++ { .base = 0x00, .phy = &rk3288_win01_data, ++ .type = DRM_PLANE_TYPE_PRIMARY }, ++ { .base = 0x40, .phy = &rk3288_win01_data, ++ .type = DRM_PLANE_TYPE_CURSOR }, ++}; ++ ++static const struct vop_data rk3228_vop = { ++ .version = VOP_VERSION(3, 7), ++ .feature = VOP_FEATURE_OUTPUT_RGB10, ++ .intr = &rk3366_vop_intr, ++ .common = &rk3288_common, ++ .modeset = &rk3288_modeset, ++ .output = &rk3399_output, ++ .misc = &rk3368_misc, ++ .win = rk3228_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3228_vop_win_data), ++}; ++ ++static const struct vop_modeset rk3328_modeset = { ++ .htotal_pw = VOP_REG(RK3328_DSP_HTOTAL_HS_END, 0x1fff1fff, 0), ++ .hact_st_end = VOP_REG(RK3328_DSP_HACT_ST_END, 0x1fff1fff, 0), ++ .vtotal_pw = VOP_REG(RK3328_DSP_VTOTAL_VS_END, 0x1fff1fff, 0), ++ .vact_st_end = VOP_REG(RK3328_DSP_VACT_ST_END, 0x1fff1fff, 0), ++ .hpost_st_end = VOP_REG(RK3328_POST_DSP_HACT_INFO, 0x1fff1fff, 0), ++ .vpost_st_end = VOP_REG(RK3328_POST_DSP_VACT_INFO, 0x1fff1fff, 0), ++}; ++ ++static const struct vop_output rk3328_output = { ++ .rgb_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 12), ++ .hdmi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 13), ++ .edp_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 14), ++ .mipi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 15), ++ .rgb_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0xf, 16), ++ .hdmi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0xf, 20), ++ .edp_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0xf, 24), ++ .mipi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0xf, 28), ++}; ++ ++static const struct vop_misc rk3328_misc = { ++ .global_regdone_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 11), ++}; ++ ++static const struct vop_common rk3328_common = { ++ .standby = VOP_REG_SYNC(RK3328_SYS_CTRL, 0x1, 22), ++ .dither_down = VOP_REG(RK3328_DSP_CTRL1, 0xf, 1), ++ .dither_up = VOP_REG(RK3328_DSP_CTRL1, 0x1, 6), ++ .dsp_blank = VOP_REG(RK3328_DSP_CTRL0, 0x3, 18), ++ .out_mode = VOP_REG(RK3328_DSP_CTRL0, 0xf, 0), ++ .cfg_done = VOP_REG_SYNC(RK3328_REG_CFG_DONE, 0x1, 0), ++}; ++ ++static const struct vop_intr rk3328_vop_intr = { ++ .intrs = rk3368_vop_intrs, ++ .nintrs = ARRAY_SIZE(rk3368_vop_intrs), ++ .line_flag_num[0] = VOP_REG(RK3328_LINE_FLAG, 0xffff, 0), ++ .line_flag_num[1] = VOP_REG(RK3328_LINE_FLAG, 0xffff, 16), ++ .status = VOP_REG_MASK_SYNC(RK3328_INTR_STATUS0, 0xffff, 0), ++ .enable = VOP_REG_MASK_SYNC(RK3328_INTR_EN0, 0xffff, 0), ++ .clear = VOP_REG_MASK_SYNC(RK3328_INTR_CLEAR0, 0xffff, 0), ++}; ++ ++static const struct vop_win_data rk3328_vop_win_data[] = { ++ { .base = 0xd0, .phy = &rk3288_win01_data, ++ .type = DRM_PLANE_TYPE_PRIMARY }, ++ { .base = 0x1d0, .phy = &rk3288_win01_data, ++ .type = DRM_PLANE_TYPE_OVERLAY }, ++ { .base = 0x2d0, .phy = &rk3288_win01_data, ++ .type = DRM_PLANE_TYPE_CURSOR }, ++}; ++ ++static const struct vop_data rk3328_vop = { ++ .version = VOP_VERSION(3, 8), ++ .feature = VOP_FEATURE_OUTPUT_RGB10, ++ .intr = &rk3328_vop_intr, ++ .common = &rk3328_common, ++ .modeset = &rk3328_modeset, ++ .output = &rk3328_output, ++ .misc = &rk3328_misc, ++ .win = rk3328_vop_win_data, ++ .win_size = ARRAY_SIZE(rk3328_vop_win_data), ++}; ++ + static const struct of_device_id vop_driver_dt_match[] = { + { .compatible = "rockchip,rk3036-vop", + .data = &rk3036_vop }, + { .compatible = "rockchip,rk3288-vop", + .data = &rk3288_vop }, ++ { .compatible = "rockchip,rk3368-vop", ++ .data = &rk3368_vop }, ++ { .compatible = "rockchip,rk3366-vop", ++ .data = &rk3366_vop }, + { .compatible = "rockchip,rk3399-vop-big", + .data = &rk3399_vop_big }, + { .compatible = "rockchip,rk3399-vop-lit", + .data = &rk3399_vop_lit }, ++ { .compatible = "rockchip,rk3228-vop", ++ .data = &rk3228_vop }, ++ { .compatible = "rockchip,rk3328-vop", ++ .data = &rk3328_vop }, + {}, + }; + MODULE_DEVICE_TABLE(of, vop_driver_dt_match); +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +index cd197260ece5..4a4799ff65de 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h ++++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h +@@ -41,6 +41,7 @@ + #define RK3288_WIN0_SRC_ALPHA_CTRL 0x0060 + #define RK3288_WIN0_DST_ALPHA_CTRL 0x0064 + #define RK3288_WIN0_FADING_CTRL 0x0068 ++#define RK3288_WIN0_CTRL2 0x006c + + /* win1 register */ + #define RK3288_WIN1_CTRL0 0x0070 +@@ -122,6 +123,717 @@ + #define RK3288_DSP_VACT_ST_END_F1 0x019c + /* register definition end */ + ++/* rk3368 register definition */ ++#define RK3368_REG_CFG_DONE 0x0000 ++#define RK3368_VERSION_INFO 0x0004 ++#define RK3368_SYS_CTRL 0x0008 ++#define RK3368_SYS_CTRL1 0x000c ++#define RK3368_DSP_CTRL0 0x0010 ++#define RK3368_DSP_CTRL1 0x0014 ++#define RK3368_DSP_BG 0x0018 ++#define RK3368_MCU_CTRL 0x001c ++#define RK3368_LINE_FLAG 0x0020 ++#define RK3368_INTR_EN 0x0024 ++#define RK3368_INTR_CLEAR 0x0028 ++#define RK3368_INTR_STATUS 0x002c ++#define RK3368_WIN0_CTRL0 0x0030 ++#define RK3368_WIN0_CTRL1 0x0034 ++#define RK3368_WIN0_COLOR_KEY 0x0038 ++#define RK3368_WIN0_VIR 0x003c ++#define RK3368_WIN0_YRGB_MST 0x0040 ++#define RK3368_WIN0_CBR_MST 0x0044 ++#define RK3368_WIN0_ACT_INFO 0x0048 ++#define RK3368_WIN0_DSP_INFO 0x004c ++#define RK3368_WIN0_DSP_ST 0x0050 ++#define RK3368_WIN0_SCL_FACTOR_YRGB 0x0054 ++#define RK3368_WIN0_SCL_FACTOR_CBR 0x0058 ++#define RK3368_WIN0_SCL_OFFSET 0x005c ++#define RK3368_WIN0_SRC_ALPHA_CTRL 0x0060 ++#define RK3368_WIN0_DST_ALPHA_CTRL 0x0064 ++#define RK3368_WIN0_FADING_CTRL 0x0068 ++#define RK3368_WIN0_CTRL2 0x006c ++#define RK3368_WIN1_CTRL0 0x0070 ++#define RK3368_WIN1_CTRL1 0x0074 ++#define RK3368_WIN1_COLOR_KEY 0x0078 ++#define RK3368_WIN1_VIR 0x007c ++#define RK3368_WIN1_YRGB_MST 0x0080 ++#define RK3368_WIN1_CBR_MST 0x0084 ++#define RK3368_WIN1_ACT_INFO 0x0088 ++#define RK3368_WIN1_DSP_INFO 0x008c ++#define RK3368_WIN1_DSP_ST 0x0090 ++#define RK3368_WIN1_SCL_FACTOR_YRGB 0x0094 ++#define RK3368_WIN1_SCL_FACTOR_CBR 0x0098 ++#define RK3368_WIN1_SCL_OFFSET 0x009c ++#define RK3368_WIN1_SRC_ALPHA_CTRL 0x00a0 ++#define RK3368_WIN1_DST_ALPHA_CTRL 0x00a4 ++#define RK3368_WIN1_FADING_CTRL 0x00a8 ++#define RK3368_WIN1_CTRL2 0x00ac ++#define RK3368_WIN2_CTRL0 0x00b0 ++#define RK3368_WIN2_CTRL1 0x00b4 ++#define RK3368_WIN2_VIR0_1 0x00b8 ++#define RK3368_WIN2_VIR2_3 0x00bc ++#define RK3368_WIN2_MST0 0x00c0 ++#define RK3368_WIN2_DSP_INFO0 0x00c4 ++#define RK3368_WIN2_DSP_ST0 0x00c8 ++#define RK3368_WIN2_COLOR_KEY 0x00cc ++#define RK3368_WIN2_MST1 0x00d0 ++#define RK3368_WIN2_DSP_INFO1 0x00d4 ++#define RK3368_WIN2_DSP_ST1 0x00d8 ++#define RK3368_WIN2_SRC_ALPHA_CTRL 0x00dc ++#define RK3368_WIN2_MST2 0x00e0 ++#define RK3368_WIN2_DSP_INFO2 0x00e4 ++#define RK3368_WIN2_DSP_ST2 0x00e8 ++#define RK3368_WIN2_DST_ALPHA_CTRL 0x00ec ++#define RK3368_WIN2_MST3 0x00f0 ++#define RK3368_WIN2_DSP_INFO3 0x00f4 ++#define RK3368_WIN2_DSP_ST3 0x00f8 ++#define RK3368_WIN2_FADING_CTRL 0x00fc ++#define RK3368_WIN3_CTRL0 0x0100 ++#define RK3368_WIN3_CTRL1 0x0104 ++#define RK3368_WIN3_VIR0_1 0x0108 ++#define RK3368_WIN3_VIR2_3 0x010c ++#define RK3368_WIN3_MST0 0x0110 ++#define RK3368_WIN3_DSP_INFO0 0x0114 ++#define RK3368_WIN3_DSP_ST0 0x0118 ++#define RK3368_WIN3_COLOR_KEY 0x011c ++#define RK3368_WIN3_MST1 0x0120 ++#define RK3368_WIN3_DSP_INFO1 0x0124 ++#define RK3368_WIN3_DSP_ST1 0x0128 ++#define RK3368_WIN3_SRC_ALPHA_CTRL 0x012c ++#define RK3368_WIN3_MST2 0x0130 ++#define RK3368_WIN3_DSP_INFO2 0x0134 ++#define RK3368_WIN3_DSP_ST2 0x0138 ++#define RK3368_WIN3_DST_ALPHA_CTRL 0x013c ++#define RK3368_WIN3_MST3 0x0140 ++#define RK3368_WIN3_DSP_INFO3 0x0144 ++#define RK3368_WIN3_DSP_ST3 0x0148 ++#define RK3368_WIN3_FADING_CTRL 0x014c ++#define RK3368_HWC_CTRL0 0x0150 ++#define RK3368_HWC_CTRL1 0x0154 ++#define RK3368_HWC_MST 0x0158 ++#define RK3368_HWC_DSP_ST 0x015c ++#define RK3368_HWC_SRC_ALPHA_CTRL 0x0160 ++#define RK3368_HWC_DST_ALPHA_CTRL 0x0164 ++#define RK3368_HWC_FADING_CTRL 0x0168 ++#define RK3368_HWC_RESERVED1 0x016c ++#define RK3368_POST_DSP_HACT_INFO 0x0170 ++#define RK3368_POST_DSP_VACT_INFO 0x0174 ++#define RK3368_POST_SCL_FACTOR_YRGB 0x0178 ++#define RK3368_POST_RESERVED 0x017c ++#define RK3368_POST_SCL_CTRL 0x0180 ++#define RK3368_POST_DSP_VACT_INFO_F1 0x0184 ++#define RK3368_DSP_HTOTAL_HS_END 0x0188 ++#define RK3368_DSP_HACT_ST_END 0x018c ++#define RK3368_DSP_VTOTAL_VS_END 0x0190 ++#define RK3368_DSP_VACT_ST_END 0x0194 ++#define RK3368_DSP_VS_ST_END_F1 0x0198 ++#define RK3368_DSP_VACT_ST_END_F1 0x019c ++#define RK3368_PWM_CTRL 0x01a0 ++#define RK3368_PWM_PERIOD_HPR 0x01a4 ++#define RK3368_PWM_DUTY_LPR 0x01a8 ++#define RK3368_PWM_CNT 0x01ac ++#define RK3368_BCSH_COLOR_BAR 0x01b0 ++#define RK3368_BCSH_BCS 0x01b4 ++#define RK3368_BCSH_H 0x01b8 ++#define RK3368_BCSH_CTRL 0x01bc ++#define RK3368_CABC_CTRL0 0x01c0 ++#define RK3368_CABC_CTRL1 0x01c4 ++#define RK3368_CABC_CTRL2 0x01c8 ++#define RK3368_CABC_CTRL3 0x01cc ++#define RK3368_CABC_GAUSS_LINE0_0 0x01d0 ++#define RK3368_CABC_GAUSS_LINE0_1 0x01d4 ++#define RK3368_CABC_GAUSS_LINE1_0 0x01d8 ++#define RK3368_CABC_GAUSS_LINE1_1 0x01dc ++#define RK3368_CABC_GAUSS_LINE2_0 0x01e0 ++#define RK3368_CABC_GAUSS_LINE2_1 0x01e4 ++#define RK3368_FRC_LOWER01_0 0x01e8 ++#define RK3368_FRC_LOWER01_1 0x01ec ++#define RK3368_FRC_LOWER10_0 0x01f0 ++#define RK3368_FRC_LOWER10_1 0x01f4 ++#define RK3368_FRC_LOWER11_0 0x01f8 ++#define RK3368_FRC_LOWER11_1 0x01fc ++#define RK3368_IFBDC_CTRL 0x0200 ++#define RK3368_IFBDC_TILES_NUM 0x0204 ++#define RK3368_IFBDC_FRAME_RST_CYCLE 0x0208 ++#define RK3368_IFBDC_BASE_ADDR 0x020c ++#define RK3368_IFBDC_MB_SIZE 0x0210 ++#define RK3368_IFBDC_CMP_INDEX_INIT 0x0214 ++#define RK3368_IFBDC_VIR 0x0220 ++#define RK3368_IFBDC_DEBUG0 0x0230 ++#define RK3368_IFBDC_DEBUG1 0x0234 ++#define RK3368_LATENCY_CTRL0 0x0250 ++#define RK3368_RD_MAX_LATENCY_NUM0 0x0254 ++#define RK3368_RD_LATENCY_THR_NUM0 0x0258 ++#define RK3368_RD_LATENCY_SAMP_NUM0 0x025c ++#define RK3368_WIN0_DSP_BG 0x0260 ++#define RK3368_WIN1_DSP_BG 0x0264 ++#define RK3368_WIN2_DSP_BG 0x0268 ++#define RK3368_WIN3_DSP_BG 0x026c ++#define RK3368_SCAN_LINE_NUM 0x0270 ++#define RK3368_CABC_DEBUG0 0x0274 ++#define RK3368_CABC_DEBUG1 0x0278 ++#define RK3368_CABC_DEBUG2 0x027c ++#define RK3368_DBG_REG_000 0x0280 ++#define RK3368_DBG_REG_001 0x0284 ++#define RK3368_DBG_REG_002 0x0288 ++#define RK3368_DBG_REG_003 0x028c ++#define RK3368_DBG_REG_004 0x0290 ++#define RK3368_DBG_REG_005 0x0294 ++#define RK3368_DBG_REG_006 0x0298 ++#define RK3368_DBG_REG_007 0x029c ++#define RK3368_DBG_REG_008 0x02a0 ++#define RK3368_DBG_REG_016 0x02c0 ++#define RK3368_DBG_REG_017 0x02c4 ++#define RK3368_DBG_REG_018 0x02c8 ++#define RK3368_DBG_REG_019 0x02cc ++#define RK3368_DBG_REG_020 0x02d0 ++#define RK3368_DBG_REG_021 0x02d4 ++#define RK3368_DBG_REG_022 0x02d8 ++#define RK3368_DBG_REG_023 0x02dc ++#define RK3368_DBG_REG_028 0x02f0 ++#define RK3368_MMU_DTE_ADDR 0x0300 ++#define RK3368_MMU_STATUS 0x0304 ++#define RK3368_MMU_COMMAND 0x0308 ++#define RK3368_MMU_PAGE_FAULT_ADDR 0x030c ++#define RK3368_MMU_ZAP_ONE_LINE 0x0310 ++#define RK3368_MMU_INT_RAWSTAT 0x0314 ++#define RK3368_MMU_INT_CLEAR 0x0318 ++#define RK3368_MMU_INT_MASK 0x031c ++#define RK3368_MMU_INT_STATUS 0x0320 ++#define RK3368_MMU_AUTO_GATING 0x0324 ++#define RK3368_WIN2_LUT_ADDR 0x0400 ++#define RK3368_WIN3_LUT_ADDR 0x0800 ++#define RK3368_HWC_LUT_ADDR 0x0c00 ++#define RK3368_GAMMA_LUT_ADDR 0x1000 ++#define RK3368_CABC_GAMMA_LUT_ADDR 0x1800 ++#define RK3368_MCU_BYPASS_WPORT 0x2200 ++#define RK3368_MCU_BYPASS_RPORT 0x2300 ++/* rk3368 register definition end */ ++ ++#define RK3366_REG_CFG_DONE 0x0000 ++#define RK3366_VERSION_INFO 0x0004 ++#define RK3366_SYS_CTRL 0x0008 ++#define RK3366_SYS_CTRL1 0x000c ++#define RK3366_DSP_CTRL0 0x0010 ++#define RK3366_DSP_CTRL1 0x0014 ++#define RK3366_DSP_BG 0x0018 ++#define RK3366_MCU_CTRL 0x001c ++#define RK3366_WB_CTRL0 0x0020 ++#define RK3366_WB_CTRL1 0x0024 ++#define RK3366_WB_YRGB_MST 0x0028 ++#define RK3366_WB_CBR_MST 0x002c ++#define RK3366_WIN0_CTRL0 0x0030 ++#define RK3366_WIN0_CTRL1 0x0034 ++#define RK3366_WIN0_COLOR_KEY 0x0038 ++#define RK3366_WIN0_VIR 0x003c ++#define RK3366_WIN0_YRGB_MST 0x0040 ++#define RK3366_WIN0_CBR_MST 0x0044 ++#define RK3366_WIN0_ACT_INFO 0x0048 ++#define RK3366_WIN0_DSP_INFO 0x004c ++#define RK3366_WIN0_DSP_ST 0x0050 ++#define RK3366_WIN0_SCL_FACTOR_YRGB 0x0054 ++#define RK3366_WIN0_SCL_FACTOR_CBR 0x0058 ++#define RK3366_WIN0_SCL_OFFSET 0x005c ++#define RK3366_WIN0_SRC_ALPHA_CTRL 0x0060 ++#define RK3366_WIN0_DST_ALPHA_CTRL 0x0064 ++#define RK3366_WIN0_FADING_CTRL 0x0068 ++#define RK3366_WIN0_CTRL2 0x006c ++#define RK3366_WIN1_CTRL0 0x0070 ++#define RK3366_WIN1_CTRL1 0x0074 ++#define RK3366_WIN1_COLOR_KEY 0x0078 ++#define RK3366_WIN1_VIR 0x007c ++#define RK3366_WIN1_YRGB_MST 0x0080 ++#define RK3366_WIN1_CBR_MST 0x0084 ++#define RK3366_WIN1_ACT_INFO 0x0088 ++#define RK3366_WIN1_DSP_INFO 0x008c ++#define RK3366_WIN1_DSP_ST 0x0090 ++#define RK3366_WIN1_SCL_FACTOR_YRGB 0x0094 ++#define RK3366_WIN1_SCL_FACTOR_CBR 0x0098 ++#define RK3366_WIN1_SCL_OFFSET 0x009c ++#define RK3366_WIN1_SRC_ALPHA_CTRL 0x00a0 ++#define RK3366_WIN1_DST_ALPHA_CTRL 0x00a4 ++#define RK3366_WIN1_FADING_CTRL 0x00a8 ++#define RK3366_WIN1_CTRL2 0x00ac ++#define RK3366_WIN2_CTRL0 0x00b0 ++#define RK3366_WIN2_CTRL1 0x00b4 ++#define RK3366_WIN2_VIR0_1 0x00b8 ++#define RK3366_WIN2_VIR2_3 0x00bc ++#define RK3366_WIN2_MST0 0x00c0 ++#define RK3366_WIN2_DSP_INFO0 0x00c4 ++#define RK3366_WIN2_DSP_ST0 0x00c8 ++#define RK3366_WIN2_COLOR_KEY 0x00cc ++#define RK3366_WIN2_MST1 0x00d0 ++#define RK3366_WIN2_DSP_INFO1 0x00d4 ++#define RK3366_WIN2_DSP_ST1 0x00d8 ++#define RK3366_WIN2_SRC_ALPHA_CTRL 0x00dc ++#define RK3366_WIN2_MST2 0x00e0 ++#define RK3366_WIN2_DSP_INFO2 0x00e4 ++#define RK3366_WIN2_DSP_ST2 0x00e8 ++#define RK3366_WIN2_DST_ALPHA_CTRL 0x00ec ++#define RK3366_WIN2_MST3 0x00f0 ++#define RK3366_WIN2_DSP_INFO3 0x00f4 ++#define RK3366_WIN2_DSP_ST3 0x00f8 ++#define RK3366_WIN2_FADING_CTRL 0x00fc ++#define RK3366_WIN3_CTRL0 0x0100 ++#define RK3366_WIN3_CTRL1 0x0104 ++#define RK3366_WIN3_VIR0_1 0x0108 ++#define RK3366_WIN3_VIR2_3 0x010c ++#define RK3366_WIN3_MST0 0x0110 ++#define RK3366_WIN3_DSP_INFO0 0x0114 ++#define RK3366_WIN3_DSP_ST0 0x0118 ++#define RK3366_WIN3_COLOR_KEY 0x011c ++#define RK3366_WIN3_MST1 0x0120 ++#define RK3366_WIN3_DSP_INFO1 0x0124 ++#define RK3366_WIN3_DSP_ST1 0x0128 ++#define RK3366_WIN3_SRC_ALPHA_CTRL 0x012c ++#define RK3366_WIN3_MST2 0x0130 ++#define RK3366_WIN3_DSP_INFO2 0x0134 ++#define RK3366_WIN3_DSP_ST2 0x0138 ++#define RK3366_WIN3_DST_ALPHA_CTRL 0x013c ++#define RK3366_WIN3_MST3 0x0140 ++#define RK3366_WIN3_DSP_INFO3 0x0144 ++#define RK3366_WIN3_DSP_ST3 0x0148 ++#define RK3366_WIN3_FADING_CTRL 0x014c ++#define RK3366_HWC_CTRL0 0x0150 ++#define RK3366_HWC_CTRL1 0x0154 ++#define RK3366_HWC_MST 0x0158 ++#define RK3366_HWC_DSP_ST 0x015c ++#define RK3366_HWC_SRC_ALPHA_CTRL 0x0160 ++#define RK3366_HWC_DST_ALPHA_CTRL 0x0164 ++#define RK3366_HWC_FADING_CTRL 0x0168 ++#define RK3366_HWC_RESERVED1 0x016c ++#define RK3366_POST_DSP_HACT_INFO 0x0170 ++#define RK3366_POST_DSP_VACT_INFO 0x0174 ++#define RK3366_POST_SCL_FACTOR_YRGB 0x0178 ++#define RK3366_POST_RESERVED 0x017c ++#define RK3366_POST_SCL_CTRL 0x0180 ++#define RK3366_POST_DSP_VACT_INFO_F1 0x0184 ++#define RK3366_DSP_HTOTAL_HS_END 0x0188 ++#define RK3366_DSP_HACT_ST_END 0x018c ++#define RK3366_DSP_VTOTAL_VS_END 0x0190 ++#define RK3366_DSP_VACT_ST_END 0x0194 ++#define RK3366_DSP_VS_ST_END_F1 0x0198 ++#define RK3366_DSP_VACT_ST_END_F1 0x019c ++#define RK3366_PWM_CTRL 0x01a0 ++#define RK3366_PWM_PERIOD_HPR 0x01a4 ++#define RK3366_PWM_DUTY_LPR 0x01a8 ++#define RK3366_PWM_CNT 0x01ac ++#define RK3366_BCSH_COLOR_BAR 0x01b0 ++#define RK3366_BCSH_BCS 0x01b4 ++#define RK3366_BCSH_H 0x01b8 ++#define RK3366_BCSH_CTRL 0x01bc ++#define RK3366_CABC_CTRL0 0x01c0 ++#define RK3366_CABC_CTRL1 0x01c4 ++#define RK3366_CABC_CTRL2 0x01c8 ++#define RK3366_CABC_CTRL3 0x01cc ++#define RK3366_CABC_GAUSS_LINE0_0 0x01d0 ++#define RK3366_CABC_GAUSS_LINE0_1 0x01d4 ++#define RK3366_CABC_GAUSS_LINE1_0 0x01d8 ++#define RK3366_CABC_GAUSS_LINE1_1 0x01dc ++#define RK3366_CABC_GAUSS_LINE2_0 0x01e0 ++#define RK3366_CABC_GAUSS_LINE2_1 0x01e4 ++#define RK3366_FRC_LOWER01_0 0x01e8 ++#define RK3366_FRC_LOWER01_1 0x01ec ++#define RK3366_FRC_LOWER10_0 0x01f0 ++#define RK3366_FRC_LOWER10_1 0x01f4 ++#define RK3366_FRC_LOWER11_0 0x01f8 ++#define RK3366_FRC_LOWER11_1 0x01fc ++#define RK3366_INTR_EN0 0x0280 ++#define RK3366_INTR_CLEAR0 0x0284 ++#define RK3366_INTR_STATUS0 0x0288 ++#define RK3366_INTR_RAW_STATUS0 0x028c ++#define RK3366_INTR_EN1 0x0290 ++#define RK3366_INTR_CLEAR1 0x0294 ++#define RK3366_INTR_STATUS1 0x0298 ++#define RK3366_INTR_RAW_STATUS1 0x029c ++#define RK3366_LINE_FLAG 0x02a0 ++#define RK3366_VOP_STATUS 0x02a4 ++#define RK3366_BLANKING_VALUE 0x02a8 ++#define RK3366_WIN0_DSP_BG 0x02b0 ++#define RK3366_WIN1_DSP_BG 0x02b4 ++#define RK3366_WIN2_DSP_BG 0x02b8 ++#define RK3366_WIN3_DSP_BG 0x02bc ++#define RK3366_WIN2_LUT_ADDR 0x0400 ++#define RK3366_WIN3_LUT_ADDR 0x0800 ++#define RK3366_HWC_LUT_ADDR 0x0c00 ++#define RK3366_GAMMA0_LUT_ADDR 0x1000 ++#define RK3366_GAMMA1_LUT_ADDR 0x1400 ++#define RK3366_CABC_GAMMA_LUT_ADDR 0x1800 ++#define RK3366_MCU_BYPASS_WPORT 0x2200 ++#define RK3366_MCU_BYPASS_RPORT 0x2300 ++#define RK3366_MMU_DTE_ADDR 0x2400 ++#define RK3366_MMU_STATUS 0x2404 ++#define RK3366_MMU_COMMAND 0x2408 ++#define RK3366_MMU_PAGE_FAULT_ADDR 0x240c ++#define RK3366_MMU_ZAP_ONE_LINE 0x2410 ++#define RK3366_MMU_INT_RAWSTAT 0x2414 ++#define RK3366_MMU_INT_CLEAR 0x2418 ++#define RK3366_MMU_INT_MASK 0x241c ++#define RK3366_MMU_INT_STATUS 0x2420 ++#define RK3366_MMU_AUTO_GATING 0x2424 ++ ++/* rk3399 register definition */ ++#define RK3399_REG_CFG_DONE 0x0000 ++#define RK3399_VERSION_INFO 0x0004 ++#define RK3399_SYS_CTRL 0x0008 ++#define RK3399_SYS_CTRL1 0x000c ++#define RK3399_DSP_CTRL0 0x0010 ++#define RK3399_DSP_CTRL1 0x0014 ++#define RK3399_DSP_BG 0x0018 ++#define RK3399_MCU_CTRL 0x001c ++#define RK3399_WB_CTRL0 0x0020 ++#define RK3399_WB_CTRL1 0x0024 ++#define RK3399_WB_YRGB_MST 0x0028 ++#define RK3399_WB_CBR_MST 0x002c ++#define RK3399_WIN0_CTRL0 0x0030 ++#define RK3399_WIN0_CTRL1 0x0034 ++#define RK3399_WIN0_COLOR_KEY 0x0038 ++#define RK3399_WIN0_VIR 0x003c ++#define RK3399_WIN0_YRGB_MST 0x0040 ++#define RK3399_WIN0_CBR_MST 0x0044 ++#define RK3399_WIN0_ACT_INFO 0x0048 ++#define RK3399_WIN0_DSP_INFO 0x004c ++#define RK3399_WIN0_DSP_ST 0x0050 ++#define RK3399_WIN0_SCL_FACTOR_YRGB 0x0054 ++#define RK3399_WIN0_SCL_FACTOR_CBR 0x0058 ++#define RK3399_WIN0_SCL_OFFSET 0x005c ++#define RK3399_WIN0_SRC_ALPHA_CTRL 0x0060 ++#define RK3399_WIN0_DST_ALPHA_CTRL 0x0064 ++#define RK3399_WIN0_FADING_CTRL 0x0068 ++#define RK3399_WIN0_CTRL2 0x006c ++#define RK3399_WIN1_CTRL0 0x0070 ++#define RK3399_WIN1_CTRL1 0x0074 ++#define RK3399_WIN1_COLOR_KEY 0x0078 ++#define RK3399_WIN1_VIR 0x007c ++#define RK3399_WIN1_YRGB_MST 0x0080 ++#define RK3399_WIN1_CBR_MST 0x0084 ++#define RK3399_WIN1_ACT_INFO 0x0088 ++#define RK3399_WIN1_DSP_INFO 0x008c ++#define RK3399_WIN1_DSP_ST 0x0090 ++#define RK3399_WIN1_SCL_FACTOR_YRGB 0x0094 ++#define RK3399_WIN1_SCL_FACTOR_CBR 0x0098 ++#define RK3399_WIN1_SCL_OFFSET 0x009c ++#define RK3399_WIN1_SRC_ALPHA_CTRL 0x00a0 ++#define RK3399_WIN1_DST_ALPHA_CTRL 0x00a4 ++#define RK3399_WIN1_FADING_CTRL 0x00a8 ++#define RK3399_WIN1_CTRL2 0x00ac ++#define RK3399_WIN2_CTRL0 0x00b0 ++#define RK3399_WIN2_CTRL1 0x00b4 ++#define RK3399_WIN2_VIR0_1 0x00b8 ++#define RK3399_WIN2_VIR2_3 0x00bc ++#define RK3399_WIN2_MST0 0x00c0 ++#define RK3399_WIN2_DSP_INFO0 0x00c4 ++#define RK3399_WIN2_DSP_ST0 0x00c8 ++#define RK3399_WIN2_COLOR_KEY 0x00cc ++#define RK3399_WIN2_MST1 0x00d0 ++#define RK3399_WIN2_DSP_INFO1 0x00d4 ++#define RK3399_WIN2_DSP_ST1 0x00d8 ++#define RK3399_WIN2_SRC_ALPHA_CTRL 0x00dc ++#define RK3399_WIN2_MST2 0x00e0 ++#define RK3399_WIN2_DSP_INFO2 0x00e4 ++#define RK3399_WIN2_DSP_ST2 0x00e8 ++#define RK3399_WIN2_DST_ALPHA_CTRL 0x00ec ++#define RK3399_WIN2_MST3 0x00f0 ++#define RK3399_WIN2_DSP_INFO3 0x00f4 ++#define RK3399_WIN2_DSP_ST3 0x00f8 ++#define RK3399_WIN2_FADING_CTRL 0x00fc ++#define RK3399_WIN3_CTRL0 0x0100 ++#define RK3399_WIN3_CTRL1 0x0104 ++#define RK3399_WIN3_VIR0_1 0x0108 ++#define RK3399_WIN3_VIR2_3 0x010c ++#define RK3399_WIN3_MST0 0x0110 ++#define RK3399_WIN3_DSP_INFO0 0x0114 ++#define RK3399_WIN3_DSP_ST0 0x0118 ++#define RK3399_WIN3_COLOR_KEY 0x011c ++#define RK3399_WIN3_MST1 0x0120 ++#define RK3399_WIN3_DSP_INFO1 0x0124 ++#define RK3399_WIN3_DSP_ST1 0x0128 ++#define RK3399_WIN3_SRC_ALPHA_CTRL 0x012c ++#define RK3399_WIN3_MST2 0x0130 ++#define RK3399_WIN3_DSP_INFO2 0x0134 ++#define RK3399_WIN3_DSP_ST2 0x0138 ++#define RK3399_WIN3_DST_ALPHA_CTRL 0x013c ++#define RK3399_WIN3_MST3 0x0140 ++#define RK3399_WIN3_DSP_INFO3 0x0144 ++#define RK3399_WIN3_DSP_ST3 0x0148 ++#define RK3399_WIN3_FADING_CTRL 0x014c ++#define RK3399_HWC_CTRL0 0x0150 ++#define RK3399_HWC_CTRL1 0x0154 ++#define RK3399_HWC_MST 0x0158 ++#define RK3399_HWC_DSP_ST 0x015c ++#define RK3399_HWC_SRC_ALPHA_CTRL 0x0160 ++#define RK3399_HWC_DST_ALPHA_CTRL 0x0164 ++#define RK3399_HWC_FADING_CTRL 0x0168 ++#define RK3399_HWC_RESERVED1 0x016c ++#define RK3399_POST_DSP_HACT_INFO 0x0170 ++#define RK3399_POST_DSP_VACT_INFO 0x0174 ++#define RK3399_POST_SCL_FACTOR_YRGB 0x0178 ++#define RK3399_POST_RESERVED 0x017c ++#define RK3399_POST_SCL_CTRL 0x0180 ++#define RK3399_POST_DSP_VACT_INFO_F1 0x0184 ++#define RK3399_DSP_HTOTAL_HS_END 0x0188 ++#define RK3399_DSP_HACT_ST_END 0x018c ++#define RK3399_DSP_VTOTAL_VS_END 0x0190 ++#define RK3399_DSP_VACT_ST_END 0x0194 ++#define RK3399_DSP_VS_ST_END_F1 0x0198 ++#define RK3399_DSP_VACT_ST_END_F1 0x019c ++#define RK3399_PWM_CTRL 0x01a0 ++#define RK3399_PWM_PERIOD_HPR 0x01a4 ++#define RK3399_PWM_DUTY_LPR 0x01a8 ++#define RK3399_PWM_CNT 0x01ac ++#define RK3399_BCSH_COLOR_BAR 0x01b0 ++#define RK3399_BCSH_BCS 0x01b4 ++#define RK3399_BCSH_H 0x01b8 ++#define RK3399_BCSH_CTRL 0x01bc ++#define RK3399_CABC_CTRL0 0x01c0 ++#define RK3399_CABC_CTRL1 0x01c4 ++#define RK3399_CABC_CTRL2 0x01c8 ++#define RK3399_CABC_CTRL3 0x01cc ++#define RK3399_CABC_GAUSS_LINE0_0 0x01d0 ++#define RK3399_CABC_GAUSS_LINE0_1 0x01d4 ++#define RK3399_CABC_GAUSS_LINE1_0 0x01d8 ++#define RK3399_CABC_GAUSS_LINE1_1 0x01dc ++#define RK3399_CABC_GAUSS_LINE2_0 0x01e0 ++#define RK3399_CABC_GAUSS_LINE2_1 0x01e4 ++#define RK3399_FRC_LOWER01_0 0x01e8 ++#define RK3399_FRC_LOWER01_1 0x01ec ++#define RK3399_FRC_LOWER10_0 0x01f0 ++#define RK3399_FRC_LOWER10_1 0x01f4 ++#define RK3399_FRC_LOWER11_0 0x01f8 ++#define RK3399_FRC_LOWER11_1 0x01fc ++#define RK3399_AFBCD0_CTRL 0x0200 ++#define RK3399_AFBCD0_HDR_PTR 0x0204 ++#define RK3399_AFBCD0_PIC_SIZE 0x0208 ++#define RK3399_AFBCD0_STATUS 0x020c ++#define RK3399_AFBCD1_CTRL 0x0220 ++#define RK3399_AFBCD1_HDR_PTR 0x0224 ++#define RK3399_AFBCD1_PIC_SIZE 0x0228 ++#define RK3399_AFBCD1_STATUS 0x022c ++#define RK3399_AFBCD2_CTRL 0x0240 ++#define RK3399_AFBCD2_HDR_PTR 0x0244 ++#define RK3399_AFBCD2_PIC_SIZE 0x0248 ++#define RK3399_AFBCD2_STATUS 0x024c ++#define RK3399_AFBCD3_CTRL 0x0260 ++#define RK3399_AFBCD3_HDR_PTR 0x0264 ++#define RK3399_AFBCD3_PIC_SIZE 0x0268 ++#define RK3399_AFBCD3_STATUS 0x026c ++#define RK3399_INTR_EN0 0x0280 ++#define RK3399_INTR_CLEAR0 0x0284 ++#define RK3399_INTR_STATUS0 0x0288 ++#define RK3399_INTR_RAW_STATUS0 0x028c ++#define RK3399_INTR_EN1 0x0290 ++#define RK3399_INTR_CLEAR1 0x0294 ++#define RK3399_INTR_STATUS1 0x0298 ++#define RK3399_INTR_RAW_STATUS1 0x029c ++#define RK3399_LINE_FLAG 0x02a0 ++#define RK3399_VOP_STATUS 0x02a4 ++#define RK3399_BLANKING_VALUE 0x02a8 ++#define RK3399_MCU_BYPASS_PORT 0x02ac ++#define RK3399_WIN0_DSP_BG 0x02b0 ++#define RK3399_WIN1_DSP_BG 0x02b4 ++#define RK3399_WIN2_DSP_BG 0x02b8 ++#define RK3399_WIN3_DSP_BG 0x02bc ++#define RK3399_YUV2YUV_WIN 0x02c0 ++#define RK3399_YUV2YUV_POST 0x02c4 ++#define RK3399_AUTO_GATING_EN 0x02cc ++#define RK3399_WIN0_CSC_COE 0x03a0 ++#define RK3399_WIN1_CSC_COE 0x03c0 ++#define RK3399_WIN2_CSC_COE 0x03e0 ++#define RK3399_WIN3_CSC_COE 0x0400 ++#define RK3399_HWC_CSC_COE 0x0420 ++#define RK3399_BCSH_R2Y_CSC_COE 0x0440 ++#define RK3399_BCSH_Y2R_CSC_COE 0x0460 ++#define RK3399_POST_YUV2YUV_Y2R_COE 0x0480 ++#define RK3399_POST_YUV2YUV_3X3_COE 0x04a0 ++#define RK3399_POST_YUV2YUV_R2Y_COE 0x04c0 ++#define RK3399_WIN0_YUV2YUV_Y2R 0x04e0 ++#define RK3399_WIN0_YUV2YUV_3X3 0x0500 ++#define RK3399_WIN0_YUV2YUV_R2Y 0x0520 ++#define RK3399_WIN1_YUV2YUV_Y2R 0x0540 ++#define RK3399_WIN1_YUV2YUV_3X3 0x0560 ++#define RK3399_WIN1_YUV2YUV_R2Y 0x0580 ++#define RK3399_WIN2_YUV2YUV_Y2R 0x05a0 ++#define RK3399_WIN2_YUV2YUV_3X3 0x05c0 ++#define RK3399_WIN2_YUV2YUV_R2Y 0x05e0 ++#define RK3399_WIN3_YUV2YUV_Y2R 0x0600 ++#define RK3399_WIN3_YUV2YUV_3X3 0x0620 ++#define RK3399_WIN3_YUV2YUV_R2Y 0x0640 ++#define RK3399_WIN2_LUT_ADDR 0x1000 ++#define RK3399_WIN3_LUT_ADDR 0x1400 ++#define RK3399_HWC_LUT_ADDR 0x1800 ++#define RK3399_CABC_GAMMA_LUT_ADDR 0x1c00 ++#define RK3399_GAMMA_LUT_ADDR 0x2000 ++/* rk3399 register definition end */ ++ ++/* rk3328 register definition end */ ++#define RK3328_REG_CFG_DONE 0x00000000 ++#define RK3328_VERSION_INFO 0x00000004 ++#define RK3328_SYS_CTRL 0x00000008 ++#define RK3328_SYS_CTRL1 0x0000000c ++#define RK3328_DSP_CTRL0 0x00000010 ++#define RK3328_DSP_CTRL1 0x00000014 ++#define RK3328_DSP_BG 0x00000018 ++#define RK3328_AUTO_GATING_EN 0x0000003c ++#define RK3328_LINE_FLAG 0x00000040 ++#define RK3328_VOP_STATUS 0x00000044 ++#define RK3328_BLANKING_VALUE 0x00000048 ++#define RK3328_WIN0_DSP_BG 0x00000050 ++#define RK3328_WIN1_DSP_BG 0x00000054 ++#define RK3328_DBG_PERF_LATENCY_CTRL0 0x000000c0 ++#define RK3328_DBG_PERF_RD_MAX_LATENCY_NUM0 0x000000c4 ++#define RK3328_DBG_PERF_RD_LATENCY_THR_NUM0 0x000000c8 ++#define RK3328_DBG_PERF_RD_LATENCY_SAMP_NUM0 0x000000cc ++#define RK3328_INTR_EN0 0x000000e0 ++#define RK3328_INTR_CLEAR0 0x000000e4 ++#define RK3328_INTR_STATUS0 0x000000e8 ++#define RK3328_INTR_RAW_STATUS0 0x000000ec ++#define RK3328_INTR_EN1 0x000000f0 ++#define RK3328_INTR_CLEAR1 0x000000f4 ++#define RK3328_INTR_STATUS1 0x000000f8 ++#define RK3328_INTR_RAW_STATUS1 0x000000fc ++#define RK3328_WIN0_CTRL0 0x00000100 ++#define RK3328_WIN0_CTRL1 0x00000104 ++#define RK3328_WIN0_COLOR_KEY 0x00000108 ++#define RK3328_WIN0_VIR 0x0000010c ++#define RK3328_WIN0_YRGB_MST 0x00000110 ++#define RK3328_WIN0_CBR_MST 0x00000114 ++#define RK3328_WIN0_ACT_INFO 0x00000118 ++#define RK3328_WIN0_DSP_INFO 0x0000011c ++#define RK3328_WIN0_DSP_ST 0x00000120 ++#define RK3328_WIN0_SCL_FACTOR_YRGB 0x00000124 ++#define RK3328_WIN0_SCL_FACTOR_CBR 0x00000128 ++#define RK3328_WIN0_SCL_OFFSET 0x0000012c ++#define RK3328_WIN0_SRC_ALPHA_CTRL 0x00000130 ++#define RK3328_WIN0_DST_ALPHA_CTRL 0x00000134 ++#define RK3328_WIN0_FADING_CTRL 0x00000138 ++#define RK3328_WIN0_CTRL2 0x0000013c ++#define RK3328_DBG_WIN0_REG0 0x000001f0 ++#define RK3328_DBG_WIN0_REG1 0x000001f4 ++#define RK3328_DBG_WIN0_REG2 0x000001f8 ++#define RK3328_DBG_WIN0_RESERVED 0x000001fc ++#define RK3328_WIN1_CTRL0 0x00000200 ++#define RK3328_WIN1_CTRL1 0x00000204 ++#define RK3328_WIN1_COLOR_KEY 0x00000208 ++#define RK3328_WIN1_VIR 0x0000020c ++#define RK3328_WIN1_YRGB_MST 0x00000210 ++#define RK3328_WIN1_CBR_MST 0x00000214 ++#define RK3328_WIN1_ACT_INFO 0x00000218 ++#define RK3328_WIN1_DSP_INFO 0x0000021c ++#define RK3328_WIN1_DSP_ST 0x00000220 ++#define RK3328_WIN1_SCL_FACTOR_YRGB 0x00000224 ++#define RK3328_WIN1_SCL_FACTOR_CBR 0x00000228 ++#define RK3328_WIN1_SCL_OFFSET 0x0000022c ++#define RK3328_WIN1_SRC_ALPHA_CTRL 0x00000230 ++#define RK3328_WIN1_DST_ALPHA_CTRL 0x00000234 ++#define RK3328_WIN1_FADING_CTRL 0x00000238 ++#define RK3328_WIN1_CTRL2 0x0000023c ++#define RK3328_DBG_WIN1_REG0 0x000002f0 ++#define RK3328_DBG_WIN1_REG1 0x000002f4 ++#define RK3328_DBG_WIN1_REG2 0x000002f8 ++#define RK3328_DBG_WIN1_RESERVED 0x000002fc ++#define RK3328_WIN2_CTRL0 0x00000300 ++#define RK3328_WIN2_CTRL1 0x00000304 ++#define RK3328_WIN2_COLOR_KEY 0x00000308 ++#define RK3328_WIN2_VIR 0x0000030c ++#define RK3328_WIN2_YRGB_MST 0x00000310 ++#define RK3328_WIN2_CBR_MST 0x00000314 ++#define RK3328_WIN2_ACT_INFO 0x00000318 ++#define RK3328_WIN2_DSP_INFO 0x0000031c ++#define RK3328_WIN2_DSP_ST 0x00000320 ++#define RK3328_WIN2_SCL_FACTOR_YRGB 0x00000324 ++#define RK3328_WIN2_SCL_FACTOR_CBR 0x00000328 ++#define RK3328_WIN2_SCL_OFFSET 0x0000032c ++#define RK3328_WIN2_SRC_ALPHA_CTRL 0x00000330 ++#define RK3328_WIN2_DST_ALPHA_CTRL 0x00000334 ++#define RK3328_WIN2_FADING_CTRL 0x00000338 ++#define RK3328_WIN2_CTRL2 0x0000033c ++#define RK3328_DBG_WIN2_REG0 0x000003f0 ++#define RK3328_DBG_WIN2_REG1 0x000003f4 ++#define RK3328_DBG_WIN2_REG2 0x000003f8 ++#define RK3328_DBG_WIN2_RESERVED 0x000003fc ++#define RK3328_WIN3_CTRL0 0x00000400 ++#define RK3328_WIN3_CTRL1 0x00000404 ++#define RK3328_WIN3_COLOR_KEY 0x00000408 ++#define RK3328_WIN3_VIR 0x0000040c ++#define RK3328_WIN3_YRGB_MST 0x00000410 ++#define RK3328_WIN3_CBR_MST 0x00000414 ++#define RK3328_WIN3_ACT_INFO 0x00000418 ++#define RK3328_WIN3_DSP_INFO 0x0000041c ++#define RK3328_WIN3_DSP_ST 0x00000420 ++#define RK3328_WIN3_SCL_FACTOR_YRGB 0x00000424 ++#define RK3328_WIN3_SCL_FACTOR_CBR 0x00000428 ++#define RK3328_WIN3_SCL_OFFSET 0x0000042c ++#define RK3328_WIN3_SRC_ALPHA_CTRL 0x00000430 ++#define RK3328_WIN3_DST_ALPHA_CTRL 0x00000434 ++#define RK3328_WIN3_FADING_CTRL 0x00000438 ++#define RK3328_WIN3_CTRL2 0x0000043c ++#define RK3328_DBG_WIN3_REG0 0x000004f0 ++#define RK3328_DBG_WIN3_REG1 0x000004f4 ++#define RK3328_DBG_WIN3_REG2 0x000004f8 ++#define RK3328_DBG_WIN3_RESERVED 0x000004fc ++ ++#define RK3328_HWC_CTRL0 0x00000500 ++#define RK3328_HWC_CTRL1 0x00000504 ++#define RK3328_HWC_MST 0x00000508 ++#define RK3328_HWC_DSP_ST 0x0000050c ++#define RK3328_HWC_SRC_ALPHA_CTRL 0x00000510 ++#define RK3328_HWC_DST_ALPHA_CTRL 0x00000514 ++#define RK3328_HWC_FADING_CTRL 0x00000518 ++#define RK3328_HWC_RESERVED1 0x0000051c ++#define RK3328_POST_DSP_HACT_INFO 0x00000600 ++#define RK3328_POST_DSP_VACT_INFO 0x00000604 ++#define RK3328_POST_SCL_FACTOR_YRGB 0x00000608 ++#define RK3328_POST_RESERVED 0x0000060c ++#define RK3328_POST_SCL_CTRL 0x00000610 ++#define RK3328_POST_DSP_VACT_INFO_F1 0x00000614 ++#define RK3328_DSP_HTOTAL_HS_END 0x00000618 ++#define RK3328_DSP_HACT_ST_END 0x0000061c ++#define RK3328_DSP_VTOTAL_VS_END 0x00000620 ++#define RK3328_DSP_VACT_ST_END 0x00000624 ++#define RK3328_DSP_VS_ST_END_F1 0x00000628 ++#define RK3328_DSP_VACT_ST_END_F1 0x0000062c ++#define RK3328_BCSH_COLOR_BAR 0x00000640 ++#define RK3328_BCSH_BCS 0x00000644 ++#define RK3328_BCSH_H 0x00000648 ++#define RK3328_BCSH_CTRL 0x0000064c ++#define RK3328_FRC_LOWER01_0 0x00000678 ++#define RK3328_FRC_LOWER01_1 0x0000067c ++#define RK3328_FRC_LOWER10_0 0x00000680 ++#define RK3328_FRC_LOWER10_1 0x00000684 ++#define RK3328_FRC_LOWER11_0 0x00000688 ++#define RK3328_FRC_LOWER11_1 0x0000068c ++#define RK3328_DBG_POST_REG0 0x000006e8 ++#define RK3328_DBG_POST_RESERVED 0x000006ec ++#define RK3328_DBG_DATAO 0x000006f0 ++#define RK3328_DBG_DATAO_2 0x000006f4 ++ ++/* sdr to hdr */ ++#define RK3328_SDR2HDR_CTRL 0x00000700 ++#define RK3328_EOTF_OETF_Y0 0x00000704 ++#define RK3328_RESERVED0001 0x00000708 ++#define RK3328_RESERVED0002 0x0000070c ++#define RK3328_EOTF_OETF_Y1 0x00000710 ++#define RK3328_EOTF_OETF_Y64 0x0000080c ++#define RK3328_OETF_DX_DXPOW1 0x00000810 ++#define RK3328_OETF_DX_DXPOW64 0x0000090c ++#define RK3328_OETF_XN1 0x00000910 ++#define RK3328_OETF_XN63 0x00000a08 ++ ++/* hdr to sdr */ ++#define RK3328_HDR2SDR_CTRL 0x00000a10 ++#define RK3328_HDR2SDR_SRC_RANGE 0x00000a14 ++#define RK3328_HDR2SDR_NORMFACEETF 0x00000a18 ++#define RK3328_RESERVED0003 0x00000a1c ++#define RK3328_HDR2SDR_DST_RANGE 0x00000a20 ++#define RK3328_HDR2SDR_NORMFACCGAMMA 0x00000a24 ++#define RK3328_EETF_OETF_Y0 0x00000a28 ++#define RK3328_SAT_Y0 0x00000a2c ++#define RK3328_EETF_OETF_Y1 0x00000a30 ++#define RK3328_SAT_Y1 0x00000ab0 ++#define RK3328_SAT_Y8 0x00000acc ++ ++#define RK3328_HWC_LUT_ADDR 0x00000c00 ++ + /* rk3036 register definition */ + #define RK3036_SYS_CTRL 0x00 + #define RK3036_DSP_CTRL0 0x04 +@@ -166,197 +878,4 @@ + #define RK3036_HWC_LUT_ADDR 0x800 + /* rk3036 register definition end */ + +-/* rk3399 register definition */ +-#define RK3399_REG_CFG_DONE 0x00000 +-#define RK3399_VERSION_INFO 0x00004 +-#define RK3399_SYS_CTRL 0x00008 +-#define RK3399_SYS_CTRL1 0x0000c +-#define RK3399_DSP_CTRL0 0x00010 +-#define RK3399_DSP_CTRL1 0x00014 +-#define RK3399_DSP_BG 0x00018 +-#define RK3399_MCU_CTRL 0x0001c +-#define RK3399_WB_CTRL0 0x00020 +-#define RK3399_WB_CTRL1 0x00024 +-#define RK3399_WB_YRGB_MST 0x00028 +-#define RK3399_WB_CBR_MST 0x0002c +-#define RK3399_WIN0_CTRL0 0x00030 +-#define RK3399_WIN0_CTRL1 0x00034 +-#define RK3399_WIN0_COLOR_KEY 0x00038 +-#define RK3399_WIN0_VIR 0x0003c +-#define RK3399_WIN0_YRGB_MST 0x00040 +-#define RK3399_WIN0_CBR_MST 0x00044 +-#define RK3399_WIN0_ACT_INFO 0x00048 +-#define RK3399_WIN0_DSP_INFO 0x0004c +-#define RK3399_WIN0_DSP_ST 0x00050 +-#define RK3399_WIN0_SCL_FACTOR_YRGB 0x00054 +-#define RK3399_WIN0_SCL_FACTOR_CBR 0x00058 +-#define RK3399_WIN0_SCL_OFFSET 0x0005c +-#define RK3399_WIN0_SRC_ALPHA_CTRL 0x00060 +-#define RK3399_WIN0_DST_ALPHA_CTRL 0x00064 +-#define RK3399_WIN0_FADING_CTRL 0x00068 +-#define RK3399_WIN0_CTRL2 0x0006c +-#define RK3399_WIN1_CTRL0 0x00070 +-#define RK3399_WIN1_CTRL1 0x00074 +-#define RK3399_WIN1_COLOR_KEY 0x00078 +-#define RK3399_WIN1_VIR 0x0007c +-#define RK3399_WIN1_YRGB_MST 0x00080 +-#define RK3399_WIN1_CBR_MST 0x00084 +-#define RK3399_WIN1_ACT_INFO 0x00088 +-#define RK3399_WIN1_DSP_INFO 0x0008c +-#define RK3399_WIN1_DSP_ST 0x00090 +-#define RK3399_WIN1_SCL_FACTOR_YRGB 0x00094 +-#define RK3399_WIN1_SCL_FACTOR_CBR 0x00098 +-#define RK3399_WIN1_SCL_OFFSET 0x0009c +-#define RK3399_WIN1_SRC_ALPHA_CTRL 0x000a0 +-#define RK3399_WIN1_DST_ALPHA_CTRL 0x000a4 +-#define RK3399_WIN1_FADING_CTRL 0x000a8 +-#define RK3399_WIN1_CTRL2 0x000ac +-#define RK3399_WIN2_CTRL0 0x000b0 +-#define RK3399_WIN2_CTRL1 0x000b4 +-#define RK3399_WIN2_VIR0_1 0x000b8 +-#define RK3399_WIN2_VIR2_3 0x000bc +-#define RK3399_WIN2_MST0 0x000c0 +-#define RK3399_WIN2_DSP_INFO0 0x000c4 +-#define RK3399_WIN2_DSP_ST0 0x000c8 +-#define RK3399_WIN2_COLOR_KEY 0x000cc +-#define RK3399_WIN2_MST1 0x000d0 +-#define RK3399_WIN2_DSP_INFO1 0x000d4 +-#define RK3399_WIN2_DSP_ST1 0x000d8 +-#define RK3399_WIN2_SRC_ALPHA_CTRL 0x000dc +-#define RK3399_WIN2_MST2 0x000e0 +-#define RK3399_WIN2_DSP_INFO2 0x000e4 +-#define RK3399_WIN2_DSP_ST2 0x000e8 +-#define RK3399_WIN2_DST_ALPHA_CTRL 0x000ec +-#define RK3399_WIN2_MST3 0x000f0 +-#define RK3399_WIN2_DSP_INFO3 0x000f4 +-#define RK3399_WIN2_DSP_ST3 0x000f8 +-#define RK3399_WIN2_FADING_CTRL 0x000fc +-#define RK3399_WIN3_CTRL0 0x00100 +-#define RK3399_WIN3_CTRL1 0x00104 +-#define RK3399_WIN3_VIR0_1 0x00108 +-#define RK3399_WIN3_VIR2_3 0x0010c +-#define RK3399_WIN3_MST0 0x00110 +-#define RK3399_WIN3_DSP_INFO0 0x00114 +-#define RK3399_WIN3_DSP_ST0 0x00118 +-#define RK3399_WIN3_COLOR_KEY 0x0011c +-#define RK3399_WIN3_MST1 0x00120 +-#define RK3399_WIN3_DSP_INFO1 0x00124 +-#define RK3399_WIN3_DSP_ST1 0x00128 +-#define RK3399_WIN3_SRC_ALPHA_CTRL 0x0012c +-#define RK3399_WIN3_MST2 0x00130 +-#define RK3399_WIN3_DSP_INFO2 0x00134 +-#define RK3399_WIN3_DSP_ST2 0x00138 +-#define RK3399_WIN3_DST_ALPHA_CTRL 0x0013c +-#define RK3399_WIN3_MST3 0x00140 +-#define RK3399_WIN3_DSP_INFO3 0x00144 +-#define RK3399_WIN3_DSP_ST3 0x00148 +-#define RK3399_WIN3_FADING_CTRL 0x0014c +-#define RK3399_HWC_CTRL0 0x00150 +-#define RK3399_HWC_CTRL1 0x00154 +-#define RK3399_HWC_MST 0x00158 +-#define RK3399_HWC_DSP_ST 0x0015c +-#define RK3399_HWC_SRC_ALPHA_CTRL 0x00160 +-#define RK3399_HWC_DST_ALPHA_CTRL 0x00164 +-#define RK3399_HWC_FADING_CTRL 0x00168 +-#define RK3399_HWC_RESERVED1 0x0016c +-#define RK3399_POST_DSP_HACT_INFO 0x00170 +-#define RK3399_POST_DSP_VACT_INFO 0x00174 +-#define RK3399_POST_SCL_FACTOR_YRGB 0x00178 +-#define RK3399_POST_RESERVED 0x0017c +-#define RK3399_POST_SCL_CTRL 0x00180 +-#define RK3399_POST_DSP_VACT_INFO_F1 0x00184 +-#define RK3399_DSP_HTOTAL_HS_END 0x00188 +-#define RK3399_DSP_HACT_ST_END 0x0018c +-#define RK3399_DSP_VTOTAL_VS_END 0x00190 +-#define RK3399_DSP_VACT_ST_END 0x00194 +-#define RK3399_DSP_VS_ST_END_F1 0x00198 +-#define RK3399_DSP_VACT_ST_END_F1 0x0019c +-#define RK3399_PWM_CTRL 0x001a0 +-#define RK3399_PWM_PERIOD_HPR 0x001a4 +-#define RK3399_PWM_DUTY_LPR 0x001a8 +-#define RK3399_PWM_CNT 0x001ac +-#define RK3399_BCSH_COLOR_BAR 0x001b0 +-#define RK3399_BCSH_BCS 0x001b4 +-#define RK3399_BCSH_H 0x001b8 +-#define RK3399_BCSH_CTRL 0x001bc +-#define RK3399_CABC_CTRL0 0x001c0 +-#define RK3399_CABC_CTRL1 0x001c4 +-#define RK3399_CABC_CTRL2 0x001c8 +-#define RK3399_CABC_CTRL3 0x001cc +-#define RK3399_CABC_GAUSS_LINE0_0 0x001d0 +-#define RK3399_CABC_GAUSS_LINE0_1 0x001d4 +-#define RK3399_CABC_GAUSS_LINE1_0 0x001d8 +-#define RK3399_CABC_GAUSS_LINE1_1 0x001dc +-#define RK3399_CABC_GAUSS_LINE2_0 0x001e0 +-#define RK3399_CABC_GAUSS_LINE2_1 0x001e4 +-#define RK3399_FRC_LOWER01_0 0x001e8 +-#define RK3399_FRC_LOWER01_1 0x001ec +-#define RK3399_FRC_LOWER10_0 0x001f0 +-#define RK3399_FRC_LOWER10_1 0x001f4 +-#define RK3399_FRC_LOWER11_0 0x001f8 +-#define RK3399_FRC_LOWER11_1 0x001fc +-#define RK3399_AFBCD0_CTRL 0x00200 +-#define RK3399_AFBCD0_HDR_PTR 0x00204 +-#define RK3399_AFBCD0_PIC_SIZE 0x00208 +-#define RK3399_AFBCD0_STATUS 0x0020c +-#define RK3399_AFBCD1_CTRL 0x00220 +-#define RK3399_AFBCD1_HDR_PTR 0x00224 +-#define RK3399_AFBCD1_PIC_SIZE 0x00228 +-#define RK3399_AFBCD1_STATUS 0x0022c +-#define RK3399_AFBCD2_CTRL 0x00240 +-#define RK3399_AFBCD2_HDR_PTR 0x00244 +-#define RK3399_AFBCD2_PIC_SIZE 0x00248 +-#define RK3399_AFBCD2_STATUS 0x0024c +-#define RK3399_AFBCD3_CTRL 0x00260 +-#define RK3399_AFBCD3_HDR_PTR 0x00264 +-#define RK3399_AFBCD3_PIC_SIZE 0x00268 +-#define RK3399_AFBCD3_STATUS 0x0026c +-#define RK3399_INTR_EN0 0x00280 +-#define RK3399_INTR_CLEAR0 0x00284 +-#define RK3399_INTR_STATUS0 0x00288 +-#define RK3399_INTR_RAW_STATUS0 0x0028c +-#define RK3399_INTR_EN1 0x00290 +-#define RK3399_INTR_CLEAR1 0x00294 +-#define RK3399_INTR_STATUS1 0x00298 +-#define RK3399_INTR_RAW_STATUS1 0x0029c +-#define RK3399_LINE_FLAG 0x002a0 +-#define RK3399_VOP_STATUS 0x002a4 +-#define RK3399_BLANKING_VALUE 0x002a8 +-#define RK3399_MCU_BYPASS_PORT 0x002ac +-#define RK3399_WIN0_DSP_BG 0x002b0 +-#define RK3399_WIN1_DSP_BG 0x002b4 +-#define RK3399_WIN2_DSP_BG 0x002b8 +-#define RK3399_WIN3_DSP_BG 0x002bc +-#define RK3399_YUV2YUV_WIN 0x002c0 +-#define RK3399_YUV2YUV_POST 0x002c4 +-#define RK3399_AUTO_GATING_EN 0x002cc +-#define RK3399_WIN0_CSC_COE 0x003a0 +-#define RK3399_WIN1_CSC_COE 0x003c0 +-#define RK3399_WIN2_CSC_COE 0x003e0 +-#define RK3399_WIN3_CSC_COE 0x00400 +-#define RK3399_HWC_CSC_COE 0x00420 +-#define RK3399_BCSH_R2Y_CSC_COE 0x00440 +-#define RK3399_BCSH_Y2R_CSC_COE 0x00460 +-#define RK3399_POST_YUV2YUV_Y2R_COE 0x00480 +-#define RK3399_POST_YUV2YUV_3X3_COE 0x004a0 +-#define RK3399_POST_YUV2YUV_R2Y_COE 0x004c0 +-#define RK3399_WIN0_YUV2YUV_Y2R 0x004e0 +-#define RK3399_WIN0_YUV2YUV_3X3 0x00500 +-#define RK3399_WIN0_YUV2YUV_R2Y 0x00520 +-#define RK3399_WIN1_YUV2YUV_Y2R 0x00540 +-#define RK3399_WIN1_YUV2YUV_3X3 0x00560 +-#define RK3399_WIN1_YUV2YUV_R2Y 0x00580 +-#define RK3399_WIN2_YUV2YUV_Y2R 0x005a0 +-#define RK3399_WIN2_YUV2YUV_3X3 0x005c0 +-#define RK3399_WIN2_YUV2YUV_R2Y 0x005e0 +-#define RK3399_WIN3_YUV2YUV_Y2R 0x00600 +-#define RK3399_WIN3_YUV2YUV_3X3 0x00620 +-#define RK3399_WIN3_YUV2YUV_R2Y 0x00640 +-#define RK3399_WIN2_LUT_ADDR 0x01000 +-#define RK3399_WIN3_LUT_ADDR 0x01400 +-#define RK3399_HWC_LUT_ADDR 0x01800 +-#define RK3399_CABC_GAMMA_LUT_ADDR 0x01c00 +-#define RK3399_GAMMA_LUT_ADDR 0x02000 +-/* rk3399 register definition end */ +- + #endif /* _ROCKCHIP_VOP_REG_H */ +-- +2.11.0 + diff --git a/patches.drivers/0070-drm-rockchip-vop-rk3328-fix-overlay-abnormal.patch b/patches.drivers/0070-drm-rockchip-vop-rk3328-fix-overlay-abnormal.patch new file mode 100644 index 0000000..fc00ada --- /dev/null +++ b/patches.drivers/0070-drm-rockchip-vop-rk3328-fix-overlay-abnormal.patch @@ -0,0 +1,74 @@ +From e63aaec6f830932e532777d186a68f16b899fd0a Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Wed, 26 Jul 2017 14:19:39 +0800 +Subject: [PATCH 70/86] drm/rockchip: vop: rk3328: fix overlay abnormal + +Git-commit: 9dd2aca46a13cc7177625f0dc3aaf5b7ebc6fe74 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +It's a hardware bug, all window's overlay channel reset +value is same, hardware overlay would be die. + +so we must initial difference id for each overlay channel. + +The Channel register is supported on all vop will full design. +Following is the details for this register +VOP_WIN0_CTRL2 + bit[7:4] win_rid_win0_cbr + axi read id of win0 cbr channel + bit[3:0] win_rid_win0_yrgb + axi read id of win0 yrgb channel + +Signed-off-by: Mark Yao +Reviewed-by: Jeffy Chen +Tested-by: Heiko Stuebner +Link: https://patchwork.freedesktop.org/patch/msgid/1501049980-6239-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 2 ++ + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 + + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 1 + + 3 files changed, 4 insertions(+) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index b80d581e05fd..9b95a0aeab06 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -1455,7 +1455,9 @@ static int vop_initial(struct vop *vop) + + for (i = 0; i < vop_data->win_size; i++) { + const struct vop_win_data *win = &vop_data->win[i]; ++ int channel = i * 2 + 1; + ++ VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel); + VOP_WIN_SET(vop, win, enable, 0); + VOP_WIN_SET(vop, win, gate, 1); + } +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index 43d08c88f1f8..af1091f51a80 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -141,6 +141,7 @@ struct vop_win_phy { + + struct vop_reg dst_alpha_ctl; + struct vop_reg src_alpha_ctl; ++ struct vop_reg channel; + }; + + struct vop_win_data { +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +index bc7b2d086109..94de7b9f6fde 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +@@ -197,6 +197,7 @@ static const struct vop_win_phy rk3288_win01_data = { + .uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16), + .src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0), + .dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0), ++ .channel = VOP_REG(RK3288_WIN0_CTRL2, 0xff, 0), + }; + + static const struct vop_win_phy rk3288_win23_data = { +-- +2.11.0 + diff --git a/patches.drivers/0071-drm-rockchip-vop-fix-iommu-page-fault-when-resume.patch b/patches.drivers/0071-drm-rockchip-vop-fix-iommu-page-fault-when-resume.patch new file mode 100644 index 0000000..6f2ef3b --- /dev/null +++ b/patches.drivers/0071-drm-rockchip-vop-fix-iommu-page-fault-when-resume.patch @@ -0,0 +1,103 @@ +From c7e9de10db4b760bd5d25f4a5c962a96b7d9bfd1 Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Mon, 31 Jul 2017 17:49:42 +0800 +Subject: [PATCH 71/86] drm/rockchip: vop: fix iommu page fault when resume + +Git-commit: da6c9bbf418dcb0f8be261f4353400fa959b1e92 +Patch-mainline: v4.13-rc5 +References: fate#323912 + +Iommu would get page fault with following path: + vop_disable: + 1, disable all windows and set vop config done + 2, vop enter to standy, all windows not works, but their registers + are not clean, when you read window's enable bit, may found the + window is enable. + + vop_enable: + 1, memcpy(vop->regsbak, vop->regs, len) + save current vop registers to vop->regsbak, then you can found + window is enable on regsbak. + 2, VOP_WIN_SET(vop, win, gate, 1); + force enable window gate, but gate and enable are on same + hardware register, then window enable bit rewrite to vop hardware. + 3, vop power on, and vop might try to scan destroyed buffer, + then iommu get page fault. + +Move windows disable after vop regsbak restore, then vop regsbak mechanism +would keep tracing the modify, everything would be safe. + +Signed-off-by: Mark Yao +Reviewed-by: Sandy huang +Link: https://patchwork.freedesktop.org/patch/msgid/1501494582-6934-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 33 +++++++++++++---------------- + 1 file changed, 15 insertions(+), 18 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index 9b95a0aeab06..f22ff38cae26 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -495,7 +495,7 @@ static void vop_line_flag_irq_disable(struct vop *vop) + static int vop_enable(struct drm_crtc *crtc) + { + struct vop *vop = to_vop(crtc); +- int ret; ++ int ret, i; + + ret = pm_runtime_get_sync(vop->dev); + if (ret < 0) { +@@ -528,6 +528,20 @@ static int vop_enable(struct drm_crtc *crtc) + } + + memcpy(vop->regs, vop->regsbak, vop->len); ++ /* ++ * We need to make sure that all windows are disabled before we ++ * enable the crtc. Otherwise we might try to scan from a destroyed ++ * buffer later. ++ */ ++ for (i = 0; i < vop->data->win_size; i++) { ++ struct vop_win *vop_win = &vop->win[i]; ++ const struct vop_win_data *win = vop_win->data; ++ ++ spin_lock(&vop->reg_lock); ++ VOP_WIN_SET(vop, win, enable, 0); ++ spin_unlock(&vop->reg_lock); ++ } ++ + vop_cfg_done(vop); + + /* +@@ -561,28 +575,11 @@ static int vop_enable(struct drm_crtc *crtc) + static void vop_crtc_disable(struct drm_crtc *crtc) + { + struct vop *vop = to_vop(crtc); +- int i; + + WARN_ON(vop->event); + + rockchip_drm_psr_deactivate(&vop->crtc); + +- /* +- * We need to make sure that all windows are disabled before we +- * disable that crtc. Otherwise we might try to scan from a destroyed +- * buffer later. +- */ +- for (i = 0; i < vop->data->win_size; i++) { +- struct vop_win *vop_win = &vop->win[i]; +- const struct vop_win_data *win = vop_win->data; +- +- spin_lock(&vop->reg_lock); +- VOP_WIN_SET(vop, win, enable, 0); +- spin_unlock(&vop->reg_lock); +- } +- +- vop_cfg_done(vop); +- + drm_crtc_vblank_off(crtc); + + /* +-- +2.11.0 + diff --git a/patches.drivers/0072-drm-rockchip-vop-fix-NV12-video-display-error.patch b/patches.drivers/0072-drm-rockchip-vop-fix-NV12-video-display-error.patch new file mode 100644 index 0000000..0dbb471 --- /dev/null +++ b/patches.drivers/0072-drm-rockchip-vop-fix-NV12-video-display-error.patch @@ -0,0 +1,37 @@ +From 907abb8cb26375314cc5f89c0b2dab7141135f67 Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Mon, 31 Jul 2017 17:49:46 +0800 +Subject: [PATCH 72/86] drm/rockchip: vop: fix NV12 video display error + +Git-commit: 6f04f5925ce763e07cb405d1fbe97a53b6c026a3 +Patch-mainline: v4.13-rc5 +References: fate#323912 + +fixup the scale calculation formula on the case +src_height == (dst_height/2). + +Signed-off-by: Mark Yao +Reviewed-by: Sandy huang +Link: https://patchwork.freedesktop.org/patch/msgid/1501494586-6984-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +index af1091f51a80..56bbd2e2a8ef 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +@@ -299,6 +299,9 @@ static inline uint16_t scl_get_bili_dn_vskip(int src_h, int dst_h, + + act_height = (src_h + vskiplines - 1) / vskiplines; + ++ if (act_height == dst_h) ++ return GET_SCL_FT_BILI_DN(src_h, dst_h) / vskiplines; ++ + return GET_SCL_FT_BILI_DN(act_height, dst_h); + } + +-- +2.11.0 + diff --git a/patches.drivers/0073-drm-rockchip-vop-round_up-pitches-to-word-align.patch b/patches.drivers/0073-drm-rockchip-vop-round_up-pitches-to-word-align.patch new file mode 100644 index 0000000..75a21b6 --- /dev/null +++ b/patches.drivers/0073-drm-rockchip-vop-round_up-pitches-to-word-align.patch @@ -0,0 +1,54 @@ +From 8050670da4659a15d4539433d3a0e0e9b1e7abc6 Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Mon, 31 Jul 2017 17:49:50 +0800 +Subject: [PATCH 73/86] drm/rockchip: vop: round_up pitches to word align + +Git-commit: 79a0b149d4e3d45fc46673f4b29a157776c174e2 +Patch-mainline: v4.13-rc5 +References: fate#323912 + +VOP pitch register is word align, need align to word. + +VOP_WIN0_VIR: + bit[31:16] win0_vir_stride_uv + Number of words of Win0 uv Virtual width + bit[15:0] win0_vir_width + Number of words of Win0 yrgb Virtual width + ARGB888 : win0_vir_width + RGB888 : (win0_vir_width*3/4) + (win0_vir_width%3) + RGB565 : ceil(win0_vir_width/2) + YUV : ceil(win0_vir_width/4) + +Signed-off-by: Mark Yao +Reviewed-by: Sandy huang +Link: https://patchwork.freedesktop.org/patch/msgid/1501494591-7034-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index f22ff38cae26..193150c70817 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -756,7 +756,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, + spin_lock(&vop->reg_lock); + + VOP_WIN_SET(vop, win, format, format); +- VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2); ++ VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); + VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); + if (is_yuv_support(fb->format->format)) { + int hsub = drm_format_horz_chroma_subsampling(fb->format->format); +@@ -770,7 +770,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, + offset += (src->y1 >> 16) * fb->pitches[1] / vsub; + + dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; +- VOP_WIN_SET(vop, win, uv_vir, fb->pitches[1] >> 2); ++ VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); + VOP_WIN_SET(vop, win, uv_mst, dma_addr); + } + +-- +2.11.0 + diff --git a/patches.drivers/0074-drm-rockchip-vop-report-error-when-check-resource-er.patch b/patches.drivers/0074-drm-rockchip-vop-report-error-when-check-resource-er.patch new file mode 100644 index 0000000..f67105d --- /dev/null +++ b/patches.drivers/0074-drm-rockchip-vop-report-error-when-check-resource-er.patch @@ -0,0 +1,40 @@ +From 92ca9a50957c806177fec43c4766edef20165cfe Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Mon, 31 Jul 2017 17:49:55 +0800 +Subject: [PATCH 74/86] drm/rockchip: vop: report error when check resource + error + +Git-commit: 80c471ea040ad9006ebff6d64221a04e8fa1b7f6 +Patch-mainline: v4.13-rc5 +References: fate#323912 + +The user would be confused while facing a error commit without +any error report. + +Signed-off-by: Mark Yao +Reviewed-by: Sandy huang +Link: https://patchwork.freedesktop.org/patch/msgid/1501494596-7090-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index 193150c70817..848c1fc2397a 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -674,8 +674,10 @@ static int vop_plane_atomic_check(struct drm_plane *plane, + * Src.x1 can be odd when do clip, but yuv plane start point + * need align with 2 pixel. + */ +- if (is_yuv_support(fb->format->format) && ((state->src.x1 >> 16) % 2)) ++ if (is_yuv_support(fb->format->format) && ((state->src.x1 >> 16) % 2)) { ++ DRM_ERROR("Invalid Source: Yuv format not support odd xpos\n"); + return -EINVAL; ++ } + + return 0; + } +-- +2.11.0 + diff --git a/patches.drivers/0075-drm-rockchip-vop-no-need-wait-vblank-on-crtc-enable.patch b/patches.drivers/0075-drm-rockchip-vop-no-need-wait-vblank-on-crtc-enable.patch new file mode 100644 index 0000000..c550597 --- /dev/null +++ b/patches.drivers/0075-drm-rockchip-vop-no-need-wait-vblank-on-crtc-enable.patch @@ -0,0 +1,70 @@ +From 3f50d2cb43406ca9ef5a094a2b54b32476d71974 Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Mon, 31 Jul 2017 17:49:37 +0800 +Subject: [PATCH 75/86] drm/rockchip: vop: no need wait vblank on crtc enable + +Git-commit: b5015e92a041443f8f4d5fd89e68ccaa672ccbe2 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Since atomic framework, crtc enable and disable are in pairs, +no need to wait vblank. + +Signed-off-by: Mark Yao +Reviewed-by: Sandy huang +Link: https://patchwork.freedesktop.org/patch/msgid/1501494577-6884-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 36 ----------------------------- + 1 file changed, 36 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index 848c1fc2397a..4b6892d3e713 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -892,42 +892,6 @@ static void vop_crtc_enable(struct drm_crtc *crtc) + return; + } + +- /* +- * If dclk rate is zero, mean that scanout is stop, +- * we don't need wait any more. +- */ +- if (clk_get_rate(vop->dclk)) { +- /* +- * Rk3288 vop timing register is immediately, when configure +- * display timing on display time, may cause tearing. +- * +- * Vop standby will take effect at end of current frame, +- * if dsp hold valid irq happen, it means standby complete. +- * +- * mode set: +- * standby and wait complete --> |---- +- * | display time +- * |---- +- * |---> dsp hold irq +- * configure display timing --> | +- * standby exit | +- * | new frame start. +- */ +- +- reinit_completion(&vop->dsp_hold_completion); +- vop_dsp_hold_valid_irq_enable(vop); +- +- spin_lock(&vop->reg_lock); +- +- VOP_REG_SET(vop, common, standby, 1); +- +- spin_unlock(&vop->reg_lock); +- +- wait_for_completion(&vop->dsp_hold_completion); +- +- vop_dsp_hold_valid_irq_disable(vop); +- } +- + pin_pol = BIT(DCLK_INVERT); + pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ? + BIT(HSYNC_POSITIVE) : 0; +-- +2.11.0 + diff --git a/patches.drivers/0076-drm-rockchip-fix-race-with-kms-hotplug-and-fbdev.patch b/patches.drivers/0076-drm-rockchip-fix-race-with-kms-hotplug-and-fbdev.patch new file mode 100644 index 0000000..14ea3bf --- /dev/null +++ b/patches.drivers/0076-drm-rockchip-fix-race-with-kms-hotplug-and-fbdev.patch @@ -0,0 +1,78 @@ +From ecdb7016726381d46e566b8d2366d00b70f408c2 Mon Sep 17 00:00:00 2001 +From: Mark yao +Date: Tue, 1 Aug 2017 16:11:43 +0800 +Subject: [PATCH 76/86] drm/rockchip: fix race with kms hotplug and fbdev + +Git-commit: 8415ab565da966b2bf85ed9322784cd15770a66e +Patch-mainline: v4.14-rc1 +References: fate#323912 + +According to the kerneldoc[0], should do fbdev setup before calling +drm_kms_helper_poll_init(), otherwise, Kms hotplug event may race +into fbdev helper initial, and fb_helper->dev may be NULL pointer, +that would cause the bug: +[ 0.735411] [00000200] *pgd=00000000f6ffe003, *pud=00000000f6ffe003, *pmd=0000000000000000 +[ 0.736156] Internal error: Oops: 96000005 [#1] PREEMPT SMP +[ 0.736648] Modules linked in: +[ 0.736930] CPU: 2 PID: 20 Comm: kworker/2:0 Not tainted 4.4.41 #20 +[ 0.737480] Hardware name: Rockchip RK3399 Board rev2 (BOX) (DT) +[ 0.738020] Workqueue: events cdn_dp_pd_event_work +[ 0.738447] task: ffffffc0f21f3100 ti: ffffffc0f2218000 task.ti: ffffffc0f2218000 +[ 0.739109] PC is at mutex_lock+0x14/0x44 +[ 0.739469] LR is at drm_fb_helper_hotplug_event+0x30/0x114 +[ 0.756253] [] mutex_lock+0x14/0x44 +[ 0.756260] [] drm_fb_helper_hotplug_event+0x30/0x114 +[ 0.756271] [] rockchip_drm_output_poll_changed+0x18/0x20 +[ 0.756280] [] drm_kms_helper_hotplug_event+0x28/0x34 +[ 0.756286] [] cdn_dp_pd_event_work+0x394/0x3c4 +[ 0.756295] [] process_one_work+0x218/0x3e0 +[ 0.756302] [] worker_thread+0x2e8/0x404 +[ 0.756308] [] kthread+0xe8/0xf0 +[ 0.756316] [] ret_from_fork+0x10/0x40 + +[0]: https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms-helpers.html + +Signed-off-by: Mark Yao +Reviewed-by: Sandy huang +Link: https://patchwork.freedesktop.org/patch/msgid/1501575103-20136-1-git-send-email-mark.yao@rock-chips.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 13 ++++++------- + 1 file changed, 6 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index 488fc2191baa..7eeeeaad24cc 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -161,22 +161,21 @@ static int rockchip_drm_bind(struct device *dev) + */ + drm_dev->irq_enabled = true; + +- /* init kms poll for handling hpd */ +- drm_kms_helper_poll_init(drm_dev); +- + ret = rockchip_drm_fbdev_init(drm_dev); + if (ret) +- goto err_kms_helper_poll_fini; ++ goto err_unbind_all; ++ ++ /* init kms poll for handling hpd */ ++ drm_kms_helper_poll_init(drm_dev); + + ret = drm_dev_register(drm_dev, 0); + if (ret) +- goto err_fbdev_fini; ++ goto err_kms_helper_poll_fini; + + return 0; +-err_fbdev_fini: +- rockchip_drm_fbdev_fini(drm_dev); + err_kms_helper_poll_fini: + drm_kms_helper_poll_fini(drm_dev); ++ rockchip_drm_fbdev_fini(drm_dev); + err_unbind_all: + component_unbind_all(dev, drm_dev); + err_mode_config_cleanup: +-- +2.11.0 + diff --git a/patches.drivers/0077-drm-rockchip-make-drm_connector_funcs-structures-con.patch b/patches.drivers/0077-drm-rockchip-make-drm_connector_funcs-structures-con.patch new file mode 100644 index 0000000..e05e0c7 --- /dev/null +++ b/patches.drivers/0077-drm-rockchip-make-drm_connector_funcs-structures-con.patch @@ -0,0 +1,68 @@ +From 236ec16a59a020cf36c654ddec3773ae758ddff9 Mon Sep 17 00:00:00 2001 +From: Bhumika Goyal +Date: Tue, 8 Aug 2017 16:58:32 +0530 +Subject: [PATCH 77/86] drm/rockchip: make drm_connector_funcs structures const + +Git-commit: 3f5ce9f0e8ffc721b91ea4de6887401d9a5d1af8 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Make these const as they are only passed to the function +drm_connector_init and the corresponding argument is of type const. +Done using Coccinelle + +@match disable optional_qualifier@ +identifier s; +@@ +static struct drm_connector_funcs s = {...}; + +@ref@ +position p; +identifier match.s; +@@ +s@p + +@good1@ +identifier match.s; +expression e1,e2; +position ref.p; +@@ +drm_connector_init(e1,e2,&s@p,...) + +@bad depends on !good1@ +position ref.p; +identifier match.s; +@@ +s@p + +@depends on forall !bad disable optional_qualifier@ +identifier match.s; +@@ +static ++ const +struct drm_connector_funcs s; + +Signed-off-by: Bhumika Goyal +Signed-off-by: Daniel Vetter +Link: https://patchwork.freedesktop.org/patch/msgid/1502191712-11231-4-git-send-email-bhumirks@gmail.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/inno_hdmi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c +index 7d9b75eb6c44..f3bfe253b279 100644 +--- a/drivers/gpu/drm/rockchip/inno_hdmi.c ++++ b/drivers/gpu/drm/rockchip/inno_hdmi.c +@@ -592,7 +592,7 @@ static void inno_hdmi_connector_destroy(struct drm_connector *connector) + drm_connector_cleanup(connector); + } + +-static struct drm_connector_funcs inno_hdmi_connector_funcs = { ++static const struct drm_connector_funcs inno_hdmi_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .fill_modes = inno_hdmi_probe_single_connector_modes, + .detect = inno_hdmi_connector_detect, +-- +2.11.0 + diff --git a/patches.drivers/0078-drm-rockchip-Fix-suspend-crash-when-drm-is-not-bound.patch b/patches.drivers/0078-drm-rockchip-Fix-suspend-crash-when-drm-is-not-bound.patch new file mode 100644 index 0000000..8b575df --- /dev/null +++ b/patches.drivers/0078-drm-rockchip-Fix-suspend-crash-when-drm-is-not-bound.patch @@ -0,0 +1,72 @@ +From 9053318c140e8b82c131b17e898551b7a037a115 Mon Sep 17 00:00:00 2001 +From: Jeffy Chen +Date: Wed, 9 Aug 2017 18:41:03 +0800 +Subject: [PATCH 78/86] drm/rockchip: Fix suspend crash when drm is not bound + +Git-commit: 0fa375e6bc9023211eead30a6a79963c45a563da +Patch-mainline: v4.13-rc7 +References: fate#323912 + +Currently we are allocating drm_device in rockchip_drm_bind, so if the +suspend/resume code access it when drm is not bound, we would hit this +crash: + +[ 253.402836] Unable to handle kernel NULL pointer dereference at virtual address 00000028 +[ 253.402837] pgd = ffffffc06c9b0000 +[ 253.402841] [00000028] *pgd=0000000000000000, *pud=0000000000000000 +[ 253.402844] Internal error: Oops: 96000005 [#1] PREEMPT SMP +[ 253.402859] Modules linked in: btusb btrtl btbcm btintel bluetooth ath10k_pci ath10k_core ar10k_ath ar10k_mac80211 cfg80211 ip6table_filter asix usbnet mii +[ 253.402864] CPU: 4 PID: 1331 Comm: cat Not tainted 4.4.70 #15 +[ 253.402865] Hardware name: Google Scarlet (DT) +[ 253.402867] task: ffffffc076c0ce00 ti: ffffffc06c2c8000 task.ti: ffffffc06c2c8000 +[ 253.402871] PC is at rockchip_drm_sys_suspend+0x20/0x5c + +Add sanity checks to prevent that. + +Reported-by: Brian Norris +Signed-off-by: Jeffy Chen +Signed-off-by: Sean Paul +Link: https://patchwork.kernel.org/patch/9890297/ +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index 7eeeeaad24cc..27e750cc40f9 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -270,11 +270,15 @@ static void rockchip_drm_fb_resume(struct drm_device *drm) + static int rockchip_drm_sys_suspend(struct device *dev) + { + struct drm_device *drm = dev_get_drvdata(dev); +- struct rockchip_drm_private *priv = drm->dev_private; ++ struct rockchip_drm_private *priv; ++ ++ if (!drm) ++ return 0; + + drm_kms_helper_poll_disable(drm); + rockchip_drm_fb_suspend(drm); + ++ priv = drm->dev_private; + priv->state = drm_atomic_helper_suspend(drm); + if (IS_ERR(priv->state)) { + rockchip_drm_fb_resume(drm); +@@ -288,8 +292,12 @@ static int rockchip_drm_sys_suspend(struct device *dev) + static int rockchip_drm_sys_resume(struct device *dev) + { + struct drm_device *drm = dev_get_drvdata(dev); +- struct rockchip_drm_private *priv = drm->dev_private; ++ struct rockchip_drm_private *priv; ++ ++ if (!drm) ++ return 0; + ++ priv = drm->dev_private; + drm_atomic_helper_resume(drm, priv->state); + rockchip_drm_fb_resume(drm); + drm_kms_helper_poll_enable(drm); +-- +2.11.0 + diff --git a/patches.drivers/0079-drm-rockchip-switch-to-drm_-_get-drm_-_put-helpers.patch b/patches.drivers/0079-drm-rockchip-switch-to-drm_-_get-drm_-_put-helpers.patch new file mode 100644 index 0000000..8307653 --- /dev/null +++ b/patches.drivers/0079-drm-rockchip-switch-to-drm_-_get-drm_-_put-helpers.patch @@ -0,0 +1,113 @@ +From 0ba70d47f666f84f7021af3989f5ebfc40b90333 Mon Sep 17 00:00:00 2001 +From: Cihangir Akturk +Date: Fri, 11 Aug 2017 15:33:06 +0300 +Subject: [PATCH 79/86] drm/rockchip: switch to drm_*_get(), drm_*_put() + helpers + +Git-commit: adedbf0325ef885e9599f9c812dc3bf5610adcf8 +Patch-mainline: v4.14-rc1 +References: fate#323912 + +Use drm_*_get() and drm_*_put() helpers instead of drm_*_reference() +and drm_*_unreference() helpers. + +drm_*_reference() and drm_*_unreference() functions are just +compatibility alias for drm_*_get() and drm_*_put() and should not be +used by new code. So convert all users of compatibility functions to +use the new APIs. + +Generated by: scripts/coccinelle/api/drm-get-put.cocci + +Signed-off-by: Cihangir Akturk +Signed-off-by: Sean Paul +Link: https://patchwork.freedesktop.org/patch/msgid/1502454794-28558-21-git-send-email-cakturk@gmail.com +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 6 +++--- + drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 2 +- + drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 2 +- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 4 ++-- + 4 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +index df6bceabeca8..83671d0a609e 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +@@ -48,7 +48,7 @@ static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb) + int i; + + for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) +- drm_gem_object_unreference_unlocked(rockchip_fb->obj[i]); ++ drm_gem_object_put_unlocked(rockchip_fb->obj[i]); + + drm_framebuffer_cleanup(fb); + kfree(rockchip_fb); +@@ -144,7 +144,7 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, + width * drm_format_plane_cpp(mode_cmd->pixel_format, i); + + if (obj->size < min_size) { +- drm_gem_object_unreference_unlocked(obj); ++ drm_gem_object_put_unlocked(obj); + ret = -EINVAL; + goto err_gem_object_unreference; + } +@@ -161,7 +161,7 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, + + err_gem_object_unreference: + for (i--; i >= 0; i--) +- drm_gem_object_unreference_unlocked(objs[i]); ++ drm_gem_object_put_unlocked(objs[i]); + return ERR_PTR(ret); + } + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +index ce946b9c57a9..724579ebf947 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +@@ -173,7 +173,7 @@ void rockchip_drm_fbdev_fini(struct drm_device *dev) + drm_fb_helper_unregister_fbi(helper); + + if (helper->fb) +- drm_framebuffer_unreference(helper->fb); ++ drm_framebuffer_put(helper->fb); + + drm_fb_helper_fini(helper); + } +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +index f74333efe4bb..1869c8bb76c8 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +@@ -383,7 +383,7 @@ rockchip_gem_create_with_handle(struct drm_file *file_priv, + goto err_handle_create; + + /* drop reference from allocate - handle holds it now. */ +- drm_gem_object_unreference_unlocked(obj); ++ drm_gem_object_put_unlocked(obj); + + return rk_obj; + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index 4b6892d3e713..41bcd3c0faef 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -1024,7 +1024,7 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc, + if (old_plane_state->fb == new_plane_state->fb) + continue; + +- drm_framebuffer_reference(old_plane_state->fb); ++ drm_framebuffer_get(old_plane_state->fb); + drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb); + set_bit(VOP_PENDING_FB_UNREF, &vop->pending); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); +@@ -1148,7 +1148,7 @@ static void vop_fb_unref_worker(struct drm_flip_work *work, void *val) + struct drm_framebuffer *fb = val; + + drm_crtc_vblank_put(&vop->crtc); +- drm_framebuffer_unreference(fb); ++ drm_framebuffer_put(fb); + } + + static void vop_handle_vblank(struct vop *vop) +-- +2.11.0 + diff --git a/patches.drivers/0080-drm-rockchip-Add-support-for-Rockchip-Soc-LVDS.patch b/patches.drivers/0080-drm-rockchip-Add-support-for-Rockchip-Soc-LVDS.patch new file mode 100644 index 0000000..e3bb168 --- /dev/null +++ b/patches.drivers/0080-drm-rockchip-Add-support-for-Rockchip-Soc-LVDS.patch @@ -0,0 +1,794 @@ +From f911feb1996b3dcf40ed659e57c5b443a51b331c Mon Sep 17 00:00:00 2001 +From: Sandy Huang +Date: Sat, 2 Sep 2017 19:28:54 +0800 +Subject: [PATCH 80/86] drm/rockchip: Add support for Rockchip Soc LVDS + +Git-commit: 34cc0aa2545603560c79aaea3340d8ff3a71bd10 +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +This adds support for Rockchip soc lvds found on rk3288 +Based on the patches from Mark yao and Heiko Stuebner. + +Signed-off-by: Mark Yao +Signed-off-by: Heiko Stuebner +Signed-off-by: Sandy Huang +Reviewed-by: Mark Yao +Tested-by: Wadim Egorov +Link: https://patchwork.freedesktop.org/patch/msgid/1504351737-136042-1-git-send-email-hjc@rock-chips.com +Signed-off-by: Mark Yao +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/Kconfig | 8 + + drivers/gpu/drm/rockchip/Makefile | 1 + + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 + + drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 + + drivers/gpu/drm/rockchip/rockchip_lvds.c | 581 ++++++++++++++++++++++++++++ + drivers/gpu/drm/rockchip/rockchip_lvds.h | 114 ++++++ + 6 files changed, 707 insertions(+) + create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.c + create mode 100644 drivers/gpu/drm/rockchip/rockchip_lvds.h + +diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig +index dcc539ba85d6..0c31f0a27b9c 100644 +--- a/drivers/gpu/drm/rockchip/Kconfig ++++ b/drivers/gpu/drm/rockchip/Kconfig +@@ -57,4 +57,12 @@ config ROCKCHIP_INNO_HDMI + for the Innosilicon HDMI driver. If you want to enable + HDMI on RK3036 based SoC, you should select this option. + ++config ROCKCHIP_LVDS ++ bool "Rockchip LVDS support" ++ depends on DRM_ROCKCHIP ++ help ++ Choose this option to enable support for Rockchip LVDS controllers. ++ Rockchip rk3288 SoC has LVDS TX Controller can be used, and it ++ support LVDS, rgb, dual LVDS output mode. say Y to enable its ++ driver. + endif +diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile +index fa8dc9d9aac2..a881d2cc4f25 100644 +--- a/drivers/gpu/drm/rockchip/Makefile ++++ b/drivers/gpu/drm/rockchip/Makefile +@@ -12,5 +12,6 @@ rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o + rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o + rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o + rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o ++rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o + + obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index 27e750cc40f9..c33dbd20f962 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -453,6 +453,8 @@ static int __init rockchip_drm_init(void) + + num_rockchip_sub_drivers = 0; + ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP); ++ ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver, ++ CONFIG_ROCKCHIP_LVDS); + ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver, + CONFIG_ROCKCHIP_ANALOGIX_DP); + ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP); +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +index c7e96b82cf63..498dfbc52cec 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +@@ -69,5 +69,6 @@ extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; + extern struct platform_driver dw_mipi_dsi_driver; + extern struct platform_driver inno_hdmi_driver; + extern struct platform_driver rockchip_dp_driver; ++extern struct platform_driver rockchip_lvds_driver; + extern struct platform_driver vop_platform_driver; + #endif /* _ROCKCHIP_DRM_DRV_H_ */ +diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c +new file mode 100644 +index 000000000000..c5fbe533796c +--- /dev/null ++++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c +@@ -0,0 +1,581 @@ ++/* ++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd ++ * Author: ++ * Mark Yao ++ * Sandy Huang ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rockchip_drm_drv.h" ++#include "rockchip_drm_vop.h" ++#include "rockchip_lvds.h" ++ ++#define DISPLAY_OUTPUT_RGB 0 ++#define DISPLAY_OUTPUT_LVDS 1 ++#define DISPLAY_OUTPUT_DUAL_LVDS 2 ++ ++#define connector_to_lvds(c) \ ++ container_of(c, struct rockchip_lvds, connector) ++ ++#define encoder_to_lvds(c) \ ++ container_of(c, struct rockchip_lvds, encoder) ++ ++/** ++ * rockchip_lvds_soc_data - rockchip lvds Soc private data ++ * @ch1_offset: lvds channel 1 registe offset ++ * grf_soc_con6: general registe offset for LVDS contrl ++ * grf_soc_con7: general registe offset for LVDS contrl ++ * has_vop_sel: to indicate whether need to choose from different VOP. ++ */ ++struct rockchip_lvds_soc_data { ++ u32 ch1_offset; ++ int grf_soc_con6; ++ int grf_soc_con7; ++ bool has_vop_sel; ++}; ++ ++struct rockchip_lvds { ++ struct device *dev; ++ void __iomem *regs; ++ struct regmap *grf; ++ struct clk *pclk; ++ const struct rockchip_lvds_soc_data *soc_data; ++ int output; /* rgb lvds or dual lvds output */ ++ int format; /* vesa or jeida format */ ++ struct drm_device *drm_dev; ++ struct drm_panel *panel; ++ struct drm_bridge *bridge; ++ struct drm_connector connector; ++ struct drm_encoder encoder; ++ struct dev_pin_info *pins; ++}; ++ ++static inline void lvds_writel(struct rockchip_lvds *lvds, u32 offset, u32 val) ++{ ++ writel_relaxed(val, lvds->regs + offset); ++ if (lvds->output == DISPLAY_OUTPUT_LVDS) ++ return; ++ writel_relaxed(val, lvds->regs + offset + lvds->soc_data->ch1_offset); ++} ++ ++static inline int lvds_name_to_format(const char *s) ++{ ++ if (strncmp(s, "jeida-18", 8) == 0) ++ return LVDS_JEIDA_18; ++ else if (strncmp(s, "jeida-24", 8) == 0) ++ return LVDS_JEIDA_24; ++ else if (strncmp(s, "vesa-24", 7) == 0) ++ return LVDS_VESA_24; ++ ++ return -EINVAL; ++} ++ ++static inline int lvds_name_to_output(const char *s) ++{ ++ if (strncmp(s, "rgb", 3) == 0) ++ return DISPLAY_OUTPUT_RGB; ++ else if (strncmp(s, "lvds", 4) == 0) ++ return DISPLAY_OUTPUT_LVDS; ++ else if (strncmp(s, "duallvds", 8) == 0) ++ return DISPLAY_OUTPUT_DUAL_LVDS; ++ ++ return -EINVAL; ++} ++ ++static int rockchip_lvds_poweron(struct rockchip_lvds *lvds) ++{ ++ int ret; ++ u32 val; ++ ++ ret = clk_enable(lvds->pclk); ++ if (ret < 0) { ++ DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret); ++ return ret; ++ } ++ ret = pm_runtime_get_sync(lvds->dev); ++ if (ret < 0) { ++ DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret); ++ clk_disable(lvds->pclk); ++ return ret; ++ } ++ val = RK3288_LVDS_CH0_REG0_LANE4_EN | RK3288_LVDS_CH0_REG0_LANE3_EN | ++ RK3288_LVDS_CH0_REG0_LANE2_EN | RK3288_LVDS_CH0_REG0_LANE1_EN | ++ RK3288_LVDS_CH0_REG0_LANE0_EN; ++ if (lvds->output == DISPLAY_OUTPUT_RGB) { ++ val |= RK3288_LVDS_CH0_REG0_TTL_EN | ++ RK3288_LVDS_CH0_REG0_LANECK_EN; ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG2, ++ RK3288_LVDS_PLL_FBDIV_REG2(0x46)); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG4, ++ RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE | ++ RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE | ++ RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE | ++ RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE | ++ RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE | ++ RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG5, ++ RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA | ++ RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA | ++ RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA | ++ RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA | ++ RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA | ++ RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA); ++ } else { ++ val |= RK3288_LVDS_CH0_REG0_LVDS_EN | ++ RK3288_LVDS_CH0_REG0_LANECK_EN; ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG0, val); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG1, ++ RK3288_LVDS_CH0_REG1_LANECK_BIAS | ++ RK3288_LVDS_CH0_REG1_LANE4_BIAS | ++ RK3288_LVDS_CH0_REG1_LANE3_BIAS | ++ RK3288_LVDS_CH0_REG1_LANE2_BIAS | ++ RK3288_LVDS_CH0_REG1_LANE1_BIAS | ++ RK3288_LVDS_CH0_REG1_LANE0_BIAS); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG2, ++ RK3288_LVDS_CH0_REG2_RESERVE_ON | ++ RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE | ++ RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE | ++ RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE | ++ RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE | ++ RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE | ++ RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE | ++ RK3288_LVDS_PLL_FBDIV_REG2(0x46)); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00); ++ } ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG3, RK3288_LVDS_PLL_FBDIV_REG3(0x46)); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REGD, RK3288_LVDS_PLL_PREDIV_REGD(0x0a)); ++ lvds_writel(lvds, RK3288_LVDS_CH0_REG20, RK3288_LVDS_CH0_REG20_LSB); ++ ++ lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE); ++ lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE); ++ ++ return 0; ++} ++ ++static void rockchip_lvds_poweroff(struct rockchip_lvds *lvds) ++{ ++ int ret; ++ u32 val; ++ ++ lvds_writel(lvds, RK3288_LVDS_CFG_REG21, RK3288_LVDS_CFG_REG21_TX_ENABLE); ++ lvds_writel(lvds, RK3288_LVDS_CFG_REGC, RK3288_LVDS_CFG_REGC_PLL_ENABLE); ++ val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN; ++ val |= val << 16; ++ ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val); ++ if (ret != 0) ++ DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret); ++ ++ pm_runtime_put(lvds->dev); ++ clk_disable(lvds->pclk); ++} ++ ++static const struct drm_connector_funcs rockchip_lvds_connector_funcs = { ++ .fill_modes = drm_helper_probe_single_connector_modes, ++ .destroy = drm_connector_cleanup, ++ .reset = drm_atomic_helper_connector_reset, ++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, ++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, ++}; ++ ++static int rockchip_lvds_connector_get_modes(struct drm_connector *connector) ++{ ++ struct rockchip_lvds *lvds = connector_to_lvds(connector); ++ struct drm_panel *panel = lvds->panel; ++ ++ return drm_panel_get_modes(panel); ++} ++ ++static const ++struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = { ++ .get_modes = rockchip_lvds_connector_get_modes, ++}; ++ ++static void rockchip_lvds_grf_config(struct drm_encoder *encoder, ++ struct drm_display_mode *mode) ++{ ++ struct rockchip_lvds *lvds = encoder_to_lvds(encoder); ++ u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0; ++ u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0; ++ u32 val; ++ int ret; ++ ++ /* iomux to LCD data/sync mode */ ++ if (lvds->output == DISPLAY_OUTPUT_RGB) ++ if (lvds->pins && !IS_ERR(lvds->pins->default_state)) ++ pinctrl_select_state(lvds->pins->p, ++ lvds->pins->default_state); ++ val = lvds->format | LVDS_CH0_EN; ++ if (lvds->output == DISPLAY_OUTPUT_RGB) ++ val |= LVDS_TTL_EN | LVDS_CH1_EN; ++ else if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS) ++ val |= LVDS_DUAL | LVDS_CH1_EN; ++ ++ if ((mode->htotal - mode->hsync_start) & 0x01) ++ val |= LVDS_START_PHASE_RST_1; ++ ++ val |= (pin_dclk << 8) | (pin_hsync << 9); ++ val |= (0xffff << 16); ++ ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con7, val); ++ if (ret != 0) { ++ DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret); ++ return; ++ } ++} ++ ++static int rockchip_lvds_set_vop_source(struct rockchip_lvds *lvds, ++ struct drm_encoder *encoder) ++{ ++ u32 val; ++ int ret; ++ ++ if (!lvds->soc_data->has_vop_sel) ++ return 0; ++ ++ ret = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder); ++ if (ret < 0) ++ return ret; ++ ++ val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16; ++ if (ret) ++ val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT; ++ ++ ret = regmap_write(lvds->grf, lvds->soc_data->grf_soc_con6, val); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int ++rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder, ++ struct drm_crtc_state *crtc_state, ++ struct drm_connector_state *conn_state) ++{ ++ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); ++ ++ s->output_mode = ROCKCHIP_OUT_MODE_P888; ++ s->output_type = DRM_MODE_CONNECTOR_LVDS; ++ ++ return 0; ++} ++ ++static void rockchip_lvds_encoder_enable(struct drm_encoder *encoder) ++{ ++ struct rockchip_lvds *lvds = encoder_to_lvds(encoder); ++ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; ++ int ret; ++ ++ drm_panel_prepare(lvds->panel); ++ ret = rockchip_lvds_poweron(lvds); ++ if (ret < 0) { ++ DRM_DEV_ERROR(lvds->dev, "failed to power on lvds: %d\n", ret); ++ drm_panel_unprepare(lvds->panel); ++ } ++ rockchip_lvds_grf_config(encoder, mode); ++ rockchip_lvds_set_vop_source(lvds, encoder); ++ drm_panel_enable(lvds->panel); ++} ++ ++static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder) ++{ ++ struct rockchip_lvds *lvds = encoder_to_lvds(encoder); ++ ++ drm_panel_disable(lvds->panel); ++ rockchip_lvds_poweroff(lvds); ++ drm_panel_unprepare(lvds->panel); ++} ++ ++static const ++struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = { ++ .enable = rockchip_lvds_encoder_enable, ++ .disable = rockchip_lvds_encoder_disable, ++ .atomic_check = rockchip_lvds_encoder_atomic_check, ++}; ++ ++static const struct drm_encoder_funcs rockchip_lvds_encoder_funcs = { ++ .destroy = drm_encoder_cleanup, ++}; ++ ++static const struct rockchip_lvds_soc_data rk3288_lvds_data = { ++ .ch1_offset = 0x100, ++ .grf_soc_con6 = 0x025c, ++ .grf_soc_con7 = 0x0260, ++ .has_vop_sel = true, ++}; ++ ++static const struct of_device_id rockchip_lvds_dt_ids[] = { ++ { ++ .compatible = "rockchip,rk3288-lvds", ++ .data = &rk3288_lvds_data ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids); ++ ++static int rockchip_lvds_bind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct rockchip_lvds *lvds = dev_get_drvdata(dev); ++ struct drm_device *drm_dev = data; ++ struct drm_encoder *encoder; ++ struct drm_connector *connector; ++ struct device_node *remote = NULL; ++ struct device_node *port, *endpoint; ++ int ret; ++ const char *name; ++ u32 endpoint_id; ++ ++ lvds->drm_dev = drm_dev; ++ port = of_graph_get_port_by_id(dev->of_node, 1); ++ if (!port) { ++ DRM_DEV_ERROR(dev, ++ "can't found port point, please init lvds panel port!\n"); ++ return -EINVAL; ++ } ++ for_each_child_of_node(port, endpoint) { ++ of_property_read_u32(endpoint, "reg", &endpoint_id); ++ ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id, ++ &lvds->panel, &lvds->bridge); ++ if (!ret) ++ break; ++ } ++ if (ret) { ++ DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n"); ++ ret = -EPROBE_DEFER; ++ goto err_put_port; ++ } ++ if (lvds->panel) ++ remote = lvds->panel->dev->of_node; ++ else ++ remote = lvds->bridge->of_node; ++ if (of_property_read_string(dev->of_node, "rockchip,output", &name)) ++ /* default set it as output rgb */ ++ lvds->output = DISPLAY_OUTPUT_RGB; ++ else ++ lvds->output = lvds_name_to_output(name); ++ ++ if (lvds->output < 0) { ++ DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name); ++ ret = lvds->output; ++ goto err_put_remote; ++ } ++ ++ if (of_property_read_string(remote, "data-mapping", &name)) ++ /* default set it as format vesa 18 */ ++ lvds->format = LVDS_VESA_18; ++ else ++ lvds->format = lvds_name_to_format(name); ++ ++ if (lvds->format < 0) { ++ DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name); ++ ret = lvds->format; ++ goto err_put_remote; ++ } ++ ++ encoder = &lvds->encoder; ++ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, ++ dev->of_node); ++ ++ ret = drm_encoder_init(drm_dev, encoder, &rockchip_lvds_encoder_funcs, ++ DRM_MODE_ENCODER_LVDS, NULL); ++ if (ret < 0) { ++ DRM_DEV_ERROR(drm_dev->dev, ++ "failed to initialize encoder: %d\n", ret); ++ goto err_put_remote; ++ } ++ ++ drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs); ++ ++ if (lvds->panel) { ++ connector = &lvds->connector; ++ connector->dpms = DRM_MODE_DPMS_OFF; ++ ret = drm_connector_init(drm_dev, connector, ++ &rockchip_lvds_connector_funcs, ++ DRM_MODE_CONNECTOR_LVDS); ++ if (ret < 0) { ++ DRM_DEV_ERROR(drm_dev->dev, ++ "failed to initialize connector: %d\n", ret); ++ goto err_free_encoder; ++ } ++ ++ drm_connector_helper_add(connector, ++ &rockchip_lvds_connector_helper_funcs); ++ ++ ret = drm_mode_connector_attach_encoder(connector, encoder); ++ if (ret < 0) { ++ DRM_DEV_ERROR(drm_dev->dev, ++ "failed to attach encoder: %d\n", ret); ++ goto err_free_connector; ++ } ++ ++ ret = drm_panel_attach(lvds->panel, connector); ++ if (ret < 0) { ++ DRM_DEV_ERROR(drm_dev->dev, ++ "failed to attach panel: %d\n", ret); ++ goto err_free_connector; ++ } ++ } else { ++ lvds->bridge->encoder = encoder; ++ ret = drm_bridge_attach(encoder, lvds->bridge, NULL); ++ if (ret) { ++ DRM_DEV_ERROR(drm_dev->dev, ++ "failed to attach bridge: %d\n", ret); ++ goto err_free_encoder; ++ } ++ encoder->bridge = lvds->bridge; ++ } ++ ++ pm_runtime_enable(dev); ++ of_node_put(remote); ++ of_node_put(port); ++ ++ return 0; ++ ++err_free_connector: ++ drm_connector_cleanup(connector); ++err_free_encoder: ++ drm_encoder_cleanup(encoder); ++err_put_remote: ++ of_node_put(remote); ++err_put_port: ++ of_node_put(port); ++ ++ return ret; ++} ++ ++static void rockchip_lvds_unbind(struct device *dev, struct device *master, ++ void *data) ++{ ++ struct rockchip_lvds *lvds = dev_get_drvdata(dev); ++ ++ rockchip_lvds_encoder_disable(&lvds->encoder); ++ if (lvds->panel) ++ drm_panel_detach(lvds->panel); ++ pm_runtime_disable(dev); ++ drm_connector_cleanup(&lvds->connector); ++ drm_encoder_cleanup(&lvds->encoder); ++} ++ ++static const struct component_ops rockchip_lvds_component_ops = { ++ .bind = rockchip_lvds_bind, ++ .unbind = rockchip_lvds_unbind, ++}; ++ ++static int rockchip_lvds_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rockchip_lvds *lvds; ++ const struct of_device_id *match; ++ struct resource *res; ++ int ret; ++ ++ if (!dev->of_node) ++ return -ENODEV; ++ ++ lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); ++ if (!lvds) ++ return -ENOMEM; ++ ++ lvds->dev = dev; ++ match = of_match_node(rockchip_lvds_dt_ids, dev->of_node); ++ if (!match) ++ return -ENODEV; ++ lvds->soc_data = match->data; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ lvds->regs = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(lvds->regs)) ++ return PTR_ERR(lvds->regs); ++ ++ lvds->pclk = devm_clk_get(&pdev->dev, "pclk_lvds"); ++ if (IS_ERR(lvds->pclk)) { ++ DRM_DEV_ERROR(dev, "could not get pclk_lvds\n"); ++ return PTR_ERR(lvds->pclk); ++ } ++ ++ lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins), ++ GFP_KERNEL); ++ if (!lvds->pins) ++ return -ENOMEM; ++ ++ lvds->pins->p = devm_pinctrl_get(lvds->dev); ++ if (IS_ERR(lvds->pins->p)) { ++ DRM_DEV_ERROR(dev, "no pinctrl handle\n"); ++ devm_kfree(lvds->dev, lvds->pins); ++ lvds->pins = NULL; ++ } else { ++ lvds->pins->default_state = ++ pinctrl_lookup_state(lvds->pins->p, "lcdc"); ++ if (IS_ERR(lvds->pins->default_state)) { ++ DRM_DEV_ERROR(dev, "no default pinctrl state\n"); ++ devm_kfree(lvds->dev, lvds->pins); ++ lvds->pins = NULL; ++ } ++ } ++ ++ lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node, ++ "rockchip,grf"); ++ if (IS_ERR(lvds->grf)) { ++ DRM_DEV_ERROR(dev, "missing rockchip,grf property\n"); ++ return PTR_ERR(lvds->grf); ++ } ++ ++ dev_set_drvdata(dev, lvds); ++ ++ ret = clk_prepare(lvds->pclk); ++ if (ret < 0) { ++ DRM_DEV_ERROR(dev, "failed to prepare pclk_lvds\n"); ++ return ret; ++ } ++ ret = component_add(&pdev->dev, &rockchip_lvds_component_ops); ++ if (ret < 0) { ++ DRM_DEV_ERROR(dev, "failed to add component\n"); ++ clk_unprepare(lvds->pclk); ++ } ++ ++ return ret; ++} ++ ++static int rockchip_lvds_remove(struct platform_device *pdev) ++{ ++ struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev); ++ ++ component_del(&pdev->dev, &rockchip_lvds_component_ops); ++ clk_unprepare(lvds->pclk); ++ ++ return 0; ++} ++ ++struct platform_driver rockchip_lvds_driver = { ++ .probe = rockchip_lvds_probe, ++ .remove = rockchip_lvds_remove, ++ .driver = { ++ .name = "rockchip-lvds", ++ .of_match_table = of_match_ptr(rockchip_lvds_dt_ids), ++ }, ++}; +diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h +new file mode 100644 +index 000000000000..15810b737809 +--- /dev/null ++++ b/drivers/gpu/drm/rockchip/rockchip_lvds.h +@@ -0,0 +1,114 @@ ++/* ++ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd ++ * Author: ++ * Sandy Huang ++ * Mark Yao ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef _ROCKCHIP_LVDS_ ++#define _ROCKCHIP_LVDS_ ++ ++#define RK3288_LVDS_CH0_REG0 0x00 ++#define RK3288_LVDS_CH0_REG0_LVDS_EN BIT(7) ++#define RK3288_LVDS_CH0_REG0_TTL_EN BIT(6) ++#define RK3288_LVDS_CH0_REG0_LANECK_EN BIT(5) ++#define RK3288_LVDS_CH0_REG0_LANE4_EN BIT(4) ++#define RK3288_LVDS_CH0_REG0_LANE3_EN BIT(3) ++#define RK3288_LVDS_CH0_REG0_LANE2_EN BIT(2) ++#define RK3288_LVDS_CH0_REG0_LANE1_EN BIT(1) ++#define RK3288_LVDS_CH0_REG0_LANE0_EN BIT(0) ++ ++#define RK3288_LVDS_CH0_REG1 0x04 ++#define RK3288_LVDS_CH0_REG1_LANECK_BIAS BIT(5) ++#define RK3288_LVDS_CH0_REG1_LANE4_BIAS BIT(4) ++#define RK3288_LVDS_CH0_REG1_LANE3_BIAS BIT(3) ++#define RK3288_LVDS_CH0_REG1_LANE2_BIAS BIT(2) ++#define RK3288_LVDS_CH0_REG1_LANE1_BIAS BIT(1) ++#define RK3288_LVDS_CH0_REG1_LANE0_BIAS BIT(0) ++ ++#define RK3288_LVDS_CH0_REG2 0x08 ++#define RK3288_LVDS_CH0_REG2_RESERVE_ON BIT(7) ++#define RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE BIT(6) ++#define RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE BIT(5) ++#define RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE BIT(4) ++#define RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE BIT(3) ++#define RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE BIT(2) ++#define RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE BIT(1) ++#define RK3288_LVDS_CH0_REG2_PLL_FBDIV8 BIT(0) ++ ++#define RK3288_LVDS_CH0_REG3 0x0c ++#define RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK 0xff ++ ++#define RK3288_LVDS_CH0_REG4 0x10 ++#define RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE BIT(5) ++#define RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE BIT(4) ++#define RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE BIT(3) ++#define RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE BIT(2) ++#define RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE BIT(1) ++#define RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE BIT(0) ++ ++#define RK3288_LVDS_CH0_REG5 0x14 ++#define RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA BIT(5) ++#define RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA BIT(4) ++#define RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA BIT(3) ++#define RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA BIT(2) ++#define RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA BIT(1) ++#define RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA BIT(0) ++ ++#define RK3288_LVDS_CFG_REGC 0x30 ++#define RK3288_LVDS_CFG_REGC_PLL_ENABLE 0x00 ++#define RK3288_LVDS_CFG_REGC_PLL_DISABLE 0xff ++ ++#define RK3288_LVDS_CH0_REGD 0x34 ++#define RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK 0x1f ++ ++#define RK3288_LVDS_CH0_REG20 0x80 ++#define RK3288_LVDS_CH0_REG20_MSB 0x45 ++#define RK3288_LVDS_CH0_REG20_LSB 0x44 ++ ++#define RK3288_LVDS_CFG_REG21 0x84 ++#define RK3288_LVDS_CFG_REG21_TX_ENABLE 0x92 ++#define RK3288_LVDS_CFG_REG21_TX_DISABLE 0x00 ++#define RK3288_LVDS_CH1_OFFSET 0x100 ++ ++/* fbdiv value is split over 2 registers, with bit8 in reg2 */ ++#define RK3288_LVDS_PLL_FBDIV_REG2(_fbd) \ ++ (_fbd & BIT(8) ? RK3288_LVDS_CH0_REG2_PLL_FBDIV8 : 0) ++#define RK3288_LVDS_PLL_FBDIV_REG3(_fbd) \ ++ (_fbd & RK3288_LVDS_CH0_REG3_PLL_FBDIV_MASK) ++#define RK3288_LVDS_PLL_PREDIV_REGD(_pd) \ ++ (_pd & RK3288_LVDS_CH0_REGD_PLL_PREDIV_MASK) ++ ++#define RK3288_LVDS_SOC_CON6_SEL_VOP_LIT BIT(3) ++ ++#define LVDS_FMT_MASK (0x07 << 16) ++#define LVDS_MSB BIT(3) ++#define LVDS_DUAL BIT(4) ++#define LVDS_FMT_1 BIT(5) ++#define LVDS_TTL_EN BIT(6) ++#define LVDS_START_PHASE_RST_1 BIT(7) ++#define LVDS_DCLK_INV BIT(8) ++#define LVDS_CH0_EN BIT(11) ++#define LVDS_CH1_EN BIT(12) ++#define LVDS_PWRDN BIT(15) ++ ++#define LVDS_24BIT (0 << 1) ++#define LVDS_18BIT (1 << 1) ++#define LVDS_FORMAT_VESA (0 << 0) ++#define LVDS_FORMAT_JEIDA (1 << 0) ++ ++#define LVDS_VESA_24 0 ++#define LVDS_JEIDA_24 1 ++#define LVDS_VESA_18 2 ++#define LVDS_JEIDA_18 3 ++ ++#endif /* _ROCKCHIP_LVDS_ */ +-- +2.11.0 + diff --git a/patches.drivers/0081-drm-rockchip-Replace-dev_-with-DRM_DEV_.patch b/patches.drivers/0081-drm-rockchip-Replace-dev_-with-DRM_DEV_.patch new file mode 100644 index 0000000..a48f226 --- /dev/null +++ b/patches.drivers/0081-drm-rockchip-Replace-dev_-with-DRM_DEV_.patch @@ -0,0 +1,838 @@ +From ee4a8c61a83a33ef9384add27fefb3a53a8cb9cd Mon Sep 17 00:00:00 2001 +From: Haneen Mohammed +Date: Fri, 15 Sep 2017 02:36:03 -0600 +Subject: [PATCH 81/86] drm/rockchip: Replace dev_* with DRM_DEV_* + +Git-commit: d8dd68045c5879c40342050a476710e31c7cdfa7 +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +This patch replace instances of dev_info/err/debug with +DRM_DEV_INFO/ERROR/WARN respectively inorder to use a drm-formatted +specific log messages. Issue corrected with the help of the following +Coccinelle script: + +@r@ +@@ + +( +-dev_info ++DRM_DEV_INFO +| +-dev_err ++DRM_DEV_ERROR +| +-dev_dbg ++DRM_DEV_DEBUG +) + +Signed-off-by: Haneen Mohammed +Signed-off-by: Mark Yao +Link: https://patchwork.freedesktop.org/patch/msgid/20170915083603.GA18992@Haneen +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 26 ++++---- + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 2 +- + drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 86 ++++++++++++++----------- + drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 19 +++--- + drivers/gpu/drm/rockchip/inno_hdmi.c | 14 ++-- + drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 12 ++-- + drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 8 ++- + drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 18 ++++-- + drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 32 ++++----- + drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 +- + 10 files changed, 121 insertions(+), 98 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +index 9606121fa185..4d3f6ad0abdd 100644 +--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c ++++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +@@ -88,7 +88,7 @@ static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled) + if (!analogix_dp_psr_supported(dp->dev)) + return; + +- dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit"); ++ DRM_DEV_DEBUG(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit"); + + spin_lock_irqsave(&dp->psr_lock, flags); + if (enabled) +@@ -110,7 +110,7 @@ static void analogix_dp_psr_work(struct work_struct *work) + ret = rockchip_drm_wait_vact_end(dp->encoder.crtc, + PSR_WAIT_LINE_FLAG_TIMEOUT_MS); + if (ret) { +- dev_err(dp->dev, "line flag interrupt did not arrive\n"); ++ DRM_DEV_ERROR(dp->dev, "line flag interrupt did not arrive\n"); + return; + } + +@@ -140,13 +140,13 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data) + + ret = clk_prepare_enable(dp->pclk); + if (ret < 0) { +- dev_err(dp->dev, "failed to enable pclk %d\n", ret); ++ DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret); + return ret; + } + + ret = rockchip_dp_pre_init(dp); + if (ret < 0) { +- dev_err(dp->dev, "failed to dp pre init %d\n", ret); ++ DRM_DEV_ERROR(dp->dev, "failed to dp pre init %d\n", ret); + clk_disable_unprepare(dp->pclk); + return ret; + } +@@ -211,17 +211,17 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder) + else + val = dp->data->lcdsel_big; + +- dev_dbg(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG"); ++ DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG"); + + ret = clk_prepare_enable(dp->grfclk); + if (ret < 0) { +- dev_err(dp->dev, "failed to enable grfclk %d\n", ret); ++ DRM_DEV_ERROR(dp->dev, "failed to enable grfclk %d\n", ret); + return; + } + + ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val); + if (ret != 0) +- dev_err(dp->dev, "Could not write to GRF: %d\n", ret); ++ DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret); + + clk_disable_unprepare(dp->grfclk); + } +@@ -277,7 +277,7 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp) + + dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(dp->grf)) { +- dev_err(dev, "failed to get rockchip,grf property\n"); ++ DRM_DEV_ERROR(dev, "failed to get rockchip,grf property\n"); + return PTR_ERR(dp->grf); + } + +@@ -287,31 +287,31 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp) + } else if (PTR_ERR(dp->grfclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(dp->grfclk)) { +- dev_err(dev, "failed to get grf clock\n"); ++ DRM_DEV_ERROR(dev, "failed to get grf clock\n"); + return PTR_ERR(dp->grfclk); + } + + dp->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dp->pclk)) { +- dev_err(dev, "failed to get pclk property\n"); ++ DRM_DEV_ERROR(dev, "failed to get pclk property\n"); + return PTR_ERR(dp->pclk); + } + + dp->rst = devm_reset_control_get(dev, "dp"); + if (IS_ERR(dp->rst)) { +- dev_err(dev, "failed to get dp reset control\n"); ++ DRM_DEV_ERROR(dev, "failed to get dp reset control\n"); + return PTR_ERR(dp->rst); + } + + ret = clk_prepare_enable(dp->pclk); + if (ret < 0) { +- dev_err(dp->dev, "failed to enable pclk %d\n", ret); ++ DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret); + return ret; + } + + ret = rockchip_dp_pre_init(dp); + if (ret < 0) { +- dev_err(dp->dev, "failed to pre init %d\n", ret); ++ DRM_DEV_ERROR(dp->dev, "failed to pre init %d\n", ret); + clk_disable_unprepare(dp->pclk); + return ret; + } +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c +index b14d211f6c21..eb3042c6d1b2 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c ++++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c +@@ -323,7 +323,7 @@ int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem, + reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff; + dp->fw_version |= reg << 24; + +- dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version); ++ DRM_DEV_DEBUG(dp->dev, "firmware version: %x\n", dp->fw_version); + + return 0; + } +diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c +index 21b9737662ae..d074d260109a 100644 +--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c ++++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c +@@ -430,9 +430,9 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi) + + testdin = max_mbps_to_testdin(dsi->lane_mbps); + if (testdin < 0) { +- dev_err(dsi->dev, +- "failed to get testdin for %dmbps lane clock\n", +- dsi->lane_mbps); ++ DRM_DEV_ERROR(dsi->dev, ++ "failed to get testdin for %dmbps lane clock\n", ++ dsi->lane_mbps); + return testdin; + } + +@@ -443,7 +443,7 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi) + + ret = clk_prepare_enable(dsi->phy_cfg_clk); + if (ret) { +- dev_err(dsi->dev, "Failed to enable phy_cfg_clk\n"); ++ DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n"); + return ret; + } + +@@ -501,7 +501,7 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi) + ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, + val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US); + if (ret < 0) { +- dev_err(dsi->dev, "failed to wait for phy lock state\n"); ++ DRM_DEV_ERROR(dsi->dev, "failed to wait for phy lock state\n"); + goto phy_init_end; + } + +@@ -509,8 +509,8 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi) + val, val & STOP_STATE_CLK_LANE, 1000, + PHY_STATUS_TIMEOUT_US); + if (ret < 0) +- dev_err(dsi->dev, +- "failed to wait for phy clk lane stop state\n"); ++ DRM_DEV_ERROR(dsi->dev, ++ "failed to wait for phy clk lane stop state\n"); + + phy_init_end: + clk_disable_unprepare(dsi->phy_cfg_clk); +@@ -529,8 +529,9 @@ static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi, + + bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + if (bpp < 0) { +- dev_err(dsi->dev, "failed to get bpp for pixel format %d\n", +- dsi->format); ++ DRM_DEV_ERROR(dsi->dev, ++ "failed to get bpp for pixel format %d\n", ++ dsi->format); + return bpp; + } + +@@ -541,7 +542,8 @@ static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi, + if (tmp < max_mbps) + target_mbps = tmp; + else +- dev_err(dsi->dev, "DPHY clock frequency is out of range\n"); ++ DRM_DEV_ERROR(dsi->dev, ++ "DPHY clock frequency is out of range\n"); + } + + pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC); +@@ -582,8 +584,9 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, + struct dw_mipi_dsi *dsi = host_to_dsi(host); + + if (device->lanes > dsi->pdata->max_data_lanes) { +- dev_err(dsi->dev, "the number of data lanes(%u) is too many\n", +- device->lanes); ++ DRM_DEV_ERROR(dsi->dev, ++ "the number of data lanes(%u) is too many\n", ++ device->lanes); + return -EINVAL; + } + +@@ -632,7 +635,8 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val) + val, !(val & GEN_CMD_FULL), 1000, + CMD_PKT_STATUS_TIMEOUT_US); + if (ret < 0) { +- dev_err(dsi->dev, "failed to get available command FIFO\n"); ++ DRM_DEV_ERROR(dsi->dev, ++ "failed to get available command FIFO\n"); + return ret; + } + +@@ -643,7 +647,7 @@ static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val) + val, (val & mask) == mask, + 1000, CMD_PKT_STATUS_TIMEOUT_US); + if (ret < 0) { +- dev_err(dsi->dev, "failed to write command FIFO\n"); ++ DRM_DEV_ERROR(dsi->dev, "failed to write command FIFO\n"); + return ret; + } + +@@ -663,8 +667,9 @@ static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi, + data |= tx_buf[1] << 8; + + if (msg->tx_len > 2) { +- dev_err(dsi->dev, "too long tx buf length %zu for short write\n", +- msg->tx_len); ++ DRM_DEV_ERROR(dsi->dev, ++ "too long tx buf length %zu for short write\n", ++ msg->tx_len); + return -EINVAL; + } + +@@ -682,8 +687,9 @@ static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi, + u32 val; + + if (msg->tx_len < 3) { +- dev_err(dsi->dev, "wrong tx buf length %zu for long write\n", +- msg->tx_len); ++ DRM_DEV_ERROR(dsi->dev, ++ "wrong tx buf length %zu for long write\n", ++ msg->tx_len); + return -EINVAL; + } + +@@ -704,8 +710,8 @@ static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi, + val, !(val & GEN_PLD_W_FULL), 1000, + CMD_PKT_STATUS_TIMEOUT_US); + if (ret < 0) { +- dev_err(dsi->dev, +- "failed to get available write payload FIFO\n"); ++ DRM_DEV_ERROR(dsi->dev, ++ "failed to get available write payload FIFO\n"); + return ret; + } + } +@@ -731,8 +737,8 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host, + ret = dw_mipi_dsi_dcs_long_write(dsi, msg); + break; + default: +- dev_err(dsi->dev, "unsupported message type 0x%02x\n", +- msg->type); ++ DRM_DEV_ERROR(dsi->dev, "unsupported message type 0x%02x\n", ++ msg->type); + ret = -EINVAL; + } + +@@ -935,7 +941,7 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder) + return; + + if (clk_prepare_enable(dsi->pclk)) { +- dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__); ++ DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n"); + return; + } + +@@ -967,7 +973,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) + return; + + if (clk_prepare_enable(dsi->pclk)) { +- dev_err(dsi->dev, "%s: Failed to enable pclk\n", __func__); ++ DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n"); + return; + } + +@@ -991,7 +997,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) + */ + ret = clk_prepare_enable(dsi->grf_clk); + if (ret) { +- dev_err(dsi->dev, "Failed to enable grf_clk: %d\n", ret); ++ DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret); + return; + } + +@@ -1004,7 +1010,7 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) + + dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE); + if (drm_panel_prepare(dsi->panel)) +- dev_err(dsi->dev, "failed to prepare panel\n"); ++ DRM_DEV_ERROR(dsi->dev, "failed to prepare panel\n"); + + dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE); + drm_panel_enable(dsi->panel); +@@ -1017,7 +1023,8 @@ static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder) + val = pdata->dsi0_en_bit << 16; + + regmap_write(dsi->grf_regmap, pdata->grf_switch_reg, val); +- dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG"); ++ DRM_DEV_DEBUG(dsi->dev, ++ "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG"); + dsi->dpms_mode = DRM_MODE_DPMS_ON; + + clk_disable_unprepare(dsi->grf_clk); +@@ -1112,7 +1119,7 @@ static int dw_mipi_dsi_register(struct drm_device *drm, + ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI, NULL); + if (ret) { +- dev_err(dev, "Failed to initialize encoder with drm\n"); ++ DRM_DEV_ERROR(dev, "Failed to initialize encoder with drm\n"); + return ret; + } + +@@ -1134,7 +1141,7 @@ static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi) + + dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(dsi->grf_regmap)) { +- dev_err(dsi->dev, "Unable to get rockchip,grf\n"); ++ DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n"); + return PTR_ERR(dsi->grf_regmap); + } + +@@ -1206,14 +1213,15 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master, + dsi->pllref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(dsi->pllref_clk)) { + ret = PTR_ERR(dsi->pllref_clk); +- dev_err(dev, "Unable to get pll reference clock: %d\n", ret); ++ DRM_DEV_ERROR(dev, ++ "Unable to get pll reference clock: %d\n", ret); + return ret; + } + + dsi->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(dsi->pclk)) { + ret = PTR_ERR(dsi->pclk); +- dev_err(dev, "Unable to get pclk: %d\n", ret); ++ DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret); + return ret; + } + +@@ -1227,7 +1235,8 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master, + if (ret == -ENOENT) { + apb_rst = NULL; + } else { +- dev_err(dev, "Unable to get reset control: %d\n", ret); ++ DRM_DEV_ERROR(dev, ++ "Unable to get reset control: %d\n", ret); + return ret; + } + } +@@ -1235,7 +1244,7 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master, + if (apb_rst) { + ret = clk_prepare_enable(dsi->pclk); + if (ret) { +- dev_err(dev, "%s: Failed to enable pclk\n", __func__); ++ DRM_DEV_ERROR(dev, "Failed to enable pclk\n"); + return ret; + } + +@@ -1250,7 +1259,8 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master, + dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg"); + if (IS_ERR(dsi->phy_cfg_clk)) { + ret = PTR_ERR(dsi->phy_cfg_clk); +- dev_err(dev, "Unable to get phy_cfg_clk: %d\n", ret); ++ DRM_DEV_ERROR(dev, ++ "Unable to get phy_cfg_clk: %d\n", ret); + return ret; + } + } +@@ -1259,20 +1269,20 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master, + dsi->grf_clk = devm_clk_get(dev, "grf"); + if (IS_ERR(dsi->grf_clk)) { + ret = PTR_ERR(dsi->grf_clk); +- dev_err(dev, "Unable to get grf_clk: %d\n", ret); ++ DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret); + return ret; + } + } + + ret = clk_prepare_enable(dsi->pllref_clk); + if (ret) { +- dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__); ++ DRM_DEV_ERROR(dev, "Failed to enable pllref_clk\n"); + return ret; + } + + ret = dw_mipi_dsi_register(drm, dsi); + if (ret) { +- dev_err(dev, "Failed to register mipi_dsi: %d\n", ret); ++ DRM_DEV_ERROR(dev, "Failed to register mipi_dsi: %d\n", ret); + goto err_pllref; + } + +@@ -1282,7 +1292,7 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master, + dsi->dsi_host.dev = dev; + ret = mipi_dsi_host_register(&dsi->dsi_host); + if (ret) { +- dev_err(dev, "Failed to register MIPI host: %d\n", ret); ++ DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret); + goto err_cleanup; + } + +diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +index 2b9cb47114c4..0eea9fce35ac 100644 +--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c ++++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +@@ -168,7 +168,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + + hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(hdmi->regmap)) { +- dev_err(hdmi->dev, "Unable to get rockchip,grf\n"); ++ DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n"); + return PTR_ERR(hdmi->regmap); + } + +@@ -178,7 +178,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + } else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(hdmi->vpll_clk)) { +- dev_err(hdmi->dev, "failed to get grf clock\n"); ++ DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n"); + return PTR_ERR(hdmi->vpll_clk); + } + +@@ -188,13 +188,14 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) + } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(hdmi->grf_clk)) { +- dev_err(hdmi->dev, "failed to get grf clock\n"); ++ DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n"); + return PTR_ERR(hdmi->grf_clk); + } + + ret = clk_prepare_enable(hdmi->vpll_clk); + if (ret) { +- dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret); ++ DRM_DEV_ERROR(hdmi->dev, ++ "Failed to enable HDMI vpll: %d\n", ret); + return ret; + } + +@@ -259,17 +260,17 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) + + ret = clk_prepare_enable(hdmi->grf_clk); + if (ret < 0) { +- dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret); ++ DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret); + return; + } + + ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val); + if (ret != 0) +- dev_err(hdmi->dev, "Could not write to GRF: %d\n", ret); ++ DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret); + + clk_disable_unprepare(hdmi->grf_clk); +- dev_dbg(hdmi->dev, "vop %s output to hdmi\n", +- ret ? "LIT" : "BIG"); ++ DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n", ++ ret ? "LIT" : "BIG"); + } + + static int +@@ -368,7 +369,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, + + ret = rockchip_hdmi_parse_dt(hdmi); + if (ret) { +- dev_err(hdmi->dev, "Unable to parse OF data\n"); ++ DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n"); + return ret; + } + +diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c +index f3bfe253b279..e55312cf1b50 100644 +--- a/drivers/gpu/drm/rockchip/inno_hdmi.c ++++ b/drivers/gpu/drm/rockchip/inno_hdmi.c +@@ -224,7 +224,7 @@ static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode) + break; + + default: +- dev_err(hdmi->dev, "Unknown power mode %d\n", mode); ++ DRM_DEV_ERROR(hdmi->dev, "Unknown power mode %d\n", mode); + } + } + +@@ -743,8 +743,9 @@ static int inno_hdmi_i2c_xfer(struct i2c_adapter *adap, + hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, m_INT_EDID_READY); + + for (i = 0; i < num; i++) { +- dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n", +- i + 1, num, msgs[i].len, msgs[i].flags); ++ DRM_DEV_DEBUG(hdmi->dev, ++ "xfer: num: %d/%d, len: %d, flags: %#x\n", ++ i + 1, num, msgs[i].len, msgs[i].flags); + + if (msgs[i].flags & I2C_M_RD) + ret = inno_hdmi_i2c_read(hdmi, &msgs[i]); +@@ -807,7 +808,7 @@ static struct i2c_adapter *inno_hdmi_i2c_adapter(struct inno_hdmi *hdmi) + + hdmi->i2c = i2c; + +- dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name); ++ DRM_DEV_INFO(hdmi->dev, "registered %s I2C bus driver\n", adap->name); + + return adap; + } +@@ -839,13 +840,14 @@ static int inno_hdmi_bind(struct device *dev, struct device *master, + + hdmi->pclk = devm_clk_get(hdmi->dev, "pclk"); + if (IS_ERR(hdmi->pclk)) { +- dev_err(hdmi->dev, "Unable to get HDMI pclk clk\n"); ++ DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI pclk clk\n"); + return PTR_ERR(hdmi->pclk); + } + + ret = clk_prepare_enable(hdmi->pclk); + if (ret) { +- dev_err(hdmi->dev, "Cannot enable HDMI pclk clock: %d\n", ret); ++ DRM_DEV_ERROR(hdmi->dev, ++ "Cannot enable HDMI pclk clock: %d\n", ret); + return ret; + } + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +index c33dbd20f962..76d63de5921d 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +@@ -58,7 +58,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, + + ret = iommu_attach_device(private->domain, dev); + if (ret) { +- dev_err(dev, "Failed to attach iommu device\n"); ++ DRM_DEV_ERROR(dev, "Failed to attach iommu device\n"); + return ret; + } + +@@ -373,8 +373,9 @@ static int rockchip_drm_platform_of_probe(struct device *dev) + + iommu = of_parse_phandle(port->parent, "iommus", 0); + if (!iommu || !of_device_is_available(iommu->parent)) { +- dev_dbg(dev, "no iommu attached for %s, using non-iommu buffers\n", +- port->parent->full_name); ++ DRM_DEV_DEBUG(dev, ++ "no iommu attached for %pOF, using non-iommu buffers\n", ++ port->parent); + /* + * if there is a crtc not support iommu, force set all + * crtc use non-iommu buffer. +@@ -389,12 +390,13 @@ static int rockchip_drm_platform_of_probe(struct device *dev) + } + + if (i == 0) { +- dev_err(dev, "missing 'ports' property\n"); ++ DRM_DEV_ERROR(dev, "missing 'ports' property\n"); + return -ENODEV; + } + + if (!found) { +- dev_err(dev, "No available vop found for display-subsystem.\n"); ++ DRM_DEV_ERROR(dev, ++ "No available vop found for display-subsystem.\n"); + return -ENODEV; + } + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +index 83671d0a609e..69904c1abe9f 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +@@ -100,8 +100,9 @@ rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cm + ret = drm_framebuffer_init(dev, &rockchip_fb->fb, + &rockchip_drm_fb_funcs); + if (ret) { +- dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", +- ret); ++ DRM_DEV_ERROR(dev->dev, ++ "Failed to initialize framebuffer: %d\n", ++ ret); + kfree(rockchip_fb); + return ERR_PTR(ret); + } +@@ -134,7 +135,8 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, + + obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]); + if (!obj) { +- dev_err(dev->dev, "Failed to lookup GEM object\n"); ++ DRM_DEV_ERROR(dev->dev, ++ "Failed to lookup GEM object\n"); + ret = -ENXIO; + goto err_gem_object_unreference; + } +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +index 724579ebf947..e6650553f5d6 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +@@ -76,7 +76,7 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, + + fbi = drm_fb_helper_alloc_fbi(helper); + if (IS_ERR(fbi)) { +- dev_err(dev->dev, "Failed to create framebuffer info.\n"); ++ DRM_DEV_ERROR(dev->dev, "Failed to create framebuffer info.\n"); + ret = PTR_ERR(fbi); + goto out; + } +@@ -84,7 +84,8 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, + helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, + private->fbdev_bo); + if (IS_ERR(helper->fb)) { +- dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); ++ DRM_DEV_ERROR(dev->dev, ++ "Failed to allocate DRM framebuffer.\n"); + ret = PTR_ERR(helper->fb); + goto out; + } +@@ -138,21 +139,24 @@ int rockchip_drm_fbdev_init(struct drm_device *dev) + + ret = drm_fb_helper_init(dev, helper, ROCKCHIP_MAX_CONNECTOR); + if (ret < 0) { +- dev_err(dev->dev, "Failed to initialize drm fb helper - %d.\n", +- ret); ++ DRM_DEV_ERROR(dev->dev, ++ "Failed to initialize drm fb helper - %d.\n", ++ ret); + return ret; + } + + ret = drm_fb_helper_single_add_all_connectors(helper); + if (ret < 0) { +- dev_err(dev->dev, "Failed to add connectors - %d.\n", ret); ++ DRM_DEV_ERROR(dev->dev, ++ "Failed to add connectors - %d.\n", ret); + goto err_drm_fb_helper_fini; + } + + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); + if (ret < 0) { +- dev_err(dev->dev, "Failed to set initial hw config - %d.\n", +- ret); ++ DRM_DEV_ERROR(dev->dev, ++ "Failed to set initial hw config - %d.\n", ++ ret); + goto err_drm_fb_helper_fini; + } + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +index 41bcd3c0faef..ba164295578f 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +@@ -160,7 +160,7 @@ static void vop_reg_set(struct vop *vop, const struct vop_reg *reg, + int offset, mask, shift; + + if (!reg || !reg->mask) { +- dev_dbg(vop->dev, "Warning: not support %s\n", reg_name); ++ DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name); + return; + } + +@@ -499,7 +499,7 @@ static int vop_enable(struct drm_crtc *crtc) + + ret = pm_runtime_get_sync(vop->dev); + if (ret < 0) { +- dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); ++ DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); + return ret; + } + +@@ -523,7 +523,8 @@ static int vop_enable(struct drm_crtc *crtc) + */ + ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev); + if (ret) { +- dev_err(vop->dev, "failed to attach dma mapping, %d\n", ret); ++ DRM_DEV_ERROR(vop->dev, ++ "failed to attach dma mapping, %d\n", ret); + goto err_disable_aclk; + } + +@@ -1359,42 +1360,42 @@ static int vop_initial(struct vop *vop) + + vop->hclk = devm_clk_get(vop->dev, "hclk_vop"); + if (IS_ERR(vop->hclk)) { +- dev_err(vop->dev, "failed to get hclk source\n"); ++ DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n"); + return PTR_ERR(vop->hclk); + } + vop->aclk = devm_clk_get(vop->dev, "aclk_vop"); + if (IS_ERR(vop->aclk)) { +- dev_err(vop->dev, "failed to get aclk source\n"); ++ DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n"); + return PTR_ERR(vop->aclk); + } + vop->dclk = devm_clk_get(vop->dev, "dclk_vop"); + if (IS_ERR(vop->dclk)) { +- dev_err(vop->dev, "failed to get dclk source\n"); ++ DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n"); + return PTR_ERR(vop->dclk); + } + + ret = pm_runtime_get_sync(vop->dev); + if (ret < 0) { +- dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); ++ DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret); + return ret; + } + + ret = clk_prepare(vop->dclk); + if (ret < 0) { +- dev_err(vop->dev, "failed to prepare dclk\n"); ++ DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n"); + goto err_put_pm_runtime; + } + + /* Enable both the hclk and aclk to setup the vop */ + ret = clk_prepare_enable(vop->hclk); + if (ret < 0) { +- dev_err(vop->dev, "failed to prepare/enable hclk\n"); ++ DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n"); + goto err_unprepare_dclk; + } + + ret = clk_prepare_enable(vop->aclk); + if (ret < 0) { +- dev_err(vop->dev, "failed to prepare/enable aclk\n"); ++ DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n"); + goto err_disable_hclk; + } + +@@ -1403,7 +1404,7 @@ static int vop_initial(struct vop *vop) + */ + ahb_rst = devm_reset_control_get(vop->dev, "ahb"); + if (IS_ERR(ahb_rst)) { +- dev_err(vop->dev, "failed to get ahb reset\n"); ++ DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n"); + ret = PTR_ERR(ahb_rst); + goto err_disable_aclk; + } +@@ -1432,7 +1433,7 @@ static int vop_initial(struct vop *vop) + */ + vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk"); + if (IS_ERR(vop->dclk_rst)) { +- dev_err(vop->dev, "failed to get dclk reset\n"); ++ DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n"); + ret = PTR_ERR(vop->dclk_rst); + goto err_disable_aclk; + } +@@ -1509,7 +1510,7 @@ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout) + vop_line_flag_irq_disable(vop); + + if (jiffies_left == 0) { +- dev_err(vop->dev, "Timeout waiting for IRQ\n"); ++ DRM_DEV_ERROR(vop->dev, "Timeout waiting for IRQ\n"); + return -ETIMEDOUT; + } + +@@ -1556,7 +1557,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data) + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { +- dev_err(dev, "cannot find irq for vop\n"); ++ DRM_DEV_ERROR(dev, "cannot find irq for vop\n"); + return irq; + } + vop->irq = (unsigned int)irq; +@@ -1582,7 +1583,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data) + + ret = vop_initial(vop); + if (ret < 0) { +- dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret); ++ DRM_DEV_ERROR(&pdev->dev, ++ "cannot initial vop dev - err %d\n", ret); + goto err_disable_pm_runtime; + } + +diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +index 94de7b9f6fde..4a39049e901a 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c ++++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +@@ -533,7 +533,7 @@ static int vop_probe(struct platform_device *pdev) + struct device *dev = &pdev->dev; + + if (!dev->of_node) { +- dev_err(dev, "can't find vop devices\n"); ++ DRM_DEV_ERROR(dev, "can't find vop devices\n"); + return -ENODEV; + } + +-- +2.11.0 + diff --git a/patches.drivers/0082-drm-rockchip-Fix-uninitialized-use-of-ret.patch b/patches.drivers/0082-drm-rockchip-Fix-uninitialized-use-of-ret.patch new file mode 100644 index 0000000..073509b --- /dev/null +++ b/patches.drivers/0082-drm-rockchip-Fix-uninitialized-use-of-ret.patch @@ -0,0 +1,67 @@ +From aa1fe742927207f22222bbdfc167eb996be62832 Mon Sep 17 00:00:00 2001 +From: Sean Paul +Date: Wed, 20 Sep 2017 17:13:56 -0700 +Subject: [PATCH 82/86] drm/rockchip: Fix uninitialized use of ret + +Git-commit: 6bf2e0324b9376512b0b9bf5c5c4b383afd419ec +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +If there are no children for lvds, ret is used uninitialized. This patch +initializes ret and returns an error if the port has no children. + +Fixes: 34cc0aa25456 ("drm/rockchip: Add support for Rockchip Soc LVDS") +Cc: Mark Yao +Cc: Heiko Stuebner +Cc: Sandy Huang +Cc: dri-devel@lists.freedesktop.org +Cc: linux-arm-kernel@lists.infradead.org +Cc: linux-rockchip@lists.infradead.org +Reviewed-by: Mark Yao +Signed-off-by: Sean Paul +Link: https://patchwork.freedesktop.org/patch/msgid/20170921001408.1839-1-seanpaul@chromium.org +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_lvds.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c +index c5fbe533796c..84911bdc27d1 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c ++++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c +@@ -346,7 +346,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, + struct drm_connector *connector; + struct device_node *remote = NULL; + struct device_node *port, *endpoint; +- int ret; ++ int ret = 0, child_count = 0; + const char *name; + u32 endpoint_id; + +@@ -358,15 +358,20 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master, + return -EINVAL; + } + for_each_child_of_node(port, endpoint) { ++ child_count++; + of_property_read_u32(endpoint, "reg", &endpoint_id); + ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id, + &lvds->panel, &lvds->bridge); + if (!ret) + break; + } +- if (ret) { ++ if (!child_count) { ++ DRM_DEV_ERROR(dev, "lvds port does not have any children\n"); ++ ret = -EINVAL; ++ goto err_put_port; ++ } else if (ret) { + DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n"); +- ret = -EPROBE_DEFER; ++ ret = -EPROBE_DEFER; + goto err_put_port; + } + if (lvds->panel) +-- +2.11.0 + diff --git a/patches.drivers/0083-drm-rockchip-Cocci-spatch-vma_pages.patch b/patches.drivers/0083-drm-rockchip-Cocci-spatch-vma_pages.patch new file mode 100644 index 0000000..37f624a --- /dev/null +++ b/patches.drivers/0083-drm-rockchip-Cocci-spatch-vma_pages.patch @@ -0,0 +1,37 @@ +From f402e48f804260db5771c7ccf20c104cd5cd521c Mon Sep 17 00:00:00 2001 +From: Thomas Meyer +Date: Thu, 21 Sep 2017 00:29:36 +0200 +Subject: [PATCH 83/86] drm/rockchip: Cocci spatch "vma_pages" + +Git-commit: f2a44dd02329707514af16fe0904a78604a97c0b +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +Use vma_pages function on vma object instead of explicit computation. +Found by coccinelle spatch "api/vma_pages.cocci" + +Signed-off-by: Thomas Meyer +Signed-off-by: Mark Yao +Link: https://patchwork.freedesktop.org/patch/msgid/1505946334393-988165015-7-diffsplit-thomas@m3y3r.de +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +index 1869c8bb76c8..1d9655576b6e 100644 +--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c ++++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +@@ -220,7 +220,7 @@ static int rockchip_drm_gem_object_mmap_iommu(struct drm_gem_object *obj, + { + struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); + unsigned int i, count = obj->size >> PAGE_SHIFT; +- unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; ++ unsigned long user_count = vma_pages(vma); + unsigned long uaddr = vma->vm_start; + unsigned long offset = vma->vm_pgoff; + unsigned long end = user_count + offset; +-- +2.11.0 + diff --git a/patches.drivers/0084-drm-rockchip-Rely-on-the-default-best_encoder-behavi.patch b/patches.drivers/0084-drm-rockchip-Rely-on-the-default-best_encoder-behavi.patch new file mode 100644 index 0000000..e532325 --- /dev/null +++ b/patches.drivers/0084-drm-rockchip-Rely-on-the-default-best_encoder-behavi.patch @@ -0,0 +1,53 @@ +From 47a9c5301854ef78da56e1cbb49626a9757ee5b9 Mon Sep 17 00:00:00 2001 +From: Haneen Mohammed +Date: Wed, 27 Sep 2017 12:23:17 -0600 +Subject: [PATCH 84/86] drm/rockchip: Rely on the default best_encoder() + behavior + +Git-commit: 7f909d9c74f3894ba8bca426ead8ccbe7282d950 +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +Since the output has 1:1 relationship between connectors and encoders, +and the driver is relying on the atomic helpers, remove the custom +best_encoder() and let the core call drm_atomic_helper_best_encoder(). + +Signed-off-by: Haneen Mohammed +Signed-off-by: Sean Paul +Link: https://patchwork.freedesktop.org/patch/msgid/20170927182317.GA8249@Haneen +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/cdn-dp-core.c | 9 --------- + 1 file changed, 9 deletions(-) + +diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c +index 9b0b0588bbed..4588b9e107d2 100644 +--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c ++++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c +@@ -288,14 +288,6 @@ static int cdn_dp_connector_get_modes(struct drm_connector *connector) + return ret; + } + +-static struct drm_encoder * +-cdn_dp_connector_best_encoder(struct drm_connector *connector) +-{ +- struct cdn_dp_device *dp = connector_to_dp(connector); +- +- return &dp->encoder; +-} +- + static int cdn_dp_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) + { +@@ -347,7 +339,6 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector, + + static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = { + .get_modes = cdn_dp_connector_get_modes, +- .best_encoder = cdn_dp_connector_best_encoder, + .mode_valid = cdn_dp_connector_mode_valid, + }; + +-- +2.11.0 + diff --git a/patches.drivers/0085-drm-rockchip-add-PINCTRL-dependency-for-LVDS.patch b/patches.drivers/0085-drm-rockchip-add-PINCTRL-dependency-for-LVDS.patch new file mode 100644 index 0000000..3a91745 --- /dev/null +++ b/patches.drivers/0085-drm-rockchip-add-PINCTRL-dependency-for-LVDS.patch @@ -0,0 +1,43 @@ +From 2dcdf86d4979f48123feeb3d264d43fb43a12aca Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Thu, 5 Oct 2017 14:09:39 +0200 +Subject: [PATCH 85/86] drm/rockchip: add PINCTRL dependency for LVDS + +Git-commit: 479b9db27453aea42bfad92e9466135ae063f326 +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +The new driver fails to build when CONFIG_PINCTRL is disabled: + +drivers/gpu/drm/rockchip/rockchip_lvds.c: In function 'rockchip_lvds_grf_config': +drivers/gpu/drm/rockchip/rockchip_lvds.c:229:39: error: dereferencing pointer to incomplete type 'struct dev_pin_info' + if (lvds->pins && !IS_ERR(lvds->pins->default_state)) + +This adds the respective Kconfig dependency. + +Fixes: 34cc0aa25456 ("drm/rockchip: Add support for Rockchip Soc LVDS") +Signed-off-by: Arnd Bergmann +Acked-by: Mark Yao +Signed-off-by: Mark Yao +Link: https://patchwork.freedesktop.org/patch/msgid/20171005120957.485433-1-arnd@arndb.de +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig +index 0c31f0a27b9c..3c70c6224bd2 100644 +--- a/drivers/gpu/drm/rockchip/Kconfig ++++ b/drivers/gpu/drm/rockchip/Kconfig +@@ -60,6 +60,7 @@ config ROCKCHIP_INNO_HDMI + config ROCKCHIP_LVDS + bool "Rockchip LVDS support" + depends on DRM_ROCKCHIP ++ depends on PINCTRL + help + Choose this option to enable support for Rockchip LVDS controllers. + Rockchip rk3288 SoC has LVDS TX Controller can be used, and it +-- +2.11.0 + diff --git a/patches.drivers/0086-drm-rockchip-add-CONFIG_OF-dependency-for-lvds.patch b/patches.drivers/0086-drm-rockchip-add-CONFIG_OF-dependency-for-lvds.patch new file mode 100644 index 0000000..35364f3 --- /dev/null +++ b/patches.drivers/0086-drm-rockchip-add-CONFIG_OF-dependency-for-lvds.patch @@ -0,0 +1,45 @@ +From 20724de48b0582da4ae2f4fb77878ace25f933ad Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Mon, 6 Nov 2017 14:58:43 +0100 +Subject: [PATCH 86/86] drm/rockchip: add CONFIG_OF dependency for lvds + +Git-commit: 30cfcf01665f3c8d8dc3ebb3dfb1a8248f53404a +Patch-mainline: Queued +Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git +References: fate#323912 + +Build-testing on randconfig kernels revealed a dependency in the +newly added lvds sub-driver: + +drivers/gpu/drm/rockchip/rockchip_lvds.c: In function 'rockchip_lvds_bind': +drivers/gpu/drm/rockchip/rockchip_lvds.c:380:24: error: 'struct drm_bridge' has no member named 'of_node' + remote = lvds->bridge->of_node; + +We could work around that in the code, adding a Kconfig dependency +seems easier. + +Fixes: 34cc0aa25456 ("drm/rockchip: Add support for Rockchip Soc LVDS") +Signed-off-by: Arnd Bergmann +Signed-off-by: Sean Paul +Link: https://patchwork.freedesktop.org/patch/msgid/20171106135852.1355487-1-arnd@arndb.de +Signed-off-by: Mian Yousaf Kaukab +--- + drivers/gpu/drm/rockchip/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig +index 3c70c6224bd2..0ccc76217ee4 100644 +--- a/drivers/gpu/drm/rockchip/Kconfig ++++ b/drivers/gpu/drm/rockchip/Kconfig +@@ -60,7 +60,7 @@ config ROCKCHIP_INNO_HDMI + config ROCKCHIP_LVDS + bool "Rockchip LVDS support" + depends on DRM_ROCKCHIP +- depends on PINCTRL ++ depends on PINCTRL && OF + help + Choose this option to enable support for Rockchip LVDS controllers. + Rockchip rk3288 SoC has LVDS TX Controller can be used, and it +-- +2.11.0 + diff --git a/series.conf b/series.conf index 6784117..ab91acf 100644 --- a/series.conf +++ b/series.conf @@ -1401,6 +1401,57 @@ patches.arch/arm64-drivers-char-kmem-disable-on-arm64.patch patches.drivers/0001-arm64-Ensure-the-instruction-emulation-is-ready-for-.patch + # rk3399 + patches.drivers/0001-PM-devfreq-rk3399_dmc-fix-error-return-code-in-rk339.patch + patches.drivers/0002-iio-adc-rockchip_saradc-add-NULL-check-on-of_match_d.patch + patches.drivers/0003-iio-adc-rockchip_saradc-explicitly-request-exclusive.patch + patches.drivers/0004-clocksource-drivers-rockchip-pr_err-strings-should-e.patch + patches.drivers/0005-pinctrl-rockchip-remove-unneeded-void-casts-in-of_ma.patch + patches.drivers/0006-pinctrl-rockchip-Add-iomux-route-switching-support.patch + patches.drivers/0007-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch + patches.drivers/0008-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch + patches.drivers/0009-pinctrl-rockchip-Add-iomux-route-switching-support-f.patch + patches.drivers/0010-pinctrl-rockchip-Use-common-interface-for-recalced-i.patch + patches.drivers/0011-pinctrl-rockchip-Fix-the-rk3399-gpio0-and-gpio1-bank.patch + patches.drivers/0015-spi-rockchip-fix-error-handling-when-probe.patch + patches.drivers/0016-spi-rockchip-Set-GPIO_SS-flag-to-enable-Slave-Select.patch + patches.drivers/0017-spi-rockchip-Disable-Runtime-PM-when-chip-select-is-.patch + patches.drivers/0018-spi-rockchip-Slightly-rework-return-value-handling.patch + patches.drivers/0019-spi-rockchip-Fix-clock-handling-in-remove.patch + patches.drivers/0020-spi-rockchip-Fix-clock-handling-in-suspend-resume.patch + patches.drivers/0021-spi-rockchip-configure-CTRLR1-according-to-size-and-.patch + patches.drivers/0022-clk-rockchip-add-ids-for-camera-on-rk3399.patch + patches.drivers/0023-clk-rockchip-add-dt-binding-header-for-rk3128.patch + patches.drivers/0024-clk-rockchip-add-ids-for-rk3399-testclks-used-for-ca.patch + patches.drivers/0025-clk-fractional-divider-allow-overriding-of-approxima.patch + patches.drivers/0026-clk-rockchip-add-special-approximation-to-fix-up-fra.patch + patches.drivers/0027-clk-rockchip-Mark-rockchip_fractional_approximation-.patch + patches.drivers/0028-clk-rockchip-Remove-superfluous-error-message-in-roc.patch + patches.drivers/0029-phy-qcom-usb-Remove-unused-ulpi-phy-header.patch + patches.drivers/0030-phy-Move-ULPI-phy-header-out-of-drivers-to-include-p.patch + patches.drivers/0031-phy-Group-vendor-specific-phy-drivers.patch + patches.drivers/0032-phy-rockchip-inno-usb2-add-a-delay-after-phy-resume.patch + patches.drivers/0033-phy-rockchip-inno-usb2-increase-otg-sm-work-first-sc.patch + patches.drivers/0034-phy-rockchip-inno-usb2-add-one-phy-comprises-with-tw.patch + patches.drivers/0035-phy-rockchip-inno-usb2-add-support-of-usb2-phy-for-r.patch + patches.drivers/0036-phy-rockchip-inno-usb2-Replace-the-extcon-API.patch + patches.drivers/0037-phy-rockchip-inno-usb2-add-support-for-rockchip-usbg.patch + patches.drivers/0038-phy-rockchip-inno-usb2-add-support-for-otg-mux-inter.patch + patches.drivers/0039-phy-rockchip-inno-usb2-add-support-of-usb2-phy-for-r.patch + patches.drivers/0040-phy-rockchip-typec-remove-unused-dfp-variable.patch + patches.drivers/0041-phy-rockchip-pcie-Reconstruct-driver-to-support-per-.patch + patches.drivers/0042-phy-rockchip-typec-Set-the-AUX-channel-flip-state-ea.patch + patches.drivers/0043-phy-rockchip-typec-Don-t-set-the-aux-voltage-swing-t.patch + patches.drivers/0044-phy-rockchip-typec-Check-for-errors-from-tcphy_phy_i.patch + patches.drivers/0045-phy-rockchip-typec-Avoid-magic-numbers-add-delays-in.patch + patches.drivers/0046-phy-rockchip-typec-Do-the-calibration-more-correctly.patch + patches.drivers/0047-pwm-rockchip-Add-APB-and-function-both-clocks-suppor.patch + patches.drivers/0048-pwm-rockchip-Remove-the-judge-from-return-value-of-p.patch + patches.drivers/0049-pwm-rockchip-Use-pwm_apply-instead-of-pwm_enable.patch + patches.drivers/0050-pwm-rockchip-Move-the-configuration-of-polarity.patch + patches.drivers/0051-pwm-rockchip-Use-same-PWM-ops-for-each-IP.patch + patches.drivers/0052-mfd-rk808-Fix-up-the-chip-id-get-failed.patch + ######################################################## # S/390 ######################################################## @@ -2246,6 +2297,41 @@ patches.drivers/gpu-host1x-Don-t-fail-on-NULL-bo-physical-address.patch patches.drivers/gpu-host1x-Free-the-IOMMU-domain-when-there-is-no-de.patch patches.drivers/gpu-ipu-v3-add-DRM-dependency.patch + # rk3399 + patches.drivers/0053-drm-rockchip-Set-line-flag-config-register-in-vop_cr.patch + patches.drivers/0054-drm-rockchip-analogix_dp-Remove-unused-check-and-var.patch + patches.drivers/0055-drm-rockchip-use-drm_for_each_connector_iter.patch + patches.drivers/0056-drm-rockchip-gem-add-the-lacks-lock-and-trivial-chan.patch + patches.drivers/0057-drm-rockchip-Remove-unnecessary-NULL-check.patch + patches.drivers/0058-drm-rockchip-dw_hdmi-add-RK3399-HDMI-support.patch + patches.drivers/0059-drm-rockchip-dw_hdmi-introduce-the-VPLL-clock-settin.patch + patches.drivers/0060-drm-rockchip-dw_hdmi-introduce-the-pclk-for-grf.patch + patches.drivers/0061-drm-rockchip-Drop-drm_vblank_cleanup.patch + patches.drivers/0062-drm-rockchip-fix-NULL-check-on-devm_kzalloc-return-v.patch + patches.drivers/0063-drm-rockchip-Use-for_each_oldnew_plane_in_state-in-v.patch + patches.drivers/0064-drm-rockchip-Use-.dumb_map_offset-and-.dumb_destroy-.patch + patches.drivers/0065-drm-rockchip-vop-initialize-registers-directly.patch + patches.drivers/0066-drm-rockchip-vop-move-write_relaxed-flags-to-vop-reg.patch + patches.drivers/0067-drm-rockchip-vop-move-line_flag_num-to-interrupt-reg.patch + patches.drivers/0068-drm-rockchip-vop-group-vop-registers.patch + patches.drivers/0069-drm-rockchip-vop-add-a-series-of-vop-support.patch + patches.drivers/0070-drm-rockchip-vop-rk3328-fix-overlay-abnormal.patch + patches.drivers/0071-drm-rockchip-vop-fix-iommu-page-fault-when-resume.patch + patches.drivers/0072-drm-rockchip-vop-fix-NV12-video-display-error.patch + patches.drivers/0073-drm-rockchip-vop-round_up-pitches-to-word-align.patch + patches.drivers/0074-drm-rockchip-vop-report-error-when-check-resource-er.patch + patches.drivers/0075-drm-rockchip-vop-no-need-wait-vblank-on-crtc-enable.patch + patches.drivers/0076-drm-rockchip-fix-race-with-kms-hotplug-and-fbdev.patch + patches.drivers/0077-drm-rockchip-make-drm_connector_funcs-structures-con.patch + patches.drivers/0078-drm-rockchip-Fix-suspend-crash-when-drm-is-not-bound.patch + patches.drivers/0079-drm-rockchip-switch-to-drm_-_get-drm_-_put-helpers.patch + patches.drivers/0080-drm-rockchip-Add-support-for-Rockchip-Soc-LVDS.patch + patches.drivers/0081-drm-rockchip-Replace-dev_-with-DRM_DEV_.patch + patches.drivers/0082-drm-rockchip-Fix-uninitialized-use-of-ret.patch + patches.drivers/0083-drm-rockchip-Cocci-spatch-vma_pages.patch + patches.drivers/0084-drm-rockchip-Rely-on-the-default-best_encoder-behavi.patch + patches.drivers/0085-drm-rockchip-add-PINCTRL-dependency-for-LVDS.patch + patches.drivers/0086-drm-rockchip-add-CONFIG_OF-dependency-for-lvds.patch patches.drivers/video-console-Add-dmi-quirk-table-for-x86-systems-wh patches.drivers/video-console-Add-new-BIOS-date-for-GPD-pocket-to-dm @@ -2292,6 +2378,8 @@ patches.suse/0015-mem-memcg-cache-rightmost-node.patch patches.suse/0016-block-cfq-cache-rightmost-rb_node.patch + patches.drivers/0001-lib-mpi-call-cond_resched-from-mpi_powm-loop.patch + ######################################################## # sorted patches ######################################################## @@ -5127,6 +5215,9 @@ patches.drivers/mfd-Add-support-for-Cherry-Trail-Dollar-Cove-TI-PMIC patches.drivers/ACPI-PMIC-Add-opregion-driver-for-Intel-Dollar-Cove- + patches.drivers/0001-PCI-rockchip-Factor-out-rockchip_pcie_get_phys.patch + patches.drivers/0002-PCI-rockchip-Add-per-lane-PHY-support.patch + ######################################################## # sysfs / driver core ######################################################## @@ -5716,6 +5807,11 @@ # bsc#1061067 patches.drivers/iommu-vt-d-avoid-calling-virt_to_phys-on-null-pointer + # rk3399 + patches.drivers/0012-iommu-rockchip-Enable-Rockchip-IOMMU-on-ARM64.patch + patches.drivers/0013-iommu-rockchip-add-multi-irqs-support.patch + patches.drivers/0014-iommu-rockchip-ignore-isp-mmu-reset-operation.patch + ######################################################## # KVM patches ######################################################## diff --git a/supported.conf b/supported.conf index f8dbe70..3202ab9 100644 --- a/supported.conf +++ b/supported.conf @@ -367,6 +367,7 @@ +external drivers/char/uv_mmtimer # SGI +base drivers/char/virtio_console # fate#303913 drivers/char/xillybus/* + drivers/clk/clk-rk808 - drivers/clk/* drivers/cpufreq/acpi-cpufreq # x86 cpufreq driver used by all, if no specific one like intel_pstate - drivers/cpufreq/arm_big_little_dt @@ -505,6 +506,8 @@ - drivers/gpu/drm/amd/amdkfd/amdkfd drivers/gpu/drm/ast/ast # fate#314487 drivers/gpu/drm/bochs/bochs-drm # bsc#969091 + drivers/gpu/drm/bridge/analogix/analogix_dp + drivers/gpu/drm/bridge/synopsys/dw-hdmi - drivers/gpu/drm/bridge/* drivers/gpu/drm/cirrus/cirrus # fate#314494 drivers/gpu/drm/drm # DRM core @@ -525,6 +528,7 @@ drivers/gpu/drm/qxl/qxl # fate#316469 drivers/gpu/drm/r128/r128 # ATI Rage 128 drivers/gpu/drm/radeon/radeon # ATI Radeon + drivers/gpu/drm/rockchip/rockchipdrm - drivers/gpu/drm/panel/* - drivers/gpu/drm/savage/savage - drivers/gpu/drm/sis/sis # SIS 300/630/540 @@ -1071,6 +1075,7 @@ - drivers/mfd/menf21bmc drivers/mfd/mfd-core - drivers/mfd/qcom_rpm + drivers/mfd/rk808 drivers/mfd/rtsx_pci drivers/mfd/rtsx_usb - drivers/mfd/ti_am335x_tscadc @@ -1509,6 +1514,18 @@ drivers/pci/pcie/aer/aer_inject # fate #306815 drivers/pci/xen-pcifront +external drivers/pci/host/pci-hyperv +- drivers/phy/allwinner/phy-sun4i-usb.ko +- drivers/phy/allwinner/phy-sun9i-usb.ko +- drivers/phy/amlogic/phy-meson8b-usb2.ko +- drivers/phy/broadcom/phy-bcm-ns-usb2.ko +- drivers/phy/broadcom/phy-bcm-ns-usb3.ko +- drivers/phy/broadcom/phy-bcm-ns2-pcie.ko +- drivers/phy/broadcom/phy-brcm-sata.ko +- drivers/phy/hisilicon/phy-hi6220-usb.ko +- drivers/phy/qualcomm/phy-qcom-qmp.ko +- drivers/phy/qualcomm/phy-qcom-qusb2.ko +- drivers/phy/qualcomm/phy-qcom-usb-hs.ko +- drivers/phy/qualcomm/phy-qcom-usb-hsic.ko drivers/phy/phy-xgene - drivers/phy/phy-bcm-kona-usb2 - drivers/phy/phy-berlin-sata @@ -1523,12 +1540,12 @@ drivers/phy/phy-qcom-ufs drivers/phy/phy-qcom-ufs-qmp-14nm drivers/phy/phy-qcom-ufs-qmp-20nm - drivers/phy/phy-rockchip-dp # fate#322003 - drivers/phy/phy-rockchip-emmc # fate#322003 - drivers/phy/phy-rockchip-inno-usb2 # fate#322003 - drivers/phy/phy-rockchip-pcie # fate#322003 - drivers/phy/phy-rockchip-typec # fate#322003 -- drivers/phy/phy-rockchip-usb + drivers/phy/rockchip/phy-rockchip-dp + drivers/phy/rockchip/phy-rockchip-emmc + drivers/phy/rockchip/phy-rockchip-inno-usb2 + drivers/phy/rockchip/phy-rockchip-pcie + drivers/phy/rockchip/phy-rockchip-typec +- drivers/phy/rockchip/phy-rockchip-usb drivers/pinctrl/intel/pinctrl-cherryview - drivers/pinctrl/* - drivers/platform/chrome/* @@ -1594,6 +1611,7 @@ - drivers/platform/x86/toshiba_bluetooth drivers/platform/x86/wmi drivers/power/pda_power + drivers/power/avs/rockchip-io-domain - drivers/power/* drivers/powercap/intel_rapl # Intel Running Average Power Limit Driver, SNB and newer drivers/pps/clients/pps-gpio @@ -1605,6 +1623,7 @@ - drivers/pwm/* - drivers/rapidio/* drivers/regulator/fixed + drivers/regulator/rk808-regulator - drivers/regulator/gpio-regulator - drivers/regulator/isl9305 - drivers/regulator/ltc3589 @@ -1637,6 +1656,7 @@ drivers/rtc/rtc-pcf8523 drivers/rtc/rtc-pcf8563 drivers/rtc/rtc-pcf8583 + drivers/rtc/rtc-rk808 drivers/rtc/rtc-rs5c372 - drivers/rtc/rtc-rv8803 drivers/rtc/rtc-s35390a