aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy/samsung
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy/samsung')
-rw-r--r--drivers/phy/samsung/Kconfig25
-rw-r--r--drivers/phy/samsung/Makefile5
-rw-r--r--drivers/phy/samsung/phy-exynos-dp-video.c4
-rw-r--r--drivers/phy/samsung/phy-exynos-mipi-video.c4
-rw-r--r--drivers/phy/samsung/phy-exynos-pcie.c311
-rw-r--r--drivers/phy/samsung/phy-exynos5-usbdrd.c59
-rw-r--r--drivers/phy/samsung/phy-exynos5250-sata.c26
-rw-r--r--drivers/phy/samsung/phy-exynos5250-usb2.c48
-rw-r--r--drivers/phy/samsung/phy-exynos7-ufs.c85
-rw-r--r--drivers/phy/samsung/phy-exynosautov9-ufs.c74
-rw-r--r--drivers/phy/samsung/phy-fsd-ufs.c63
-rw-r--r--drivers/phy/samsung/phy-s5pv210-usb2.c4
-rw-r--r--drivers/phy/samsung/phy-samsung-ufs.c329
-rw-r--r--drivers/phy/samsung/phy-samsung-ufs.h146
-rw-r--r--drivers/phy/samsung/phy-samsung-usb2.c10
-rw-r--r--drivers/phy/samsung/phy-samsung-usb2.h3
16 files changed, 919 insertions, 277 deletions
diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig
index 9e483d1fdaf2..3ccaabf2850a 100644
--- a/drivers/phy/samsung/Kconfig
+++ b/drivers/phy/samsung/Kconfig
@@ -3,23 +3,23 @@
# Phy drivers for Samsung platforms
#
config PHY_EXYNOS_DP_VIDEO
- tristate "EXYNOS SoC series Display Port PHY driver"
+ tristate "Exynos SoC series Display Port PHY driver"
depends on OF
depends on ARCH_EXYNOS || COMPILE_TEST
default ARCH_EXYNOS
select GENERIC_PHY
help
- Support for Display Port PHY found on Samsung EXYNOS SoCs.
+ Support for Display Port PHY found on Samsung Exynos SoCs.
config PHY_EXYNOS_MIPI_VIDEO
- tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
+ tristate "S5P/Exynos SoC series MIPI CSI-2/DSI PHY driver"
depends on HAS_IOMEM
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select GENERIC_PHY
default y if ARCH_S5PV210 || ARCH_EXYNOS
help
Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
- and EXYNOS SoCs.
+ and Exynos SoCs.
config PHY_EXYNOS_PCIE
bool "Exynos PCIe PHY driver"
@@ -29,8 +29,17 @@ config PHY_EXYNOS_PCIE
Enable PCIe PHY support for Exynos SoC series.
This driver provides PHY interface for Exynos PCIe controller.
+config PHY_SAMSUNG_UFS
+ tristate "Exynos SoC series UFS PHY driver"
+ depends on OF && (ARCH_EXYNOS || COMPILE_TEST)
+ select GENERIC_PHY
+ help
+ Enable this to support the Samsung Exynos SoC UFS PHY driver for
+ Samsung Exynos SoCs. This driver provides the interface for UFS host
+ controller to do PHY related programming.
+
config PHY_SAMSUNG_USB2
- tristate "Samsung USB 2.0 PHY driver"
+ tristate "S5P/Exynos SoC series USB 2.0 PHY driver"
depends on HAS_IOMEM
depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 || COMPILE_TEST
select GENERIC_PHY
@@ -38,9 +47,9 @@ config PHY_SAMSUNG_USB2
default ARCH_EXYNOS
help
Enable this to support the Samsung USB 2.0 PHY driver for Samsung
- SoCs. This driver provides the interface for USB 2.0 PHY. Support
- for particular PHYs will be enabled based on the SoC type in addition
- to this driver.
+ S5Pv210 and Exynos SoCs. This driver provides the interface for USB
+ 2.0 PHY. Support for particular PHYs will be enabled based on the SoC
+ type in addition to this driver.
config PHY_EXYNOS4210_USB2
bool
diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile
index db9b1aa0de6e..afb34a153e34 100644
--- a/drivers/phy/samsung/Makefile
+++ b/drivers/phy/samsung/Makefile
@@ -2,6 +2,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_UFS) += phy-exynos-ufs.o
+phy-exynos-ufs-y += phy-samsung-ufs.o
+phy-exynos-ufs-y += phy-exynos7-ufs.o
+phy-exynos-ufs-y += phy-exynosautov9-ufs.o
+phy-exynos-ufs-y += phy-fsd-ufs.o
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
phy-exynos-usb2-y += phy-samsung-usb2.o
phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c
index 6c607df1dc9a..2b670ef91deb 100644
--- a/drivers/phy/samsung/phy-exynos-dp-video.c
+++ b/drivers/phy/samsung/phy-exynos-dp-video.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Samsung EXYNOS SoC series Display Port PHY driver
+ * Samsung Exynos SoC series Display Port PHY driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Jingoo Han <jg1.han@samsung.com>
@@ -115,5 +115,5 @@ static struct platform_driver exynos_dp_video_phy_driver = {
module_platform_driver(exynos_dp_video_phy_driver);
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
-MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver");
+MODULE_DESCRIPTION("Samsung Exynos SoC DP PHY driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c
index bb51195f189f..c1df1ef3ee3c 100644
--- a/drivers/phy/samsung/phy-exynos-mipi-video.c
+++ b/drivers/phy/samsung/phy-exynos-mipi-video.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ * Samsung S5P/Exynos SoC series MIPI CSIS/DSIM DPHY driver
*
* Copyright (C) 2013,2016 Samsung Electronics Co., Ltd.
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
@@ -364,6 +364,6 @@ static struct platform_driver exynos_mipi_video_phy_driver = {
};
module_platform_driver(exynos_mipi_video_phy_driver);
-MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
+MODULE_DESCRIPTION("Samsung S5P/Exynos SoC MIPI CSI-2/DSI PHY driver");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/samsung/phy-exynos-pcie.c b/drivers/phy/samsung/phy-exynos-pcie.c
index 659e7ae0a6cf..53c9230c2907 100644
--- a/drivers/phy/samsung/phy-exynos-pcie.c
+++ b/drivers/phy/samsung/phy-exynos-pcie.c
@@ -1,73 +1,44 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Samsung EXYNOS SoC series PCIe PHY driver
+ * Samsung Exynos SoC series PCIe PHY driver
*
* Phy provider for PCIe controller on Exynos SoC series
*
- * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2017-2020 Samsung Electronics Co., Ltd.
* Jaehoon Chung <jh80.chung@samsung.com>
*/
-#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/init.h>
#include <linux/mfd/syscon.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
-/* 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;
-};
+#define PCIE_PHY_OFFSET(x) ((x) * 0x4)
+
+/* Sysreg FSYS register offsets and bits for Exynos5433 */
+#define PCIE_EXYNOS5433_PHY_MAC_RESET 0x0208
+#define PCIE_MAC_RESET_MASK 0xFF
+#define PCIE_MAC_RESET BIT(4)
+#define PCIE_EXYNOS5433_PHY_L1SUB_CM_CON 0x1010
+#define PCIE_REFCLK_GATING_EN BIT(0)
+#define PCIE_EXYNOS5433_PHY_COMMON_RESET 0x1020
+#define PCIE_PHY_RESET BIT(0)
+#define PCIE_EXYNOS5433_PHY_GLOBAL_RESET 0x1040
+#define PCIE_GLOBAL_RESET BIT(0)
+#define PCIE_REFCLK BIT(1)
+#define PCIE_REFCLK_MASK 0x16
+#define PCIE_APP_REQ_EXIT_L1_MODE BIT(5)
+
+/* PMU PCIE PHY isolation control */
+#define EXYNOS5433_PMU_PCIE_PHY_OFFSET 0x730
/* 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 */
+ void __iomem *base;
+ struct regmap *pmureg;
+ struct regmap *fsysreg;
};
static void exynos_pcie_phy_writel(void __iomem *base, u32 val, u32 offset)
@@ -75,153 +46,96 @@ 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)
+/* Exynos5433 specific functions */
+static int exynos5433_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);
-
+ regmap_update_bits(ep->pmureg, EXYNOS5433_PMU_PCIE_PHY_OFFSET,
+ BIT(0), 1);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET,
+ PCIE_APP_REQ_EXIT_L1_MODE, 0);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_L1SUB_CM_CON,
+ PCIE_REFCLK_GATING_EN, 0);
+
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_COMMON_RESET,
+ PCIE_PHY_RESET, 1);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_MAC_RESET,
+ PCIE_MAC_RESET, 0);
+
+ /* PHY refclk 24MHz */
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET,
+ PCIE_REFCLK_MASK, PCIE_REFCLK);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_GLOBAL_RESET,
+ PCIE_GLOBAL_RESET, 0);
+
+
+ exynos_pcie_phy_writel(ep->base, 0x11, PCIE_PHY_OFFSET(0x3));
+
+ /* band gap reference on */
+ exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x20));
+ exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x4b));
+
+ /* jitter tuning */
+ exynos_pcie_phy_writel(ep->base, 0x34, PCIE_PHY_OFFSET(0x4));
+ exynos_pcie_phy_writel(ep->base, 0x02, PCIE_PHY_OFFSET(0x7));
+ exynos_pcie_phy_writel(ep->base, 0x41, PCIE_PHY_OFFSET(0x21));
+ exynos_pcie_phy_writel(ep->base, 0x7F, PCIE_PHY_OFFSET(0x14));
+ exynos_pcie_phy_writel(ep->base, 0xC0, PCIE_PHY_OFFSET(0x15));
+ exynos_pcie_phy_writel(ep->base, 0x61, PCIE_PHY_OFFSET(0x36));
+
+ /* D0 uninit.. */
+ exynos_pcie_phy_writel(ep->base, 0x44, PCIE_PHY_OFFSET(0x3D));
+
+ /* 24MHz */
+ exynos_pcie_phy_writel(ep->base, 0x94, PCIE_PHY_OFFSET(0x8));
+ exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x9));
+ exynos_pcie_phy_writel(ep->base, 0x93, PCIE_PHY_OFFSET(0xA));
+ exynos_pcie_phy_writel(ep->base, 0x6B, PCIE_PHY_OFFSET(0xC));
+ exynos_pcie_phy_writel(ep->base, 0xA5, PCIE_PHY_OFFSET(0xF));
+ exynos_pcie_phy_writel(ep->base, 0x34, PCIE_PHY_OFFSET(0x16));
+ exynos_pcie_phy_writel(ep->base, 0xA3, PCIE_PHY_OFFSET(0x17));
+ exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x1A));
+ exynos_pcie_phy_writel(ep->base, 0x71, PCIE_PHY_OFFSET(0x23));
+ exynos_pcie_phy_writel(ep->base, 0x4C, PCIE_PHY_OFFSET(0x24));
+
+ exynos_pcie_phy_writel(ep->base, 0x0E, PCIE_PHY_OFFSET(0x26));
+ exynos_pcie_phy_writel(ep->base, 0x14, PCIE_PHY_OFFSET(0x7));
+ exynos_pcie_phy_writel(ep->base, 0x48, PCIE_PHY_OFFSET(0x43));
+ exynos_pcie_phy_writel(ep->base, 0x44, PCIE_PHY_OFFSET(0x44));
+ exynos_pcie_phy_writel(ep->base, 0x03, PCIE_PHY_OFFSET(0x45));
+ exynos_pcie_phy_writel(ep->base, 0xA7, PCIE_PHY_OFFSET(0x48));
+ exynos_pcie_phy_writel(ep->base, 0x13, PCIE_PHY_OFFSET(0x54));
+ exynos_pcie_phy_writel(ep->base, 0x04, PCIE_PHY_OFFSET(0x31));
+ exynos_pcie_phy_writel(ep->base, 0, PCIE_PHY_OFFSET(0x32));
+
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_COMMON_RESET,
+ PCIE_PHY_RESET, 0);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_MAC_RESET,
+ PCIE_MAC_RESET_MASK, PCIE_MAC_RESET);
return 0;
}
-static int exynos5440_pcie_phy_power_on(struct phy *phy)
+static int exynos5433_pcie_phy_exit(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);
+ regmap_update_bits(ep->fsysreg, PCIE_EXYNOS5433_PHY_L1SUB_CM_CON,
+ PCIE_REFCLK_GATING_EN, PCIE_REFCLK_GATING_EN);
+ regmap_update_bits(ep->pmureg, EXYNOS5433_PMU_PCIE_PHY_OFFSET,
+ BIT(0), 0);
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,
+static const struct phy_ops exynos5433_phy_ops = {
+ .init = exynos5433_pcie_phy_init,
+ .exit = exynos5433_pcie_phy_exit,
.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,
+ .compatible = "samsung,exynos5433-pcie-phy",
},
{},
};
@@ -232,30 +146,30 @@ static int exynos_pcie_phy_probe(struct platform_device *pdev)
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);
+ exynos_phy->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(exynos_phy->base))
+ return PTR_ERR(exynos_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->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,pmu-syscon");
+ if (IS_ERR(exynos_phy->pmureg)) {
+ dev_err(&pdev->dev, "PMU regmap lookup failed.\n");
+ return PTR_ERR(exynos_phy->pmureg);
+ }
- exynos_phy->drv_data = drv_data;
+ exynos_phy->fsysreg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "samsung,fsys-sysreg");
+ if (IS_ERR(exynos_phy->fsysreg)) {
+ dev_err(&pdev->dev, "FSYS sysreg regmap lookup failed.\n");
+ return PTR_ERR(exynos_phy->fsysreg);
+ }
- generic_phy = devm_phy_create(dev, dev->of_node, drv_data->ops);
+ generic_phy = devm_phy_create(dev, dev->of_node, &exynos5433_phy_ops);
if (IS_ERR(generic_phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(generic_phy);
@@ -275,5 +189,4 @@ static struct platform_driver exynos_pcie_phy_driver = {
.suppress_bind_attrs = true,
}
};
-
builtin_platform_driver(exynos_pcie_phy_driver);
diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c
index e510732afb8b..ee0848fe8432 100644
--- a/drivers/phy/samsung/phy-exynos5-usbdrd.c
+++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Samsung EXYNOS5 SoC series USB DRD PHY driver
+ * Samsung Exynos5 SoC series USB DRD PHY driver
*
* Phy provider for USB 3.0 DRD controller on Exynos5 SoC series
*
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
+#include <linux/iopoll.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
@@ -33,7 +34,7 @@
#define EXYNOS5_FSEL_24MHZ 0x5
#define EXYNOS5_FSEL_50MHZ 0x7
-/* EXYNOS5: USB 3.0 DRD PHY registers */
+/* Exynos5: USB 3.0 DRD PHY registers */
#define EXYNOS5_DRD_LINKSYSTEM 0x04
#define LINKSYSTEM_FLADJ_MASK (0x3f << 1)
@@ -180,14 +181,14 @@ struct exynos5_usbdrd_phy_drvdata {
* @utmiclk: clock for utmi+ phy
* @itpclk: clock for ITP generation
* @drv_data: pointer to SoC level driver data structure
- * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
+ * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
* instances each with its 'phy' and 'phy_cfg'.
* @extrefclk: frequency select settings when using 'separate
* reference clocks' for SS and HS operations
* @ref_clk: reference clock to PHY block from which PHY's
* operational clocks are derived
- * vbus: VBUS regulator for phy
- * vbus_boost: Boost regulator for VBUS present on few Exynos boards
+ * @vbus: VBUS regulator for phy
+ * @vbus_boost: Boost regulator for VBUS present on few Exynos boards
*/
struct exynos5_usbdrd_phy {
struct device *dev;
@@ -556,41 +557,25 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd,
u32 val, u32 cmd)
{
- u32 usec = 100;
unsigned int result;
+ int err;
writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
- do {
- result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
- if (result & PHYREG1_CR_ACK)
- break;
-
- udelay(1);
- } while (usec-- > 0);
-
- if (!usec) {
- dev_err(phy_drd->dev,
- "CRPORT handshake timeout1 (0x%08x)\n", val);
- return -ETIME;
+ err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
+ result, (result & PHYREG1_CR_ACK), 1, 100);
+ if (err == -ETIMEDOUT) {
+ dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n", val);
+ return err;
}
- usec = 100;
-
writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
- do {
- result = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1);
- if (!(result & PHYREG1_CR_ACK))
- break;
-
- udelay(1);
- } while (usec-- > 0);
-
- if (!usec) {
- dev_err(phy_drd->dev,
- "CRPORT handshake timeout2 (0x%08x)\n", val);
- return -ETIME;
+ err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
+ result, !(result & PHYREG1_CR_ACK), 1, 100);
+ if (err == -ETIMEDOUT) {
+ dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n", val);
+ return err;
}
return 0;
@@ -714,7 +699,9 @@ static int exynos5_usbdrd_phy_calibrate(struct phy *phy)
struct phy_usb_instance *inst = phy_get_drvdata(phy);
struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
- return exynos5420_usbdrd_phy_calibrate(phy_drd);
+ if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI)
+ return exynos5420_usbdrd_phy_calibrate(phy_drd);
+ return 0;
}
static const struct phy_ops exynos5_usbdrd_phy_ops = {
@@ -842,7 +829,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct exynos5_usbdrd_phy *phy_drd;
struct phy_provider *phy_provider;
- struct resource *res;
const struct exynos5_usbdrd_phy_drvdata *drv_data;
struct regmap *reg_pmu;
u32 pmu_offset;
@@ -856,8 +842,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
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);
+ phy_drd->reg_phy = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(phy_drd->reg_phy))
return PTR_ERR(phy_drd->reg_phy);
@@ -958,7 +943,7 @@ static struct platform_driver exynos5_usb3drd_phy = {
};
module_platform_driver(exynos5_usb3drd_phy);
-MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver");
+MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver");
MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
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
index 4dd7324d91b2..595adba5fb8f 100644
--- a/drivers/phy/samsung/phy-exynos5250-sata.c
+++ b/drivers/phy/samsung/phy-exynos5250-sata.c
@@ -162,7 +162,6 @@ 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;
@@ -171,9 +170,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
if (!sata_phy)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- sata_phy->regs = devm_ioremap_resource(dev, res);
+ sata_phy->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(sata_phy->regs))
return PTR_ERR(sata_phy->regs);
@@ -190,6 +187,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
return -EINVAL;
sata_phy->client = of_find_i2c_device_by_node(node);
+ of_node_put(node);
if (!sata_phy->client)
return -EPROBE_DEFER;
@@ -198,20 +196,21 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
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 = PTR_ERR(sata_phy->phyclk);
+ goto put_dev;
}
ret = clk_prepare_enable(sata_phy->phyclk);
if (ret < 0) {
dev_err(dev, "failed to enable source clk\n");
- return ret;
+ goto put_dev;
}
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);
+ ret = PTR_ERR(sata_phy->phy);
+ goto clk_disable;
}
phy_set_drvdata(sata_phy->phy, sata_phy);
@@ -219,11 +218,18 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
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);
+ ret = PTR_ERR(phy_provider);
+ goto clk_disable;
}
return 0;
+
+clk_disable:
+ clk_disable_unprepare(sata_phy->phyclk);
+put_dev:
+ put_device(&sata_phy->client->dev);
+
+ return ret;
}
static const struct of_device_id exynos_sata_phy_of_match[] = {
diff --git a/drivers/phy/samsung/phy-exynos5250-usb2.c b/drivers/phy/samsung/phy-exynos5250-usb2.c
index 4f53b711fd6f..e198010e1bfd 100644
--- a/drivers/phy/samsung/phy-exynos5250-usb2.c
+++ b/drivers/phy/samsung/phy-exynos5250-usb2.c
@@ -117,9 +117,9 @@
/* 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)
+#define EXYNOS_5420_USB_ISOL_HOST_OFFSET 0x70C
+#define EXYNOS_5250_USB_ISOL_ENABLE BIT(0)
/* Mode swtich register */
#define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230
@@ -132,7 +132,6 @@ enum exynos4x12_phy_id {
EXYNOS5250_HOST,
EXYNOS5250_HSIC0,
EXYNOS5250_HSIC1,
- EXYNOS5250_NUM_PHYS,
};
/*
@@ -176,20 +175,19 @@ static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 offset;
- u32 mask;
+ u32 mask = EXYNOS_5250_USB_ISOL_ENABLE;
- switch (inst->cfg->id) {
- case EXYNOS5250_DEVICE:
+ if (drv->cfg == &exynos5250_usb2_phy_config &&
+ inst->cfg->id == EXYNOS5250_DEVICE)
offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET;
- mask = EXYNOS_5250_USB_ISOL_OTG;
- break;
- case EXYNOS5250_HOST:
+ else if (drv->cfg == &exynos5250_usb2_phy_config &&
+ inst->cfg->id == EXYNOS5250_HOST)
offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET;
- mask = EXYNOS_5250_USB_ISOL_HOST;
- break;
- default:
+ else if (drv->cfg == &exynos5420_usb2_phy_config &&
+ inst->cfg->id == EXYNOS5250_HOST)
+ offset = EXYNOS_5420_USB_ISOL_HOST_OFFSET;
+ else
return;
- }
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
}
@@ -390,9 +388,31 @@ static const struct samsung_usb2_common_phy exynos5250_phys[] = {
},
};
+static const struct samsung_usb2_common_phy exynos5420_phys[] = {
+ {
+ .label = "host",
+ .id = EXYNOS5250_HOST,
+ .power_on = exynos5250_power_on,
+ .power_off = exynos5250_power_off,
+ },
+ {
+ .label = "hsic",
+ .id = EXYNOS5250_HSIC0,
+ .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,
+ .num_phys = ARRAY_SIZE(exynos5250_phys),
.phys = exynos5250_phys,
.rate_to_clk = exynos5250_rate_to_clk,
};
+
+const struct samsung_usb2_phy_config exynos5420_usb2_phy_config = {
+ .has_mode_switch = 1,
+ .num_phys = ARRAY_SIZE(exynos5420_phys),
+ .phys = exynos5420_phys,
+ .rate_to_clk = exynos5250_rate_to_clk,
+};
diff --git a/drivers/phy/samsung/phy-exynos7-ufs.c b/drivers/phy/samsung/phy-exynos7-ufs.c
new file mode 100644
index 000000000000..a982e7c128c5
--- /dev/null
+++ b/drivers/phy/samsung/phy-exynos7-ufs.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver data for Samsung EXYNOS7 SoC
+ *
+ * Copyright (C) 2020 Samsung Electronics Co., Ltd.
+ */
+
+#include "phy-samsung-ufs.h"
+
+#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL 0x720
+#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1
+#define EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0)
+
+#define EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e
+
+/* Calibration for phy initialization */
+static const struct samsung_ufs_phy_cfg exynos7_pre_init_cfg[] = {
+ PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x017, 0x84, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY),
+ END_UFS_PHY_CFG
+};
+
+/* Calibration for HS mode series A/B */
+static const struct samsung_ufs_phy_cfg exynos7_pre_pwr_hs_cfg[] = {
+ PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_HS_ANY),
+ PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_HS_ANY),
+ PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_HS_ANY),
+ /* Setting order: 1st(0x16, 2nd(0x15) */
+ PHY_COMN_REG_CFG(0x016, 0xff, PWR_MODE_HS_ANY),
+ PHY_COMN_REG_CFG(0x015, 0x80, PWR_MODE_HS_ANY),
+ PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x037, 0x43, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x038, 0x3f, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_HS_G2_SER_A),
+ PHY_TRSV_REG_CFG(0x042, 0xbb, PWR_MODE_HS_G2_SER_B),
+ PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x034, 0x35, PWR_MODE_HS_G2_SER_A),
+ PHY_TRSV_REG_CFG(0x034, 0x36, PWR_MODE_HS_G2_SER_B),
+ PHY_TRSV_REG_CFG(0x035, 0x5b, PWR_MODE_HS_G2_SER_A),
+ PHY_TRSV_REG_CFG(0x035, 0x5c, PWR_MODE_HS_G2_SER_B),
+ END_UFS_PHY_CFG
+};
+
+/* Calibration for HS mode series A/B atfer PMC */
+static const struct samsung_ufs_phy_cfg exynos7_post_pwr_hs_cfg[] = {
+ PHY_COMN_REG_CFG(0x015, 0x00, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_HS_ANY),
+ END_UFS_PHY_CFG
+};
+
+static const struct samsung_ufs_phy_cfg *exynos7_ufs_phy_cfgs[CFG_TAG_MAX] = {
+ [CFG_PRE_INIT] = exynos7_pre_init_cfg,
+ [CFG_PRE_PWR_HS] = exynos7_pre_pwr_hs_cfg,
+ [CFG_POST_PWR_HS] = exynos7_post_pwr_hs_cfg,
+};
+
+static const char * const exynos7_ufs_phy_clks[] = {
+ "tx0_symbol_clk", "rx0_symbol_clk", "rx1_symbol_clk", "ref_clk",
+};
+
+const struct samsung_ufs_phy_drvdata exynos7_ufs_phy = {
+ .cfgs = exynos7_ufs_phy_cfgs,
+ .isol = {
+ .offset = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL,
+ .mask = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_MASK,
+ .en = EXYNOS7_EMBEDDED_COMBO_PHY_CTRL_EN,
+ },
+ .clk_list = exynos7_ufs_phy_clks,
+ .num_clks = ARRAY_SIZE(exynos7_ufs_phy_clks),
+ .cdr_lock_status_offset = EXYNOS7_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
+};
diff --git a/drivers/phy/samsung/phy-exynosautov9-ufs.c b/drivers/phy/samsung/phy-exynosautov9-ufs.c
new file mode 100644
index 000000000000..49e2bcbef0b4
--- /dev/null
+++ b/drivers/phy/samsung/phy-exynosautov9-ufs.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver data for Samsung EXYNOSAUTO v9 SoC
+ *
+ * Copyright (C) 2021 Samsung Electronics Co., Ltd.
+ */
+
+#include "phy-samsung-ufs.h"
+
+#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL 0x728
+#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1
+#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0)
+#define EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x5e
+
+#define PHY_TRSV_REG_CFG_AUTOV9(o, v, d) \
+ PHY_TRSV_REG_CFG_OFFSET(o, v, d, 0x50)
+
+/* Calibration for phy initialization */
+static const struct samsung_ufs_phy_cfg exynosautov9_pre_init_cfg[] = {
+ PHY_COMN_REG_CFG(0x023, 0x80, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x01d, 0x10, PWR_MODE_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV9(0x044, 0xb5, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x04d, 0x43, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x05b, 0x20, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x05e, 0xc0, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x038, 0x12, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x059, 0x58, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x06c, 0x18, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x06d, 0x02, PWR_MODE_ANY),
+
+ PHY_COMN_REG_CFG(0x023, 0xc0, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x023, 0x00, PWR_MODE_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV9(0x042, 0x5d, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x043, 0x80, PWR_MODE_ANY),
+
+ END_UFS_PHY_CFG,
+};
+
+/* Calibration for HS mode series A/B */
+static const struct samsung_ufs_phy_cfg exynosautov9_pre_pwr_hs_cfg[] = {
+ PHY_TRSV_REG_CFG_AUTOV9(0x032, 0xbc, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x03c, 0x7f, PWR_MODE_HS_ANY),
+ PHY_TRSV_REG_CFG_AUTOV9(0x048, 0xc0, PWR_MODE_HS_ANY),
+
+ PHY_TRSV_REG_CFG_AUTOV9(0x04a, 0x00, PWR_MODE_HS_G3_SER_B),
+ PHY_TRSV_REG_CFG_AUTOV9(0x04b, 0x10, PWR_MODE_HS_G1_SER_B |
+ PWR_MODE_HS_G3_SER_B),
+ PHY_TRSV_REG_CFG_AUTOV9(0x04d, 0x63, PWR_MODE_HS_G3_SER_B),
+
+ END_UFS_PHY_CFG,
+};
+
+static const struct samsung_ufs_phy_cfg *exynosautov9_ufs_phy_cfgs[CFG_TAG_MAX] = {
+ [CFG_PRE_INIT] = exynosautov9_pre_init_cfg,
+ [CFG_PRE_PWR_HS] = exynosautov9_pre_pwr_hs_cfg,
+};
+
+static const char * const exynosautov9_ufs_phy_clks[] = {
+ "ref_clk",
+};
+
+const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy = {
+ .cfgs = exynosautov9_ufs_phy_cfgs,
+ .isol = {
+ .offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL,
+ .mask = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_MASK,
+ .en = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CTRL_EN,
+ },
+ .clk_list = exynosautov9_ufs_phy_clks,
+ .num_clks = ARRAY_SIZE(exynosautov9_ufs_phy_clks),
+ .cdr_lock_status_offset = EXYNOSAUTOV9_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
+};
diff --git a/drivers/phy/samsung/phy-fsd-ufs.c b/drivers/phy/samsung/phy-fsd-ufs.c
new file mode 100644
index 000000000000..d36cabd53434
--- /dev/null
+++ b/drivers/phy/samsung/phy-fsd-ufs.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver data for FSD SoC
+ *
+ * Copyright (C) 2022 Samsung Electronics Co., Ltd.
+ *
+ */
+#include "phy-samsung-ufs.h"
+
+#define FSD_EMBEDDED_COMBO_PHY_CTRL 0x724
+#define FSD_EMBEDDED_COMBO_PHY_CTRL_MASK 0x1
+#define FSD_EMBEDDED_COMBO_PHY_CTRL_EN BIT(0)
+#define FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS 0x6e
+
+static const struct samsung_ufs_phy_cfg fsd_pre_init_cfg[] = {
+ PHY_COMN_REG_CFG(0x00f, 0xfa, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x010, 0x82, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x011, 0x1e, PWR_MODE_ANY),
+ PHY_COMN_REG_CFG(0x017, 0x94, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x035, 0x58, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x036, 0x32, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x037, 0x40, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x03b, 0x83, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x042, 0x88, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x043, 0xa6, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x048, 0x74, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x04c, 0x5b, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x04d, 0x83, PWR_MODE_ANY),
+ PHY_TRSV_REG_CFG(0x05c, 0x14, PWR_MODE_ANY),
+ END_UFS_PHY_CFG
+};
+
+/* Calibration for HS mode series A/B */
+static const struct samsung_ufs_phy_cfg fsd_pre_pwr_hs_cfg[] = {
+ END_UFS_PHY_CFG
+};
+
+/* Calibration for HS mode series A/B atfer PMC */
+static const struct samsung_ufs_phy_cfg fsd_post_pwr_hs_cfg[] = {
+ END_UFS_PHY_CFG
+};
+
+static const struct samsung_ufs_phy_cfg *fsd_ufs_phy_cfgs[CFG_TAG_MAX] = {
+ [CFG_PRE_INIT] = fsd_pre_init_cfg,
+ [CFG_PRE_PWR_HS] = fsd_pre_pwr_hs_cfg,
+ [CFG_POST_PWR_HS] = fsd_post_pwr_hs_cfg,
+};
+
+static const char * const fsd_ufs_phy_clks[] = {
+ "ref_clk",
+};
+
+const struct samsung_ufs_phy_drvdata fsd_ufs_phy = {
+ .cfgs = fsd_ufs_phy_cfgs,
+ .isol = {
+ .offset = FSD_EMBEDDED_COMBO_PHY_CTRL,
+ .mask = FSD_EMBEDDED_COMBO_PHY_CTRL_MASK,
+ .en = FSD_EMBEDDED_COMBO_PHY_CTRL_EN,
+ },
+ .clk_list = fsd_ufs_phy_clks,
+ .num_clks = ARRAY_SIZE(fsd_ufs_phy_clks),
+ .cdr_lock_status_offset = FSD_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
+};
diff --git a/drivers/phy/samsung/phy-s5pv210-usb2.c b/drivers/phy/samsung/phy-s5pv210-usb2.c
index 56a5083fe6f9..32be62e49804 100644
--- a/drivers/phy/samsung/phy-s5pv210-usb2.c
+++ b/drivers/phy/samsung/phy-s5pv210-usb2.c
@@ -139,6 +139,10 @@ static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
udelay(10);
rst &= ~rstbits;
writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+ /* The following delay is necessary for the reset sequence to be
+ * completed
+ */
+ udelay(80);
} else {
pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
pwr |= phypwr;
diff --git a/drivers/phy/samsung/phy-samsung-ufs.c b/drivers/phy/samsung/phy-samsung-ufs.c
new file mode 100644
index 000000000000..183c88e3d1ec
--- /dev/null
+++ b/drivers/phy/samsung/phy-samsung-ufs.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UFS PHY driver for Samsung SoC
+ *
+ * Copyright (C) 2020 Samsung Electronics Co., Ltd.
+ * Author: Seungwon Jeon <essuuj@gmail.com>
+ * Author: Alim Akhtar <alim.akhtar@samsung.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "phy-samsung-ufs.h"
+
+#define for_each_phy_lane(phy, i) \
+ for (i = 0; i < (phy)->lane_cnt; i++)
+#define for_each_phy_cfg(cfg) \
+ for (; (cfg)->id; (cfg)++)
+
+#define PHY_DEF_LANE_CNT 1
+
+static void samsung_ufs_phy_config(struct samsung_ufs_phy *phy,
+ const struct samsung_ufs_phy_cfg *cfg,
+ u8 lane)
+{
+ enum {LANE_0, LANE_1}; /* lane index */
+
+ switch (lane) {
+ case LANE_0:
+ writel(cfg->val, (phy)->reg_pma + cfg->off_0);
+ break;
+ case LANE_1:
+ if (cfg->id == PHY_TRSV_BLK)
+ writel(cfg->val, (phy)->reg_pma + cfg->off_1);
+ break;
+ }
+}
+
+static int samsung_ufs_phy_wait_for_lock_acq(struct phy *phy)
+{
+ struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
+ const unsigned int timeout_us = 100000;
+ const unsigned int sleep_us = 10;
+ u32 val;
+ int err;
+
+ err = readl_poll_timeout(
+ ufs_phy->reg_pma + PHY_APB_ADDR(PHY_PLL_LOCK_STATUS),
+ val, (val & PHY_PLL_LOCK_BIT), sleep_us, timeout_us);
+ if (err) {
+ dev_err(ufs_phy->dev,
+ "failed to get phy pll lock acquisition %d\n", err);
+ goto out;
+ }
+
+ err = readl_poll_timeout(
+ ufs_phy->reg_pma +
+ PHY_APB_ADDR(ufs_phy->drvdata->cdr_lock_status_offset),
+ val, (val & PHY_CDR_LOCK_BIT), sleep_us, timeout_us);
+ if (err)
+ dev_err(ufs_phy->dev,
+ "failed to get phy cdr lock acquisition %d\n", err);
+out:
+ return err;
+}
+
+static int samsung_ufs_phy_calibrate(struct phy *phy)
+{
+ struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
+ const struct samsung_ufs_phy_cfg * const *cfgs = ufs_phy->cfgs;
+ const struct samsung_ufs_phy_cfg *cfg;
+ int err = 0;
+ int i;
+
+ if (unlikely(ufs_phy->ufs_phy_state < CFG_PRE_INIT ||
+ ufs_phy->ufs_phy_state >= CFG_TAG_MAX)) {
+ dev_err(ufs_phy->dev, "invalid phy config index %d\n", ufs_phy->ufs_phy_state);
+ return -EINVAL;
+ }
+
+ cfg = cfgs[ufs_phy->ufs_phy_state];
+ if (!cfg)
+ goto out;
+
+ for_each_phy_cfg(cfg) {
+ for_each_phy_lane(ufs_phy, i) {
+ samsung_ufs_phy_config(ufs_phy, cfg, i);
+ }
+ }
+
+ if (ufs_phy->ufs_phy_state == CFG_POST_PWR_HS)
+ err = samsung_ufs_phy_wait_for_lock_acq(phy);
+
+ /**
+ * In Samsung ufshci, PHY need to be calibrated at different
+ * stages / state mainly before Linkstartup, after Linkstartup,
+ * before power mode change and after power mode change.
+ * Below state machine to make sure to calibrate PHY in each
+ * state. Here after configuring PHY in a given state, will
+ * change the state to next state so that next state phy
+ * calibration value can be programed
+ */
+out:
+ switch (ufs_phy->ufs_phy_state) {
+ case CFG_PRE_INIT:
+ ufs_phy->ufs_phy_state = CFG_POST_INIT;
+ break;
+ case CFG_POST_INIT:
+ ufs_phy->ufs_phy_state = CFG_PRE_PWR_HS;
+ break;
+ case CFG_PRE_PWR_HS:
+ ufs_phy->ufs_phy_state = CFG_POST_PWR_HS;
+ break;
+ case CFG_POST_PWR_HS:
+ /* Change back to INIT state */
+ ufs_phy->ufs_phy_state = CFG_PRE_INIT;
+ break;
+ default:
+ dev_err(ufs_phy->dev, "wrong state for phy calibration\n");
+ }
+
+ return err;
+}
+
+static int samsung_ufs_phy_clks_init(struct samsung_ufs_phy *phy)
+{
+ int i;
+ const struct samsung_ufs_phy_drvdata *drvdata = phy->drvdata;
+ int num_clks = drvdata->num_clks;
+
+ phy->clks = devm_kcalloc(phy->dev, num_clks, sizeof(*phy->clks),
+ GFP_KERNEL);
+ if (!phy->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < num_clks; i++)
+ phy->clks[i].id = drvdata->clk_list[i];
+
+ return devm_clk_bulk_get(phy->dev, num_clks, phy->clks);
+}
+
+static int samsung_ufs_phy_init(struct phy *phy)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+
+ ss_phy->lane_cnt = phy->attrs.bus_width;
+ ss_phy->ufs_phy_state = CFG_PRE_INIT;
+
+ return 0;
+}
+
+static int samsung_ufs_phy_power_on(struct phy *phy)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+ int ret;
+
+ samsung_ufs_phy_ctrl_isol(ss_phy, false);
+
+ ret = clk_bulk_prepare_enable(ss_phy->drvdata->num_clks, ss_phy->clks);
+ if (ret) {
+ dev_err(ss_phy->dev, "failed to enable ufs phy clocks\n");
+ return ret;
+ }
+
+ if (ss_phy->ufs_phy_state == CFG_PRE_INIT) {
+ ret = samsung_ufs_phy_calibrate(phy);
+ if (ret)
+ dev_err(ss_phy->dev, "ufs phy calibration failed\n");
+ }
+
+ return ret;
+}
+
+static int samsung_ufs_phy_power_off(struct phy *phy)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+
+ clk_bulk_disable_unprepare(ss_phy->drvdata->num_clks, ss_phy->clks);
+
+ samsung_ufs_phy_ctrl_isol(ss_phy, true);
+
+ return 0;
+}
+
+static int samsung_ufs_phy_set_mode(struct phy *generic_phy,
+ enum phy_mode mode, int submode)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(generic_phy);
+
+ ss_phy->mode = PHY_MODE_INVALID;
+
+ if (mode > 0)
+ ss_phy->mode = mode;
+
+ return 0;
+}
+
+static int samsung_ufs_phy_exit(struct phy *phy)
+{
+ struct samsung_ufs_phy *ss_phy = get_samsung_ufs_phy(phy);
+
+ ss_phy->ufs_phy_state = CFG_TAG_MAX;
+
+ return 0;
+}
+
+static const struct phy_ops samsung_ufs_phy_ops = {
+ .init = samsung_ufs_phy_init,
+ .exit = samsung_ufs_phy_exit,
+ .power_on = samsung_ufs_phy_power_on,
+ .power_off = samsung_ufs_phy_power_off,
+ .calibrate = samsung_ufs_phy_calibrate,
+ .set_mode = samsung_ufs_phy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id samsung_ufs_phy_match[];
+
+static int samsung_ufs_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *match;
+ struct samsung_ufs_phy *phy;
+ struct phy *gen_phy;
+ struct phy_provider *phy_provider;
+ const struct samsung_ufs_phy_drvdata *drvdata;
+ u32 isol_offset;
+ int err = 0;
+
+ match = of_match_node(samsung_ufs_phy_match, dev->of_node);
+ if (!match) {
+ err = -EINVAL;
+ dev_err(dev, "failed to get match_node\n");
+ goto out;
+ }
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ phy->reg_pma = devm_platform_ioremap_resource_byname(pdev, "phy-pma");
+ if (IS_ERR(phy->reg_pma)) {
+ err = PTR_ERR(phy->reg_pma);
+ goto out;
+ }
+
+ phy->reg_pmu = syscon_regmap_lookup_by_phandle(
+ dev->of_node, "samsung,pmu-syscon");
+ if (IS_ERR(phy->reg_pmu)) {
+ err = PTR_ERR(phy->reg_pmu);
+ dev_err(dev, "failed syscon remap for pmu\n");
+ goto out;
+ }
+
+ gen_phy = devm_phy_create(dev, NULL, &samsung_ufs_phy_ops);
+ if (IS_ERR(gen_phy)) {
+ err = PTR_ERR(gen_phy);
+ dev_err(dev, "failed to create PHY for ufs-phy\n");
+ goto out;
+ }
+
+ drvdata = match->data;
+ phy->dev = dev;
+ phy->drvdata = drvdata;
+ phy->cfgs = drvdata->cfgs;
+ memcpy(&phy->isol, &drvdata->isol, sizeof(phy->isol));
+
+ if (!of_property_read_u32_index(dev->of_node, "samsung,pmu-syscon", 1,
+ &isol_offset))
+ phy->isol.offset = isol_offset;
+
+ phy->lane_cnt = PHY_DEF_LANE_CNT;
+
+ err = samsung_ufs_phy_clks_init(phy);
+ if (err) {
+ dev_err(dev, "failed to get phy clocks\n");
+ goto out;
+ }
+
+ phy_set_drvdata(gen_phy, phy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ err = PTR_ERR(phy_provider);
+ dev_err(dev, "failed to register phy-provider\n");
+ goto out;
+ }
+out:
+ return err;
+}
+
+static const struct of_device_id samsung_ufs_phy_match[] = {
+ {
+ .compatible = "samsung,exynos7-ufs-phy",
+ .data = &exynos7_ufs_phy,
+ }, {
+ .compatible = "samsung,exynosautov9-ufs-phy",
+ .data = &exynosautov9_ufs_phy,
+ }, {
+ .compatible = "tesla,fsd-ufs-phy",
+ .data = &fsd_ufs_phy,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, samsung_ufs_phy_match);
+
+static struct platform_driver samsung_ufs_phy_driver = {
+ .probe = samsung_ufs_phy_probe,
+ .driver = {
+ .name = "samsung-ufs-phy",
+ .of_match_table = samsung_ufs_phy_match,
+ },
+};
+module_platform_driver(samsung_ufs_phy_driver);
+MODULE_DESCRIPTION("Samsung SoC UFS PHY Driver");
+MODULE_AUTHOR("Seungwon Jeon <essuuj@gmail.com>");
+MODULE_AUTHOR("Alim Akhtar <alim.akhtar@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/samsung/phy-samsung-ufs.h b/drivers/phy/samsung/phy-samsung-ufs.h
new file mode 100644
index 000000000000..e122960cfee8
--- /dev/null
+++ b/drivers/phy/samsung/phy-samsung-ufs.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * UFS PHY driver for Samsung EXYNOS SoC
+ *
+ * Copyright (C) 2020 Samsung Electronics Co., Ltd.
+ * Author: Seungwon Jeon <essuuj@gmail.com>
+ * Author: Alim Akhtar <alim.akhtar@samsung.com>
+ *
+ */
+#ifndef _PHY_SAMSUNG_UFS_
+#define _PHY_SAMSUNG_UFS_
+
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+#define PHY_COMN_BLK 1
+#define PHY_TRSV_BLK 2
+#define END_UFS_PHY_CFG { 0 }
+#define PHY_TRSV_CH_OFFSET 0x30
+#define PHY_APB_ADDR(off) ((off) << 2)
+
+#define PHY_COMN_REG_CFG(o, v, d) { \
+ .off_0 = PHY_APB_ADDR((o)), \
+ .off_1 = 0, \
+ .val = (v), \
+ .desc = (d), \
+ .id = PHY_COMN_BLK, \
+}
+
+#define PHY_TRSV_REG_CFG_OFFSET(o, v, d, c) { \
+ .off_0 = PHY_APB_ADDR((o)), \
+ .off_1 = PHY_APB_ADDR((o) + (c)), \
+ .val = (v), \
+ .desc = (d), \
+ .id = PHY_TRSV_BLK, \
+}
+
+#define PHY_TRSV_REG_CFG(o, v, d) \
+ PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_TRSV_CH_OFFSET)
+
+/* UFS PHY registers */
+#define PHY_PLL_LOCK_STATUS 0x1e
+
+#define PHY_PLL_LOCK_BIT BIT(5)
+#define PHY_CDR_LOCK_BIT BIT(4)
+
+/* description for PHY calibration */
+enum {
+ /* applicable to any */
+ PWR_DESC_ANY = 0,
+ /* mode */
+ PWR_DESC_PWM = 1,
+ PWR_DESC_HS = 2,
+ /* series */
+ PWR_DESC_SER_A = 1,
+ PWR_DESC_SER_B = 2,
+ /* gear */
+ PWR_DESC_G1 = 1,
+ PWR_DESC_G2 = 2,
+ PWR_DESC_G3 = 3,
+ /* field mask */
+ MD_MASK = 0x3,
+ SR_MASK = 0x3,
+ GR_MASK = 0x7,
+};
+
+#define PWR_MODE_HS_G1_ANY PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_ANY)
+#define PWR_MODE_HS_G1_SER_A PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_A)
+#define PWR_MODE_HS_G1_SER_B PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_B)
+#define PWR_MODE_HS_G2_ANY PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_ANY)
+#define PWR_MODE_HS_G2_SER_A PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_A)
+#define PWR_MODE_HS_G2_SER_B PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_B)
+#define PWR_MODE_HS_G3_ANY PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_ANY)
+#define PWR_MODE_HS_G3_SER_A PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_A)
+#define PWR_MODE_HS_G3_SER_B PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_B)
+#define PWR_MODE(g, s, m) ((((g) & GR_MASK) << 4) |\
+ (((s) & SR_MASK) << 2) | ((m) & MD_MASK))
+#define PWR_MODE_PWM_ANY PWR_MODE(PWR_DESC_ANY,\
+ PWR_DESC_ANY, PWR_DESC_PWM)
+#define PWR_MODE_HS(g, s) ((((g) & GR_MASK) << 4) |\
+ (((s) & SR_MASK) << 2) | PWR_DESC_HS)
+#define PWR_MODE_HS_ANY PWR_MODE(PWR_DESC_ANY,\
+ PWR_DESC_ANY, PWR_DESC_HS)
+#define PWR_MODE_ANY PWR_MODE(PWR_DESC_ANY,\
+ PWR_DESC_ANY, PWR_DESC_ANY)
+/* PHY calibration point/state */
+enum {
+ CFG_PRE_INIT,
+ CFG_POST_INIT,
+ CFG_PRE_PWR_HS,
+ CFG_POST_PWR_HS,
+ CFG_TAG_MAX,
+};
+
+struct samsung_ufs_phy_cfg {
+ u32 off_0;
+ u32 off_1;
+ u32 val;
+ u8 desc;
+ u8 id;
+};
+
+struct samsung_ufs_phy_pmu_isol {
+ u32 offset;
+ u32 mask;
+ u32 en;
+};
+
+struct samsung_ufs_phy_drvdata {
+ const struct samsung_ufs_phy_cfg **cfgs;
+ struct samsung_ufs_phy_pmu_isol isol;
+ const char * const *clk_list;
+ int num_clks;
+ u32 cdr_lock_status_offset;
+};
+
+struct samsung_ufs_phy {
+ struct device *dev;
+ void __iomem *reg_pma;
+ struct regmap *reg_pmu;
+ struct clk_bulk_data *clks;
+ const struct samsung_ufs_phy_drvdata *drvdata;
+ const struct samsung_ufs_phy_cfg * const *cfgs;
+ struct samsung_ufs_phy_pmu_isol isol;
+ u8 lane_cnt;
+ int ufs_phy_state;
+ enum phy_mode mode;
+};
+
+static inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *phy)
+{
+ return (struct samsung_ufs_phy *)phy_get_drvdata(phy);
+}
+
+static inline void samsung_ufs_phy_ctrl_isol(
+ struct samsung_ufs_phy *phy, u32 isol)
+{
+ regmap_update_bits(phy->reg_pmu, phy->isol.offset,
+ phy->isol.mask, isol ? 0 : phy->isol.en);
+}
+
+extern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy;
+extern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy;
+extern const struct samsung_ufs_phy_drvdata fsd_ufs_phy;
+
+#endif /* _PHY_SAMSUNG_UFS_ */
diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c
index 090aa02e02de..ec2befabeea6 100644
--- a/drivers/phy/samsung/phy-samsung-usb2.c
+++ b/drivers/phy/samsung/phy-samsung-usb2.c
@@ -127,6 +127,10 @@ static const struct of_device_id samsung_usb2_phy_of_match[] = {
.compatible = "samsung,exynos5250-usb2-phy",
.data = &exynos5250_usb2_phy_config,
},
+ {
+ .compatible = "samsung,exynos5420-usb2-phy",
+ .data = &exynos5420_usb2_phy_config,
+ },
#endif
#ifdef CONFIG_PHY_S5PV210_USB2
{
@@ -143,7 +147,6 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
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;
@@ -167,8 +170,7 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
drv->cfg = cfg;
drv->dev = dev;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- drv->reg_phy = devm_ioremap_resource(dev, mem);
+ drv->reg_phy = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(drv->reg_phy)) {
dev_err(dev, "Failed to map register memory (phy)\n");
return PTR_ERR(drv->reg_phy);
@@ -255,7 +257,7 @@ static struct platform_driver samsung_usb2_phy_driver = {
};
module_platform_driver(samsung_usb2_phy_driver);
-MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
+MODULE_DESCRIPTION("Samsung S5P/Exynos SoC USB PHY driver");
MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
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
index 2c1a7d71142b..ebaf43bfc5a2 100644
--- a/drivers/phy/samsung/phy-samsung-usb2.h
+++ b/drivers/phy/samsung/phy-samsung-usb2.h
@@ -43,7 +43,7 @@ struct samsung_usb2_phy_driver {
struct regmap *reg_pmu;
struct regmap *reg_sys;
spinlock_t lock;
- struct samsung_usb2_phy_instance instances[0];
+ struct samsung_usb2_phy_instance instances[];
};
struct samsung_usb2_common_phy {
@@ -66,5 +66,6 @@ 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 exynos5420_usb2_phy_config;
extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config;
#endif