}
EXPORT_SYMBOL_GPL(phy_exit);
+int phy_vendor_set(struct phy *phy, int is_enable, int is_cancel)
+{
+ int ret;
+
+ if (!phy || !phy->ops->vendor_set)
+ return 0;
+
+ ret = phy->ops->vendor_set(phy, is_enable, is_cancel);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy vendor setting failed --> %d\n", ret);
+ } else {
+ ret = 0; /* Override possible ret == -ENOTSUPP */
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_vendor_set);
+
+int phy_ilbk(struct phy *phy)
+{
+ int ret;
+
+ if (!phy || !phy->ops->ilbk)
+ return 0;
+
+ ret = phy->ops->ilbk(phy);
+ if (ret < 0)
+ dev_err(&phy->dev, "phy ilbk failed --> %d\n", ret);
+ else
+ ret = 0; /* Override possible ret == -ENOTSUPP */
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_ilbk);
+
+int phy_tune(struct phy *phy, int phy_state)
+{
+ int ret;
+
+ if (!phy || !phy->ops->tune)
+ return 0;
+
+ ret = phy->ops->tune(phy, phy_state);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy tune failed --> %d\n", ret);
+ } else {
+ ret = 0; /* Override possible ret == -ENOTSUPP */
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_tune);
+
+void phy_conn(struct phy *phy, int option)
+{
+ if (!phy || !phy->ops->conn)
+ return;
+
+ phy->ops->conn(phy, option);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(phy_conn);
+
+int phy_set(struct phy *phy, int option, void *info)
+{
+ int ret;
+
+ if (!phy || !phy->ops->set)
+ return 0;
+
+ ret = phy->ops->set(phy, option, info);
+ if (ret < 0)
+ dev_err(&phy->dev, "phy set failed --> %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_set);
+
int phy_power_on(struct phy *phy)
{
int ret = 0;
This driver provides PHY interface for USB 3.0 DRD controller
present on Exynos5 SoC series.
+config PHY_EXYNOS_USBDRD
+ tristate "Exynos 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 SoC series.
+ This driver provides PHY interface for USB 3.0 DRD controller
+ present on Exynos SoC series.
+
+config PHY_EXYNOS_USBDRD3
+ tristate "Exynos SoC series USB DRD PHY3 & PHY2 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 SoC series.
+ This driver provides PHY interface for USB 3.0 DRD controller
+ present on Exynos SoC series.
+
+config PHY_EXYNOS_DEBUGFS
+ tristate "Exynos SoC series USB DRD PHY DebugFS"
+ depends on DEBUG_FS
+ depends on PHY_EXYNOS_USBDRD || PHY_EXYNOS_USBDRD3
+ select GENERIC_PHY
+ default y
+ help
+ Enable USB DRD PHY DEBUGFS support for Exynos SoC series.
+ This driver provides PHY debugging interface for USBDRD PHY controller
+ present on Exynos SoC series.
+
+config PHY_EXYNOS_DP_DEBUGFS
+ tristate "Exynos SoC series USB DRD DP PHY DebugFS"
+ depends on DEBUG_FS
+ depends on PHY_EXYNOS_DEBUGFS
+ select GENERIC_PHY
+ default y
+ help
+ Enable USB DRD DP PHY DEBUGFS support for Exynos SoC series.
+ This driver provides PHY debugging interface for USBDRD DP PHY controller
+ present on Exynos SoC series.
+
+config PHY_SAMSUNG_USB_CAL
+ bool "Samsung USB PHY CAL"
+ depends on PHY_EXYNOS_USBDRD || PHY_EXYNOS_USBDRD3
+ default y
+ help
+ Enable this to support CAL (Chip Abstraction Layer)
+ for Samsung USB PHY controller.
+
config PHY_EXYNOS5250_SATA
tristate "Exynos5250 Sata SerDes/PHY driver"
depends on SOC_EXYNOS5250
port to accept one SATA device.
config PHY_EXYNOS8895_MIPI
- tristate "Samsung EXYNOS8895 SoC MIPI CSI/DSI PHY driver"
+ tristate "Samsung EXYNOS SoC MIPI CSI/DSI D/C-PHY driver"
depends on HAS_IOMEM
depends on ARCH_EXYNOS && OF || COMPILE_TEST
select GENERIC_PHY
help
Support for MIPI CSI and MIPI DSI DPHY found on Samsung
and EXYNOS SoCs.
+
+config PHY_EXYNOS_DISPLAYPORT
+ tristate "Samsung EXYNOS SoC DISPLAYPORT PHY driver"
+ depends on HAS_IOMEM
+ depends on ARCH_EXYNOS && OF || COMPILE_TEST
+ select GENERIC_PHY
+ help
+ Support for DISPLAYPORT PHY found on Samsung
+ and EXYNOS SoCs.
# SPDX-License-Identifier: GPL-2.0
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_EXYNOS8895_MIPI) += phy-exynos8895-mipi.o
+obj-$(CONFIG_PHY_EXYNOS8895_MIPI) += phy-exynos8895-mipi.o
+obj-$(CONFIG_PHY_EXYNOS_DISPLAYPORT) += phy-exynos-displayport.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_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_DEBUGFS) += phy-exynos-debugfs.o
+obj-$(CONFIG_PHY_EXYNOS_DEBUGFS) += phy-exynos-dp-debugfs.o
+obj-$(CONFIG_PHY_EXYNOS_USBDRD) += phy-exynos-usbdrd.o
+obj-$(CONFIG_PHY_EXYNOS_USBDRD3) += phy-exynos-usbdrd3.o
+obj-$(CONFIG_PHY_EXYNOS_DEBUGFS) += phy-exynos-debugfs.o
+obj-$(CONFIG_PHY_SAMSUNG_USB_CAL) += phy-exynos-usb3p1.o phy-exynos-usbdp.o
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
--- /dev/null
+/**
+ * phy-exynos-debug.h - Samsung EXYNOS SoC series USB DRD PHY Debug Header
+ *
+ * Phy provider for USB 3.0 DRD controller on Exynos SoC series
+ *
+ * Copyright (C) 2016 Samsung Electronics Co., Ltd.
+ * Author: Kyounghye Yunm <k-hye.yun@samsung.com>
+ *
+ * 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 "phy-exynos-usbdrd.h"
+#ifdef CONFIG_DEBUG_FS
+extern int exynos_usbdrd_debugfs_init(struct exynos_usbdrd_phy *phy_drd);
+extern int exynos_usbdrd_dp_debugfs_init(struct exynos_usbdrd_phy *phy_drd);
+extern void exynos_usbdrd_debugfs_exit(struct exynos_usbdrd_phy *phy_drd);
+#else
+static inline int exynos_usbdrd_debugfs_init(struct exynos_usbdrd_phy *)
+{ return 0; }
+static inline void exynos_usbdrd_debugfs_exit(struct exynos_usbdrd_phy *)
+{ }
+#endif
+
+#define dump_register(nm) \
+{ \
+ .name = __stringify(nm), \
+ .offset = EXYNOS_USBCON_##nm, \
+}
+#define dump_regmap_mask(nm, bit_nm, bit) \
+{ \
+ .name = __stringify(nm), \
+ .offset = EXYNOS_USBCON_##nm, \
+ .bitname = #bit_nm, \
+ .bitmask = nm##_##bit_nm##_MASK, \
+ .bitoffset = bit, \
+ .mask = true, \
+}
+#define dump_regmap(nm, bit_nm, bit) \
+{ \
+ .name = __stringify(nm), \
+ .offset = EXYNOS_USBCON_##nm, \
+ .bitname = #bit_nm, \
+ .bitmask = nm##_##bit_nm, \
+ .bitoffset = bit, \
+ .mask = false, \
+}
+
+#define dump_register_dp(rnm) \
+{ \
+ .name = __stringify(rnm), \
+ .offset = EXYNOS_USBDP_COM_##rnm, \
+}
+#define dump_regmap_dp_mask(rnm, bit_rnm, bit_nm, bit) \
+{ \
+ .name = __stringify(rnm), \
+ .offset = EXYNOS_USBDP_COM_##rnm, \
+ .bitname = #bit_nm, \
+ .bitmask = bit_rnm##_##bit_nm##_MASK, \
+ .bitoffset = bit, \
+ .mask = true, \
+}
+#define dump_regmap_dp(rnm, bit_rnm, bit_nm, bit) \
+{ \
+ .name = __stringify(rnm), \
+ .offset = EXYNOS_USBDP_COM_##rnm, \
+ .bitname = #bit_nm, \
+ .bitmask = bit_rnm##_##bit_nm, \
+ .bitoffset = bit, \
+ .mask = false, \
+}
+
+struct debugfs_regmap32 {
+ char *name;
+ char *bitname;
+ unsigned int offset;
+ unsigned int bitoffset;
+ unsigned int bitmask;
+ bool mask;
+};
+
+struct debugfs_regset_map {
+ const struct debugfs_regmap32 *regs;
+ int nregs;
+};
+
+struct exynos_debugfs_prvdata {
+ struct exynos_usbdrd_phy *phy_drd;
+ struct dentry *root;
+ struct debugfs_regset32 *regset;
+ struct debugfs_regset_map *regmap;
+};
--- /dev/null
+/*
+ * Samsung EXYNOS SoC series USB DRD PHY DebugFS file
+ *
+ * Phy provider for USB 3.0 DRD controller on Exynos SoC series
+ *
+ * Copyright (C) 2016 Samsung Electronics Co., Ltd.
+ * Author: Kyounghye Yun <k-hye.yun@samsung.com>
+ *
+ * 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 <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+#include <linux/usb/ch9.h>
+#include "phy-exynos-usbdrd.h"
+#include "phy-samsung-usb3-cal.h"
+#include "phy-samsung-usb3-cal-combo.h"
+#include "phy-exynos-debug.h"
+
+struct exynos_debugfs_prvdata *prvdata;
+
+/* PHY Control register set of SOC which combo phy is applied in */
+static const struct debugfs_reg32 exynos_usb3drd_phycon_regs[] = {
+ dump_register(CTRLVER),
+ dump_register(LINKCTRL),
+ dump_register(PHYCON_LINKPORT),
+ dump_register(LINK_DEBUG_L),
+ dump_register(LINK_DEBUG_H),
+ dump_register(LTSTATE_HIS),
+ dump_register(CLKRSTCTRL),
+ dump_register(PWRCTL),
+ dump_register(SSPPLLCTL),
+ dump_register(SECPMACTL),
+ dump_register(UTMICTRL),
+ dump_register(HSPCTRL),
+ dump_register(HSPPARACON),
+ dump_register(HSPTEST),
+ dump_register(HSPPLLTUNE),
+ dump_register(REWA_CTRL),
+ dump_register(HSREWA_INTR),
+ dump_register(HSREWA_CTRL),
+ dump_register(HSREWA_REFTO),
+ dump_register(HSREWA_HSTK),
+ dump_register(HSREWA_CNT),
+ dump_register(HSREWA_INT1_EVNT),
+ dump_register(HSREWA_INT1_EVNT_MSK),
+};
+
+static const struct debugfs_reg32 exynos_usb3drd_regs[] = {
+ dump_register(LINKSYSTEM),
+ dump_register(PHYUTMI),
+ dump_register(PHYCLKRST),
+ dump_register(PHYREG0),
+ dump_register(PHYPARAM0),
+ dump_register(PHYPARAM1),
+ dump_register(PHYTEST),
+ dump_register(PHYRESUME),
+ dump_register(PHYPCSVAL),
+ dump_register(LINKPORT),
+ dump_register(PHYPARAM2),
+};
+
+static const struct debugfs_reg32 exynos_usb2drd_regs[] = {
+ dump_register(LINKSYSTEM),
+ dump_register(PHYUTMI),
+ dump_register(PHYCLKRST),
+ dump_register(PHYPARAM0),
+ dump_register(PHYPOWERDOWN),
+ dump_register(PHYRESUME),
+ dump_register(LINKPORT),
+ dump_register(HSPHYCTRL),
+ dump_register(HSPHYPLLTUNE),
+};
+
+/* PHY Control register set of SOC which combo phy is applied in */
+static const struct debugfs_regmap32 exynos_usb3drd_phycon_regmap[] = {
+ dump_regmap_mask(CTRLVER, CTRL_VER, 0),
+ dump_regmap(LINKCTRL, FORCE_RXELECIDLE, 18),
+ dump_regmap(LINKCTRL, FORCE_PHYSTATUS, 17),
+ dump_regmap(LINKCTRL, FORCE_PIPE_EN, 16),
+ dump_regmap(LINKCTRL, DIS_BUSPEND_QACT, 13),
+ dump_regmap(LINKCTRL, DIS_LINKGATE_QACT, 12),
+ dump_regmap(LINKCTRL, DIS_ID0_QACT, 11),
+ dump_regmap(LINKCTRL, DIS_VBUSVALID_QACT, 10),
+ dump_regmap(LINKCTRL, DIS_BVALID_QACT, 9),
+ dump_regmap(LINKCTRL, FORCE_QACT, 8),
+ dump_regmap_mask(LINKCTRL, BUS_FILTER_BYPASS, 4),
+ dump_regmap(LINKCTRL, HOST_SYSTEM_ERR, 2),
+ dump_regmap(LINKCTRL, LINK_PME, 1),
+ dump_regmap(LINKCTRL, PME_GENERATION, 0),
+ dump_regmap_mask(PHYCON_LINKPORT, HOST_NUM_U3, 16),
+ dump_regmap_mask(PHYCON_LINKPORT, HOST_NUM_U2, 12),
+ dump_regmap(PHYCON_LINKPORT, HOST_U3_PORT_DISABLE, 9),
+ dump_regmap(PHYCON_LINKPORT, HOST_U2_PORT_DISABLE, 8),
+ dump_regmap(PHYCON_LINKPORT, HOST_PORT_POWER_CON_PRESENT, 6),
+ dump_regmap(PHYCON_LINKPORT, HUB_PORT_OVERCURRENT_U3, 5),
+ dump_regmap(PHYCON_LINKPORT, HUB_PORT_OVERCURRENT_U2, 4),
+ dump_regmap(PHYCON_LINKPORT, HUB_PORT_OVERCURRENT_SET_U3, 3),
+ dump_regmap(PHYCON_LINKPORT, HUB_PORT_OVERCURRENT_SET_U2, 2),
+ dump_regmap(PHYCON_LINKPORT, HUB_PERM_ATTACH_U3, 1),
+ dump_regmap(PHYCON_LINKPORT, HUB_PERM_ATTACH_U2, 0),
+ dump_regmap_mask(LINK_DEBUG_L, DEBUG_L, 0),
+ dump_regmap_mask(LINK_DEBUG_H, DEBUG_H, 0),
+ dump_regmap(LTSTATE_HIS, LINKTRN_DONE, 31),
+ dump_regmap_mask(LTSTATE_HIS, LTSTATE_HIS4, 16),
+ dump_regmap_mask(LTSTATE_HIS, LTSTATE_HIS3, 12),
+ dump_regmap_mask(LTSTATE_HIS, LTSTATE_HIS2, 8),
+ dump_regmap_mask(LTSTATE_HIS, LTSTATE_HIS1, 4),
+ dump_regmap_mask(LTSTATE_HIS, LTSTATE_HIS0, 0),
+ dump_regmap(CLKRSTCTRL, USBAUDIO_CLK_GATE_EN, 9),
+ dump_regmap(CLKRSTCTRL, USBAUDIO_CLK_SEL, 8),
+ dump_regmap(CLKRSTCTRL, LINK_PCLK_SEL, 7),
+ dump_regmap(CLKRSTCTRL, REFCLKSEL, 4),
+ dump_regmap(CLKRSTCTRL, PHY_SW_RST, 3),
+ dump_regmap(CLKRSTCTRL, PHY_RESET_SEL, 2),
+ dump_regmap(CLKRSTCTRL, PORTRESET, 1),
+ dump_regmap(CLKRSTCTRL, LINK_SW_RST, 0),
+ dump_regmap(PWRCTL, FORCE_POWERDOWN, 2),
+ dump_regmap_mask(SSPPLLCTL, FSEL, 0),
+ dump_regmap_mask(SECPMACTL, PMA_PLL_REF_CLK_SEL, 10),
+ dump_regmap_mask(SECPMACTL, PMA_REF_FREQ_SEL, 8),
+ dump_regmap(SECPMACTL, PMA_LOW_PWR, 4),
+ dump_regmap(SECPMACTL, PMA_TRSV_SW_RST, 3),
+ dump_regmap(SECPMACTL, PMA_CMN_SW_RST, 2),
+ dump_regmap(SECPMACTL, PMA_INIT_SW_RST, 1),
+ dump_regmap(SECPMACTL, PMA_APB_SW_RST, 0),
+ dump_regmap(UTMICTRL, OPMODE_EN, 8),
+ dump_regmap_mask(UTMICTRL, FORCE_OPMODE, 6),
+ dump_regmap(UTMICTRL, FORCE_VBUSVALID, 5),
+ dump_regmap(UTMICTRL, FORCE_BVALID, 4),
+ dump_regmap(UTMICTRL, FORCE_DPPULLDOWN, 3),
+ dump_regmap(UTMICTRL, FORCE_DMPULLDOWN, 2),
+ dump_regmap(UTMICTRL, FORCE_SUSPEND, 1),
+ dump_regmap(UTMICTRL, FORCE_SLEEP, 0),
+ dump_regmap(HSPCTRL, AUTORSM_ENB, 29),
+ dump_regmap(HSPCTRL, RETENABLE_EN, 28),
+ dump_regmap(HSPCTRL, FSLS_SPEED_SEL, 25),
+ dump_regmap(HSPCTRL, FSV_OUT_EN, 24),
+ dump_regmap(HSPCTRL, HS_XCVR_EXT_CTL, 22),
+ dump_regmap(HSPCTRL, HS_RXDAT, 21),
+ dump_regmap(HSPCTRL, HS_SQUELCH, 20),
+ dump_regmap(HSPCTRL, FSVMINUS, 17),
+ dump_regmap(HSPCTRL, FSVPLUS, 16),
+ dump_regmap(HSPCTRL, VBUSVLDEXTSEL, 13),
+ dump_regmap(HSPCTRL, VBUSVLDEXT, 12),
+ dump_regmap(HSPCTRL, EN_UTMISUSPEND, 9),
+ dump_regmap(HSPCTRL, COMMONONN, 8),
+ dump_regmap(HSPCTRL, VATESTENB, 6),
+ dump_regmap(HSPCTRL, CHGDET, 5),
+ dump_regmap(HSPCTRL, VDATSRCENB, 4),
+ dump_regmap(HSPCTRL, VDATDETENB, 3),
+ dump_regmap(HSPCTRL, CHRGSEL, 2),
+ dump_regmap(HSPCTRL, ACAENB, 1),
+ dump_regmap(HSPCTRL, DEDENB, 0),
+ dump_regmap_mask(HSPPARACON, TXVREFTUNE, 28),
+ dump_regmap_mask(HSPPARACON, TXRISETUNE, 24),
+ dump_regmap_mask(HSPPARACON, TXRESTUNE, 21),
+ dump_regmap(HSPPARACON, TXPREEMPPULSETUNE, 20),
+ dump_regmap_mask(HSPPARACON, TXPREEMPAMPTUNE, 18),
+ dump_regmap_mask(HSPPARACON, TXHSXVTUNE, 16),
+ dump_regmap_mask(HSPPARACON, TXFSLSTUNE, 12),
+ dump_regmap_mask(HSPPARACON, SQRXTUNE, 8),
+ dump_regmap_mask(HSPPARACON, OTGTUNE, 4),
+ dump_regmap_mask(HSPPARACON, COMPDISTUNE, 0),
+ dump_regmap(HSPTEST, SIDDQ_PORT0, 24),
+ dump_regmap_mask(HSPTEST, LINESTATE_PORT0, 20),
+ dump_regmap_mask(HSPTEST, TESTDATAOUT_PORT0, 16),
+ dump_regmap(HSPTEST, TESTCLK_PORT0, 13),
+ dump_regmap(HSPTEST, TESTDATAOUTSEL_PORT0, 12),
+ dump_regmap_mask(HSPTEST, TESTADDR_PORT0, 8),
+ dump_regmap_mask(HSPTEST, TESTDATAIN_PORT0, 0),
+ dump_regmap(HSPPLLTUNE, PLLBTUNE, 8),
+ dump_regmap_mask(HSPPLLTUNE, PLLITUNE, 4),
+ dump_regmap_mask(HSPPLLTUNE, PLLPTUNE, 0),
+ dump_regmap(REWA_CTRL, HSREWA_EN, 0),
+ dump_regmap_mask(HSREWA_INTR, WAKEUP_MASK, 12),
+ dump_regmap_mask(HSREWA_INTR, TIMEOUT_INTR_MASK, 8),
+ dump_regmap_mask(HSREWA_INTR, EVENT_INT_MASK, 4),
+ dump_regmap_mask(HSREWA_INTR, WAKEUP_INTR_MASK, 0),
+ dump_regmap(HSREWA_CTRL, DIG_BYPASS_CON_EN, 28),
+ dump_regmap(HSREWA_CTRL, DPDM_MONITOR_SEL, 24),
+ dump_regmap(HSREWA_CTRL, HS_LINK_READY, 20),
+ dump_regmap(HSREWA_CTRL, HS_SYS_VALID, 16),
+ dump_regmap(HSREWA_CTRL, HS_REWA_ERROR, 4),
+ dump_regmap(HSREWA_CTRL, HS_REWA_DONE, 0),
+ dump_regmap_mask(HSREWA_REFTO, HOST_K_TIMEOUT, 0),
+ dump_regmap_mask(HSREWA_HSTK, HOST_K_DELAY, 0),
+ dump_regmap_mask(HSREWA_CNT, WAKEUP_CNT, 0),
+ dump_regmap(HSREWA_INT1_EVNT, ERR_SUS, 18),
+ dump_regmap(HSREWA_INT1_EVNT, ERR_DEV_K, 17),
+ dump_regmap(HSREWA_INT1_EVNT, DISCON, 16),
+ dump_regmap(HSREWA_INT1_EVNT, BYPASS_DIS, 2),
+ dump_regmap(HSREWA_INT1_EVNT, RET_DIS, 1),
+ dump_regmap(HSREWA_INT1_EVNT, RET_EN, 0),
+ dump_regmap(HSREWA_INT1_EVNT_MSK, ERR_SUS_MASK, 18),
+ dump_regmap(HSREWA_INT1_EVNT_MSK, ERR_DEV_K_MASK, 17),
+ dump_regmap(HSREWA_INT1_EVNT_MSK, DISCON_MASK, 16),
+ dump_regmap(HSREWA_INT1_EVNT_MSK, BYPASS_DIS_MASK, 2),
+ dump_regmap(HSREWA_INT1_EVNT_MSK, RET_DIS_MASK, 1),
+ dump_regmap(HSREWA_INT1_EVNT_MSK, RET_EN_MASK, 0),
+};
+
+static const struct debugfs_regmap32 exynos_usb2drd_regmap[] = {
+ dump_regmap(LINKSYSTEM, HOST_SYSTEM_ERR, 31),
+ dump_regmap(LINKSYSTEM, PHY_POWER_DOWN, 30),
+ dump_regmap(LINKSYSTEM, PHY_SW_RESET, 29),
+ dump_regmap(LINKSYSTEM, LINK_SW_RESET, 28),
+ dump_regmap(LINKSYSTEM, XHCI_VERSION_CONTROL, 27),
+ dump_regmap(LINKSYSTEM, FORCE_VBUSVALID, 8),
+ dump_regmap(LINKSYSTEM, FORCE_BVALID, 7),
+ dump_regmap_mask(LINKSYSTEM, FLADJ, 1),
+ dump_regmap(PHYUTMI, UTMI_SUSPEND_COM_N, 12),
+ dump_regmap(PHYUTMI, UTMI_L1_SUSPEND_COM_N, 11),
+ dump_regmap(PHYUTMI, VBUSVLDEXTSEL, 10),
+ dump_regmap(PHYUTMI, VBUSVLDEXT, 9),
+ dump_regmap(PHYUTMI, TXBITSTUFFENH, 8),
+ dump_regmap(PHYUTMI, TXBITSTUFFEN, 7),
+ dump_regmap(PHYUTMI, OTGDISABLE, 6),
+ dump_regmap(PHYUTMI, IDPULLUP, 5),
+ dump_regmap(PHYUTMI, DRVVBUS, 4),
+ dump_regmap(PHYUTMI, DPPULLDOWN, 3),
+ dump_regmap(PHYUTMI, DMPULLDOWN, 2),
+ dump_regmap(PHYUTMI, FORCESUSPEND, 1),
+ dump_regmap(PHYUTMI, FORCESLEEP, 0),
+ dump_regmap(PHYCLKRST, EN_UTMISUSPEND, 31),
+ dump_regmap_mask(PHYCLKRST, SSC_REFCLKSEL, 23),
+ dump_regmap_mask(PHYCLKRST, SSC_RANGE, 21),
+ dump_regmap(PHYCLKRST, SSC_EN, 20),
+ dump_regmap(PHYCLKRST, REF_SSP_EN, 19),
+ dump_regmap(PHYCLKRST, REF_CLKDIV2, 18),
+ dump_regmap_mask(PHYCLKRST, MPLL_MULTIPLIER, 11),
+ dump_regmap_mask(PHYCLKRST, FSEL, 5),
+ dump_regmap(PHYCLKRST, RETENABLEN, 4),
+ dump_regmap_mask(PHYCLKRST, REFCLKSEL, 2),
+ dump_regmap(PHYCLKRST, PORTRESET, 1),
+ dump_regmap(PHYCLKRST, COMMONONN, 0),
+ dump_regmap_mask(PHYPARAM0, TXVREFTUNE, 22),
+ dump_regmap_mask(PHYPARAM0, TXRISETUNE, 20),
+ dump_regmap_mask(PHYPARAM0, TXRESTUNE, 18),
+ dump_regmap(PHYPARAM0, TXPREEMPPULSETUNE, 17),
+ dump_regmap_mask(PHYPARAM0, TXPREEMPAMPTUNE, 15),
+ dump_regmap_mask(PHYPARAM0, TXHSXVTUNE, 13),
+ dump_regmap_mask(PHYPARAM0, TXFSLSTUNE, 9),
+ dump_regmap_mask(PHYPARAM0, SQRXTUNE, 6),
+ dump_regmap_mask(PHYPARAM0, OTGTUNE, 3),
+ dump_regmap_mask(PHYPARAM0, COMPDISTUNE, 0),
+ dump_regmap(PHYPOWERDOWN, VATESTENB, 6),
+ dump_regmap_mask(PHYPOWERDOWN, TEST_BURNIN, 4),
+ dump_regmap(PHYRESUME, BYPASS_SEL, 4),
+ dump_regmap(PHYRESUME, BYPASS_DM_EN, 3),
+ dump_regmap(PHYRESUME, BYPASS_DP_EN, 2),
+ dump_regmap(PHYRESUME, BYPASS_DM_DATA, 1),
+ dump_regmap(PHYRESUME, BYPASS_DP_DATA, 0),
+ dump_regmap_mask(LINKPORT, HOST_NUM_U2_PORT, 9),
+ dump_regmap(LINKPORT, HOST_U2_PORT_DISABLE, 7),
+ dump_regmap(LINKPORT, PORT_POWER_CONTROL, 6),
+ dump_regmap(LINKPORT, HOST_PORT_OVCR_U2, 4),
+ dump_regmap(LINKPORT, HOST_PORT_OVCR_U2_SEL, 2),
+ dump_regmap(LINKPORT, PERM_ATTACH_U2, 0),
+ dump_regmap(HSPHYCTRL, PHYSWRSTALL, 31),
+ dump_regmap(HSPHYCTRL, SIDDQ, 6),
+ dump_regmap(HSPHYCTRL, PHYSWRST, 0),
+ dump_regmap(HSPHYPLLTUNE, PLL_B_TUNE, 6),
+ dump_regmap_mask(HSPHYPLLTUNE, PLL_I_TUNE, 4),
+ dump_regmap_mask(HSPHYPLLTUNE, PLL_P_TUNE, 0),
+};
+
+static const struct debugfs_regmap32 exynos_usb3drd_regmap[] = {
+ dump_regmap(LINKSYSTEM, HOST_SYSTEM_ERR, 31),
+ dump_regmap(LINKSYSTEM, PHY_POWER_DOWN, 30),
+ dump_regmap(LINKSYSTEM, XHCI_VERSION_CONTROL, 27),
+ dump_regmap(LINKSYSTEM, FORCE_VBUSVALID, 8),
+ dump_regmap(LINKSYSTEM, FORCE_BVALID, 7),
+ dump_regmap_mask(LINKSYSTEM, FLADJ, 1),
+ dump_regmap(PHYUTMI, VBUSVLDEXTSEL, 10),
+ dump_regmap(PHYUTMI, VBUSVLDEXT, 9),
+ dump_regmap(PHYUTMI, TXBITSTUFFENH, 8),
+ dump_regmap(PHYUTMI, TXBITSTUFFEN, 7),
+ dump_regmap(PHYUTMI, OTGDISABLE, 6),
+ dump_regmap(PHYUTMI, IDPULLUP, 5),
+ dump_regmap(PHYUTMI, DRVVBUS, 4),
+ dump_regmap(PHYUTMI, DPPULLDOWN, 3),
+ dump_regmap(PHYUTMI, DMPULLDOWN, 2),
+ dump_regmap(PHYUTMI, FORCESUSPEND, 1),
+ dump_regmap(PHYUTMI, FORCESLEEP, 0),
+ dump_regmap(PHYPIPE, PHY_CLOCK_SEL, 4),
+ dump_regmap(PHYCLKRST, EN_UTMISUSPEND, 31),
+ dump_regmap(PHYCLKRST, SSC_EN, 20),
+ dump_regmap(PHYCLKRST, REF_SSP_EN, 19),
+ dump_regmap(PHYCLKRST, REF_CLKDIV2, 18),
+ dump_regmap_mask(PHYCLKRST, MPLL_MULTIPLIER, 11),
+ dump_regmap_mask(PHYCLKRST, FSEL, 5),
+ dump_regmap(PHYCLKRST, RETENABLEN, 4),
+ dump_regmap_mask(PHYCLKRST, REFCLKSEL, 2),
+ dump_regmap(PHYCLKRST, PORTRESET, 1),
+ dump_regmap(PHYCLKRST, COMMONONN, 0),
+ dump_regmap_mask(PHYREG0, SSC_REFCLKSEL, 23),
+ dump_regmap_mask(PHYREG0, SSC_RANGE, 20),
+ dump_regmap(PHYPARAM0, REF_USE_PAD, 31),
+ dump_regmap_mask(PHYPARAM0, REF_LOSLEVEL, 26),
+ dump_regmap_mask(PHYPARAM0, TXVREFTUNE, 22),
+ dump_regmap_mask(PHYPARAM0, TXRISETUNE, 20),
+ dump_regmap_mask(PHYPARAM0, TXRESTUNE, 18),
+ dump_regmap(PHYPARAM0, TXPREEMPPULSETUNE, 17),
+ dump_regmap_mask(PHYPARAM0, TXPREEMPAMPTUNE, 15),
+ dump_regmap_mask(PHYPARAM0, TXHSXVTUNE, 13),
+ dump_regmap_mask(PHYPARAM0, TXFSLSTUNE, 9),
+ dump_regmap_mask(PHYPARAM0, SQRXTUNE, 6),
+ dump_regmap_mask(PHYPARAM0, OTGTUNE, 3),
+ dump_regmap_mask(PHYPARAM0, COMPDISTUNE, 0),
+ dump_regmap_mask(PHYPARAM1, TX0_TERM_OFFSET, 26),
+ dump_regmap_mask(PHYPARAM1, PCS_TXSWING_FULL, 12),
+ dump_regmap_mask(PHYPARAM1, PCS_TXDEEMPH_3P5DB, 0),
+ dump_regmap(PHYTEST, POWERDOWN_SSP, 3),
+ dump_regmap(PHYTEST, POWERDOWN_HSP, 2),
+ dump_regmap(PHYRESUME, BYPASS_SEL, 4),
+ dump_regmap(PHYRESUME, BYPASS_DM_EN, 3),
+ dump_regmap(PHYRESUME, BYPASS_DP_EN, 2),
+ dump_regmap(PHYRESUME, BYPASS_DM_DATA, 1),
+ dump_regmap(PHYRESUME, BYPASS_DP_DATA, 0),
+ dump_regmap_mask(PHYPCSVAL, PCS_RX_LOS_MASK_VAL, 0),
+ dump_regmap_mask(LINKPORT, HOST_NUM_U3_PORT, 13),
+ dump_regmap_mask(LINKPORT, HOST_NUM_U2_PORT, 9),
+ dump_regmap(LINKPORT, HOST_U3_PORT_DISABLE, 8),
+ dump_regmap(LINKPORT, HOST_U2_PORT_DISABLE, 7),
+ dump_regmap(LINKPORT, PORT_POWER_CONTROL, 6),
+ dump_regmap(LINKPORT, HOST_PORT_OVCR_U3, 5),
+ dump_regmap(LINKPORT, HOST_PORT_OVCR_U2, 4),
+ dump_regmap(LINKPORT, HOST_PORT_OVCR_U3_SEL, 3),
+ dump_regmap(LINKPORT, HOST_PORT_OVCR_U2_SEL, 2),
+ dump_regmap(LINKPORT, PERM_ATTACH_U2, 0),
+ dump_regmap_mask(PHYPARAM2, TX_VBOOST_LVL, 3),
+ dump_regmap_mask(PHYPARAM2, LOS_BIAS, 0),
+};
+
+static int debugfs_phy_power_state(struct exynos_usbdrd_phy *phy_drd, int phy_index)
+{
+ struct regmap *reg_pmu;
+ u32 pmu_offset;
+ int phy_on;
+ int ret;
+
+ reg_pmu = phy_drd->phys[phy_index].reg_pmu;
+ pmu_offset = phy_drd->phys[phy_index].pmu_offset;
+ ret = regmap_read(reg_pmu, pmu_offset, &phy_on);
+ if (ret) {
+ dev_err(phy_drd->dev, "Can't read 0x%x\n", pmu_offset);
+ return ret;
+ }
+ phy_on &= phy_drd->phys[phy_index].pmu_mask;
+
+ return phy_on;
+}
+
+static int debugfs_print_regmap(struct seq_file *s, const struct debugfs_regmap32 *regs,
+ int nregs, void __iomem *base,
+ const struct debugfs_reg32 *parent)
+{
+ int i, j = 0;
+ int bit = 0;
+ unsigned int bitmask;
+ int max_string = 24;
+ int calc_tab;
+ u32 bit_value, reg_value;
+
+ reg_value = readl(base + parent->offset);
+ seq_printf(s, "%s (0x%04lx) : 0x%08x\n", parent->name,
+ parent->offset, reg_value);
+ for (i = 0; i < nregs; i++, regs++) {
+ if (!strcmp(regs->name, parent->name)) {
+ bit_value = (reg_value & regs->bitmask) >> regs->bitoffset;
+
+ seq_printf(s, "\t%s", regs->bitname);
+ calc_tab = max_string/8 - strlen(regs->bitname)/8;
+ for (j = 0 ; j < calc_tab; j++)
+ seq_printf(s, "\t");
+
+ if (regs->mask) {
+ bitmask = regs->bitmask;
+ bitmask = bitmask >> regs->bitoffset;
+ while (bitmask) {
+ bitmask = bitmask >> 1;
+ bit++;
+ }
+ seq_printf(s, "[%d:%d]\t: 0x%x\n", (int)regs->bitoffset,
+ ((int)regs->bitoffset + bit - 1), bit_value);
+ bit = 0;
+ } else {
+ seq_printf(s, "[%d]\t: 0x%x\n", (int)regs->bitoffset,
+ bit_value);
+ }
+ }
+ }
+ return 0;
+
+}
+
+static int debugfs_show_regmap(struct seq_file *s, void *data)
+{
+ struct exynos_debugfs_prvdata *prvdata = s->private;
+ struct debugfs_regset_map *regmap = prvdata->regmap;
+ struct debugfs_regset32 *regset = prvdata->regset;
+ const struct debugfs_reg32 *regs = regset->regs;
+ int phy_on, i = 0;
+
+ phy_on = debugfs_phy_power_state(prvdata->phy_drd, 0);
+ if (phy_on < 0) {
+ seq_printf(s, "can't read PHY register, error : %d\n", phy_on);
+ return -EIO;
+ }
+ if (!phy_on) {
+ seq_printf(s, "can't get PHY register, PHY: Power OFF\n");
+ return 0;
+ }
+ for (i = 0; i < regset->nregs; i++, regs++) {
+ debugfs_print_regmap(s, regmap->regs, regmap->nregs,
+ regset->base, regs);
+ }
+
+ return 0;
+}
+
+static int debugfs_open_regmap(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show_regmap, inode->i_private);
+}
+
+static const struct file_operations fops_regmap = {
+ .open = debugfs_open_regmap,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int debugfs_print_regdump(struct seq_file *s, struct exynos_usbdrd_phy *phy_drd,
+ const struct debugfs_reg32 *regs, int nregs,
+ void __iomem *base)
+{
+ int phy_on;
+ int i;
+
+ for (i = 0; i < EXYNOS_DRDPHYS_NUM; i++) {
+ phy_on = debugfs_phy_power_state(phy_drd, i);
+ if (phy_on < 0) {
+ seq_printf(s, "can't read PHY register, error : %d\n", phy_on);
+ return phy_on;
+ }
+ if (!phy_on) {
+ seq_printf(s, "can't get PHY register, PHY%d : Power OFF\n", i);
+ continue;
+ }
+
+ for (i = 0; i < nregs; i++, regs++) {
+ seq_printf(s, "%s", regs->name);
+ if (strlen(regs->name) < 8)
+ seq_printf(s, "\t\t");
+ else
+ seq_printf(s, "\t");
+
+ seq_printf(s, "= 0x%08x\n", readl(base + regs->offset));
+ }
+ }
+
+ return 0;
+}
+static int debugfs_show_regdump(struct seq_file *s, void *data)
+{
+ struct exynos_debugfs_prvdata *prvdata = s->private;
+ struct debugfs_regset32 *regset = prvdata->regset;
+ int ret;
+
+ ret = debugfs_print_regdump(s, prvdata->phy_drd, regset->regs,
+ regset->nregs, regset->base);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int debugfs_open_regdump(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show_regdump, inode->i_private);
+}
+
+static const struct file_operations fops_regdump = {
+ .open = debugfs_open_regdump,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+static int debugfs_show_bitset(struct seq_file *s, void *data)
+{
+ char *b_name = s->private;
+ struct debugfs_regset_map *regmap = prvdata->regmap;
+ const struct debugfs_regmap32 *cmp = regmap->regs;
+ const struct debugfs_regmap32 *regs;
+ unsigned int bitmask;
+ int i, bit = 0;
+ u32 reg_value, bit_value;
+ u32 detect_regs = 0;
+
+ for (i = 0; i < regmap->nregs; i++, cmp++) {
+ if (!strcmp(cmp->bitname, b_name)) {
+ regs = cmp;
+ detect_regs = 1;
+ break;
+ }
+ }
+
+ if (!detect_regs)
+ return -EINVAL;
+
+ reg_value = readl(prvdata->regset->base + regs->offset);
+ bit_value = (reg_value & regs->bitmask) >> regs->bitoffset;
+ if (regs->mask) {
+ bitmask = regs->bitmask;
+ bitmask = bitmask >> regs->bitoffset;
+ while (bitmask) {
+ bitmask = bitmask >> 1;
+ bit++;
+ }
+ seq_printf(s, "%s [%d:%d] = 0x%x\n", regs->name,
+ (int)regs->bitoffset,
+ ((int)regs->bitoffset + bit - 1), bit_value);
+ } else {
+ seq_printf(s, "%s [%d] = 0x%x\n", regs->name,
+ (int)regs->bitoffset, bit_value);
+ }
+ return 0;
+}
+static ssize_t debugfs_write_regset(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ char *reg_name = s->private;
+ struct debugfs_regset32 *regset = prvdata->regset;
+ const struct debugfs_reg32 *regs = regset->regs;
+ unsigned long value;
+ char buf[8];
+ int i;
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ value = simple_strtol(buf, NULL, 16);
+
+ for (i = 0; i < regset->nregs; i++, regs++) {
+ if (!strcmp(regs->name, reg_name))
+ break;
+ }
+
+ writel(value, regset->base + regs->offset);
+
+ return count;
+}
+static ssize_t debugfs_write_bitset(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ char *b_name = s->private;
+ struct debugfs_regset_map *regmap = prvdata->regmap;
+ const struct debugfs_regmap32 *regs = regmap->regs;
+ unsigned long value;
+ char buf[32];
+ int i;
+ u32 reg_value;
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) {
+ seq_printf(s, "%s, write error\n", __func__);
+ return -EFAULT;
+ }
+ value = simple_strtol(buf, NULL, 2);
+
+ for (i = 0; i < regmap->nregs; i++, regs++) {
+ if (!strcmp(regs->bitname, b_name))
+ break;
+ }
+
+ value = value << regs->bitoffset;
+ reg_value = readl(prvdata->regset->base + regs->offset);
+ reg_value &= ~(regs->bitmask);
+ reg_value |= (u32)value;
+ writel(reg_value, prvdata->regset->base + regs->offset);
+
+ return count;
+}
+
+static int debugfs_open_bitset(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show_bitset, inode->i_private);
+}
+
+static int debugfs_show_regset(struct seq_file *s, void *data)
+{
+ char *p_name = s->private;
+ struct debugfs_regset32 *regset = prvdata->regset;
+ struct debugfs_regset_map *regmap = prvdata->regmap;
+ const struct debugfs_reg32 *regs = regset->regs;
+ const struct debugfs_reg32 *parents;
+ u32 detect_regs = 0;
+
+
+ int i;
+
+ for (i = 0; i < regset->nregs; i++, regs++) {
+ if (!strcmp(regs->name, p_name)) {
+ parents = regs;
+ detect_regs = 1;
+ break;
+ }
+ }
+ if (!detect_regs)
+ return -EINVAL;
+
+ debugfs_print_regmap(s, prvdata->regmap->regs, regmap->nregs,
+ regset->base, parents);
+
+ return 0;
+}
+
+static int debugfs_open_regset(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show_regset, inode->i_private);
+}
+
+static const struct file_operations fops_regset = {
+ .open = debugfs_open_regset,
+ .write = debugfs_write_regset,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations fops_bitset = {
+ .open = debugfs_open_bitset,
+ .write = debugfs_write_bitset,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int debugfs_create_regfile(struct exynos_debugfs_prvdata *prvdata,
+ const struct debugfs_reg32 *parents,
+ struct dentry *root)
+{
+ struct debugfs_regset_map *regmap = prvdata->regmap;
+ const struct debugfs_regmap32 *regs = regmap->regs;
+ struct dentry *file;
+ int i, ret;
+
+ file = debugfs_create_file(parents->name, S_IRUGO | S_IWUGO, root,
+ parents->name, &fops_regset);
+ if (!file) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ for (i = 0; i < regmap->nregs; i++, regs++) {
+ if (!strcmp(regs->name, parents->name)) {
+ file = debugfs_create_file(regs->bitname, S_IRUGO | S_IWUGO,
+ root, regs->bitname, &fops_bitset);
+ if (!file) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int debugfs_create_regdir(struct exynos_debugfs_prvdata *prvdata,
+ struct dentry *root)
+{
+ struct exynos_usbdrd_phy *phy_drd = prvdata->phy_drd;
+ struct debugfs_regset32 *regset = prvdata->regset;
+ const struct debugfs_reg32 *regs = regset->regs;
+ struct dentry *dir;
+ int ret, i;
+
+ for (i = 0; i < regset->nregs; i++, regs++) {
+ dir = debugfs_create_dir(regs->name, root);
+ if (!dir) {
+ dev_err(phy_drd->dev, "failed to create '%s' reg dir",
+ regs->name);
+ return -ENOMEM;
+ }
+ ret = debugfs_create_regfile(prvdata, regs, dir);
+ if (ret < 0) {
+ dev_err(phy_drd->dev, "failed to create bitfile for %s, error : %d\n",
+ regs->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+int exynos_usbdrd_debugfs_init(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ struct dentry *root;
+ struct dentry *dir;
+ struct dentry *file;
+ u32 version = phy_drd->usbphy_info.version;
+ int ret;
+
+
+ root = debugfs_create_dir(dev_name(dev), NULL);
+ if (!root) {
+ dev_err(dev, "failed to create root directory for USBPHY debugfs");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ prvdata = devm_kmalloc(dev, sizeof(struct exynos_debugfs_prvdata), GFP_KERNEL);
+ if (!prvdata) {
+ dev_err(dev, "failed to alloc private data for debugfs");
+ ret = -ENOMEM;
+ goto err1;
+ }
+ prvdata->root = root;
+ prvdata->phy_drd = phy_drd;
+
+ prvdata->regset = devm_kmalloc(dev, sizeof(*prvdata->regset), GFP_KERNEL);
+ if (!prvdata->regset) {
+ dev_err(dev, "failed to alloc regmap");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ if ((version >= EXYNOS_USBCON_VER_02_0_0) &&
+ (version <= EXYNOS_USBCON_VER_02_MAX)) {
+ /* for USB2PHY */
+ prvdata->regset->regs = exynos_usb2drd_regs;
+ prvdata->regset->nregs = ARRAY_SIZE(exynos_usb2drd_regs);
+ } else if ((version >= EXYNOS_USBCON_VER_03_0_0) &&
+ (version <= EXYNOS_USBCON_VER_03_MAX)) {
+ /* for USB3PHY Lhotse */
+ prvdata->regset->regs = exynos_usb3drd_phycon_regs;
+ prvdata->regset->nregs = ARRAY_SIZE(exynos_usb3drd_phycon_regs);
+ } else {
+ /* for USB3PHY */
+ prvdata->regset->regs = exynos_usb3drd_regs;
+ prvdata->regset->nregs = ARRAY_SIZE(exynos_usb3drd_regs);
+ }
+ prvdata->regset->base = phy_drd->reg_phy;
+
+ prvdata->regmap = devm_kmalloc(dev, sizeof(*prvdata->regmap), GFP_KERNEL);
+ if (!prvdata->regmap) {
+ dev_err(dev, "failed to alloc regmap");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ if ((version >= EXYNOS_USBCON_VER_02_0_0) &&
+ /* for USB2PHY */
+ (version <= EXYNOS_USBCON_VER_02_MAX)) {
+ prvdata->regmap->regs = exynos_usb2drd_regmap;
+ prvdata->regmap->nregs = ARRAY_SIZE(exynos_usb2drd_regmap);
+ } else if ((version >= EXYNOS_USBCON_VER_03_0_0) &&
+ (version <= EXYNOS_USBCON_VER_03_MAX)) {
+ /* for USB3PHY Lhotse */
+ prvdata->regmap->regs = exynos_usb3drd_phycon_regmap;
+ prvdata->regmap->nregs = ARRAY_SIZE(exynos_usb3drd_phycon_regmap);
+ } else {
+ /* for USB3PHY */
+ prvdata->regmap->regs = exynos_usb3drd_regmap;
+ prvdata->regmap->nregs = ARRAY_SIZE(exynos_usb3drd_regmap);
+ }
+
+ file = debugfs_create_file("regdump", S_IRUGO, root, prvdata, &fops_regdump);
+ if (!file) {
+ dev_err(dev, "failed to create file for register dump");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ file = debugfs_create_file("regmap", S_IRUGO, root, prvdata, &fops_regmap);
+ if (!file) {
+ dev_err(dev, "failed to create file for register dump");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ dir = debugfs_create_dir("regset", root);
+ if (!dir) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ ret = debugfs_create_regdir(prvdata, dir);
+ if (ret < 0) {
+ dev_err(dev, "failed to create regfile, error = %d\n", ret);
+ goto err1;
+ }
+
+
+ return 0;
+
+err1:
+ debugfs_remove_recursive(root);
+err0:
+ return ret;
+}
--- /dev/null
+/*
+ * Samsung EXYNOS SoC series MIPI DISPLAYPORT PHY driver
+ *
+ * Copyright (C) 2016 Samsung Electronics Co., Ltd.
+ * Author: Kwangje Kim <kj1.kim@samsung.com>
+ *
+ * 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 <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#define EXYNOS_DISPLAYPORT_PHY_ISO_BYPASS (1 << 0)
+
+struct exynos_displayport_phy {
+ struct device *dev;
+ spinlock_t slock;
+ void __iomem *regs;
+ struct regmap *reg_pmu;
+ struct displayport_phy_desc {
+ struct phy *phy;
+ unsigned int iso_offset;
+ } phys;
+};
+
+/* 1: Isolation bypass, 0: Isolation enable */
+static int __set_phy_isolation(struct regmap *reg_pmu,
+ unsigned int offset, unsigned int on)
+{
+ unsigned int val;
+ int ret;
+
+ val = on ? EXYNOS_DISPLAYPORT_PHY_ISO_BYPASS : 0;
+
+ ret = regmap_update_bits(reg_pmu, offset,
+ EXYNOS_DISPLAYPORT_PHY_ISO_BYPASS, val);
+
+ pr_debug("%s off=0x%x, val=0x%x\n", __func__, offset, val);
+ return ret;
+}
+
+static const struct of_device_id exynos_displayport_phy_of_table[] = {
+ { .compatible = "samsung,displayport-phy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, exynos_displayport_phy_of_table);
+
+#define to_displayport_phy(desc) \
+ container_of((desc), struct exynos_displayport_phy, phys)
+
+static int exynos_displayport_phy_power_on(struct phy *phy)
+{
+ struct displayport_phy_desc *phy_desc = phy_get_drvdata(phy);
+ struct exynos_displayport_phy *state = to_displayport_phy(phy_desc);
+
+ return __set_phy_isolation(state->reg_pmu, phy_desc->iso_offset, 1);
+}
+
+static int exynos_displayport_phy_power_off(struct phy *phy)
+{
+ struct displayport_phy_desc *phy_desc = phy_get_drvdata(phy);
+ struct exynos_displayport_phy *state = to_displayport_phy(phy_desc);
+
+ return __set_phy_isolation(state->reg_pmu, phy_desc->iso_offset, 0);
+}
+
+static struct phy *exynos_displayport_phy_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct exynos_displayport_phy *state = dev_get_drvdata(dev);
+
+ return state->phys.phy;
+}
+
+static struct phy_ops exynos_displayport_phy_ops = {
+ .power_on = exynos_displayport_phy_power_on,
+ .power_off = exynos_displayport_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int exynos_displayport_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct exynos_displayport_phy *state;
+ struct phy_provider *phy_provider;
+ const struct of_device_id *of_id;
+ struct phy *generic_phy;
+ unsigned int iso[1];
+ unsigned int elements;
+ int ret = 0;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ state->dev = &pdev->dev;
+
+ of_id = of_match_device(of_match_ptr(exynos_displayport_phy_of_table), dev);
+ if (!of_id)
+ return -EINVAL;
+
+ dev_set_drvdata(dev, state);
+ spin_lock_init(&state->slock);
+
+ state->reg_pmu = syscon_regmap_lookup_by_phandle(node,
+ "samsung,pmu-syscon");
+ if (IS_ERR(state->reg_pmu)) {
+ dev_err(dev, "Failed to lookup PMU regmap\n");
+ return PTR_ERR(state->reg_pmu);
+ }
+
+ elements = of_property_count_u32_elems(node, "isolation");
+ ret = of_property_read_u32_array(node, "isolation", iso, elements);
+ if (ret) {
+ dev_err(dev, "cannot get displayport-phy isolation!!!\n");
+ return ret;
+ }
+
+ state->phys.iso_offset = iso[0];
+ dev_dbg(dev, "%s: iso 0x%x\n", __func__, state->phys.iso_offset);
+
+ generic_phy = devm_phy_create(dev, NULL,
+ &exynos_displayport_phy_ops);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(generic_phy);
+ }
+
+ state->phys.phy = generic_phy;
+
+ phy_set_drvdata(generic_phy, &state->phys);
+
+ phy_provider = devm_of_phy_provider_register(dev,
+ exynos_displayport_phy_of_xlate);
+
+ if (IS_ERR(phy_provider))
+ dev_err(dev, "failed to create exynos displayport-phy\n");
+ else
+ dev_err(dev, "Creating exynos-displayport-phy\n");
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static int exynos_displayport_phy_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ dev_info(dev, "%s, successfully removed\n", __func__);
+ return 0;
+}
+
+static struct platform_driver exynos_displayport_phy_driver = {
+ .probe = exynos_displayport_phy_probe,
+ .remove = exynos_displayport_phy_remove,
+ .driver = {
+ .name = "exynos-displayport-phy",
+ .of_match_table = of_match_ptr(exynos_displayport_phy_of_table),
+ .suppress_bind_attrs = true,
+ }
+};
+module_platform_driver(exynos_displayport_phy_driver);
+
+MODULE_DESCRIPTION("Samsung EXYNOS SoC DISPLAYPORT PHY driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Samsung EXYNOS SoC series USB DRD PHY DebugFS file
+ *
+ * Phy provider for USB 3.0 DRD controller on Exynos SoC series
+ *
+ * Copyright (C) 2016 Samsung Electronics Co., Ltd.
+ * Author: Kyounghye Yun <k-hye.yun@samsung.com>
+ *
+ * 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 <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+#include <linux/usb/ch9.h>
+#include "phy-exynos-usbdrd.h"
+#include "phy-exynos-usbdp-reg.h"
+#include "phy-exynos-debug.h"
+
+static struct exynos_debugfs_prvdata *prvdata_dp;
+
+/* PHY Combo DP register set */
+static const struct debugfs_reg32 exynos_usb3drd_dp_regs[] = {
+ dump_register_dp(TRSV_R01),
+ dump_register_dp(TRSV_R02),
+ dump_register_dp(TRSV_R03),
+ dump_register_dp(TRSV_R04),
+ dump_register_dp(TRSV_R0C),
+};
+
+/* PHY Combo DP register set */
+static const struct debugfs_regmap32 exynos_usb3drd_dp_regmap[] = {
+ dump_regmap_dp_mask(TRSV_R01, USBDP_TRSV01, RXAFE_LEQ_ISEL_GEN2, 6),
+ dump_regmap_dp_mask(TRSV_R01, USBDP_TRSV01, RXAFE_LEQ_ISEL_GEN1, 4),
+ dump_regmap_dp_mask(TRSV_R01, USBDP_TRSV01, RXAFE_CTLE_SEL, 2),
+ dump_regmap_dp_mask(TRSV_R01, USBDP_TRSV01, RXAFE_SCLBUF_EN, 0),
+ dump_regmap_dp_mask(TRSV_R02, USBDP_TRSV02, RXAFE_LEQ_CSEL_GEN2, 4),
+ dump_regmap_dp_mask(TRSV_R02, USBDP_TRSV02, RXAFE_LEQ_CSEL_GEN1, 0),
+ dump_regmap_dp_mask(TRSV_R03, USBDP_TRSV03, RXAFE_LEQ_RSEL_GEN2, 3),
+ dump_regmap_dp_mask(TRSV_R03, USBDP_TRSV03, RXAFE_LEQ_RSEL_GEN1, 0),
+ dump_regmap_dp_mask(TRSV_R04, USBDP_TRSV04, RXAFE_SQ_VFFSET_CTRL, 0),
+ dump_regmap_dp_mask(TRSV_R0C, USBDP_TRSV0C, MAN_TX_DE_EMP_LVL, 4),
+ dump_regmap_dp_mask(TRSV_R0C, USBDP_TRSV0C, MAN_TX_DRVR_LVL, 0),
+};
+
+static int debugfs_phy_power_state(struct exynos_usbdrd_phy *phy_drd, int phy_index)
+{
+ struct regmap *reg_pmu;
+ u32 pmu_offset;
+ int phy_on;
+ int ret;
+
+ reg_pmu = phy_drd->phys[phy_index].reg_pmu;
+ pmu_offset = phy_drd->phys[phy_index].pmu_offset;
+ ret = regmap_read(reg_pmu, pmu_offset, &phy_on);
+ if (ret) {
+ dev_err(phy_drd->dev, "Can't read 0x%x\n", pmu_offset);
+ return ret;
+ }
+ phy_on &= phy_drd->phys[phy_index].pmu_mask;
+
+ return phy_on;
+}
+
+static int debugfs_print_regmap(struct seq_file *s, const struct debugfs_regmap32 *regs,
+ int nregs, void __iomem *base,
+ const struct debugfs_reg32 *parent)
+{
+ int i, j = 0;
+ int bit = 0;
+ unsigned int bitmask;
+ int max_string = 24;
+ int calc_tab;
+ u32 bit_value, reg_value;
+
+ reg_value = readl(base + parent->offset);
+ seq_printf(s, "%s (0x%04lx) : 0x%08x\n", parent->name,
+ parent->offset, reg_value);
+ for (i = 0; i < nregs; i++, regs++) {
+ if (!strcmp(regs->name, parent->name)) {
+ bit_value = (reg_value & regs->bitmask) >> regs->bitoffset;
+
+ seq_printf(s, "\t%s", regs->bitname);
+ calc_tab = max_string/8 - strlen(regs->bitname)/8;
+ for (j = 0 ; j < calc_tab; j++)
+ seq_puts(s, "\t");
+
+ if (regs->mask) {
+ bitmask = regs->bitmask;
+ bitmask = bitmask >> regs->bitoffset;
+ while (bitmask) {
+ bitmask = bitmask >> 1;
+ bit++;
+ }
+ seq_printf(s, "[%d:%d]\t: 0x%x\n", (int)regs->bitoffset,
+ ((int)regs->bitoffset + bit - 1), bit_value);
+ bit = 0;
+ } else {
+ seq_printf(s, "[%d]\t: 0x%x\n", (int)regs->bitoffset,
+ bit_value);
+ }
+ }
+ }
+ return 0;
+
+}
+
+static int debugfs_show_regmap(struct seq_file *s, void *data)
+{
+ struct exynos_debugfs_prvdata *prvdata = s->private;
+ struct debugfs_regset_map *regmap = prvdata->regmap;
+ struct debugfs_regset32 *regset = prvdata->regset;
+ const struct debugfs_reg32 *regs = regset->regs;
+ int phy_on, i = 0;
+
+ phy_on = debugfs_phy_power_state(prvdata->phy_drd, 0);
+ if (phy_on < 0) {
+ seq_printf(s, "can't read PHY register, error : %d\n", phy_on);
+ return -EIO;
+ }
+ if (!phy_on) {
+ seq_puts(s, "can't get PHY register, PHY: Power OFF\n");
+ return 0;
+ }
+ for (i = 0; i < regset->nregs; i++, regs++) {
+ debugfs_print_regmap(s, regmap->regs, regmap->nregs,
+ regset->base, regs);
+ }
+
+ return 0;
+}
+
+static int debugfs_open_regmap(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show_regmap, inode->i_private);
+}
+
+static const struct file_operations fops_regmap = {
+ .open = debugfs_open_regmap,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int debugfs_print_regdump(struct seq_file *s, struct exynos_usbdrd_phy *phy_drd,
+ const struct debugfs_reg32 *regs, int nregs,
+ void __iomem *base)
+{
+ int phy_on;
+ int i;
+
+ for (i = 0; i < EXYNOS_DRDPHYS_NUM; i++) {
+ phy_on = debugfs_phy_power_state(phy_drd, i);
+ if (phy_on < 0) {
+ seq_printf(s, "can't read PHY register, error : %d\n", phy_on);
+ return phy_on;
+ }
+ if (!phy_on) {
+ seq_printf(s, "can't get PHY register, PHY%d : Power OFF\n", i);
+ continue;
+ }
+
+ for (i = 0; i < nregs; i++, regs++) {
+ seq_printf(s, "%s", regs->name);
+ if (strlen(regs->name) < 8)
+ seq_puts(s, "\t\t");
+ else
+ seq_puts(s, "\t");
+
+ seq_printf(s, "= 0x%08x\n", readl(base + regs->offset));
+ }
+ }
+
+ return 0;
+}
+static int debugfs_show_regdump(struct seq_file *s, void *data)
+{
+ struct exynos_debugfs_prvdata *prvdata = s->private;
+ struct debugfs_regset32 *regset = prvdata->regset;
+ int ret;
+
+ ret = debugfs_print_regdump(s, prvdata->phy_drd, regset->regs,
+ regset->nregs, regset->base);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int debugfs_open_regdump(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show_regdump, inode->i_private);
+}
+
+static const struct file_operations fops_regdump = {
+ .open = debugfs_open_regdump,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+static int debugfs_show_bitset(struct seq_file *s, void *data)
+{
+ char *b_name = s->private;
+ struct debugfs_regset_map *regmap = prvdata_dp->regmap;
+ const struct debugfs_regmap32 *cmp = regmap->regs;
+ const struct debugfs_regmap32 *regs;
+ unsigned int bitmask;
+ int i, bit = 0;
+ u32 reg_value, bit_value;
+ u32 detect_regs = 0;
+
+ for (i = 0; i < regmap->nregs; i++, cmp++) {
+ if (!strcmp(cmp->bitname, b_name)) {
+ regs = cmp;
+ detect_regs = 1;
+ break;
+ }
+ }
+
+ if (!detect_regs)
+ return -EINVAL;
+
+ reg_value = readl(prvdata_dp->regset->base + regs->offset);
+ bit_value = (reg_value & regs->bitmask) >> regs->bitoffset;
+ if (regs->mask) {
+ bitmask = regs->bitmask;
+ bitmask = bitmask >> regs->bitoffset;
+ while (bitmask) {
+ bitmask = bitmask >> 1;
+ bit++;
+ }
+ seq_printf(s, "%s [%d:%d] = 0x%x\n", regs->name,
+ (int)regs->bitoffset,
+ ((int)regs->bitoffset + bit - 1), bit_value);
+ } else {
+ seq_printf(s, "%s [%d] = 0x%x\n", regs->name,
+ (int)regs->bitoffset, bit_value);
+ }
+ return 0;
+}
+static ssize_t debugfs_write_regset(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ char *reg_name = s->private;
+ struct debugfs_regset32 *regset = prvdata_dp->regset;
+ const struct debugfs_reg32 *regs = regset->regs;
+ unsigned long value;
+ char buf[8];
+ int i;
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+ return -EFAULT;
+
+ value = simple_strtol(buf, NULL, 16);
+
+ for (i = 0; i < regset->nregs; i++, regs++) {
+ if (!strcmp(regs->name, reg_name))
+ break;
+ }
+
+ writel(value, regset->base + regs->offset);
+
+ return count;
+}
+static ssize_t debugfs_write_bitset(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ char *b_name = s->private;
+ struct debugfs_regset_map *regmap = prvdata_dp->regmap;
+ const struct debugfs_regmap32 *regs = regmap->regs;
+ unsigned long value;
+ char buf[32];
+ int i;
+ u32 reg_value;
+
+ if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) {
+ seq_printf(s, "%s, write error\n", __func__);
+ return -EFAULT;
+ }
+ value = simple_strtol(buf, NULL, 2);
+
+ for (i = 0; i < regmap->nregs; i++, regs++) {
+ if (!strcmp(regs->bitname, b_name))
+ break;
+ }
+
+ value = value << regs->bitoffset;
+ reg_value = readl(prvdata_dp->regset->base + regs->offset);
+ reg_value &= ~(regs->bitmask);
+ reg_value |= (u32)value;
+ writel(reg_value, prvdata_dp->regset->base + regs->offset);
+
+ return count;
+}
+
+static int debugfs_open_bitset(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show_bitset, inode->i_private);
+}
+
+static int debugfs_show_regset(struct seq_file *s, void *data)
+{
+ char *p_name = s->private;
+ struct debugfs_regset32 *regset = prvdata_dp->regset;
+ struct debugfs_regset_map *regmap = prvdata_dp->regmap;
+ const struct debugfs_reg32 *regs = regset->regs;
+ const struct debugfs_reg32 *parents;
+ u32 detect_regs = 0;
+
+
+ int i;
+
+ for (i = 0; i < regset->nregs; i++, regs++) {
+ if (!strcmp(regs->name, p_name)) {
+ parents = regs;
+ detect_regs = 1;
+ break;
+ }
+ }
+ if (!detect_regs)
+ return -EINVAL;
+
+ debugfs_print_regmap(s, prvdata_dp->regmap->regs, regmap->nregs,
+ regset->base, parents);
+
+ return 0;
+}
+
+static int debugfs_open_regset(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show_regset, inode->i_private);
+}
+
+static const struct file_operations fops_regset = {
+ .open = debugfs_open_regset,
+ .write = debugfs_write_regset,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations fops_bitset = {
+ .open = debugfs_open_bitset,
+ .write = debugfs_write_bitset,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int debugfs_create_regfile(struct exynos_debugfs_prvdata *prvdata,
+ const struct debugfs_reg32 *parents,
+ struct dentry *root)
+{
+ struct debugfs_regset_map *regmap = prvdata->regmap;
+ const struct debugfs_regmap32 *regs = regmap->regs;
+ struct dentry *file;
+ int i, ret;
+
+ file = debugfs_create_file(parents->name, 0644, root,
+ parents->name, &fops_regset);
+ if (!file) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ for (i = 0; i < regmap->nregs; i++, regs++) {
+ if (!strcmp(regs->name, parents->name)) {
+ file = debugfs_create_file(regs->bitname, 0644,
+ root, regs->bitname, &fops_bitset);
+ if (!file) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int debugfs_create_regdir(struct exynos_debugfs_prvdata *prvdata,
+ struct dentry *root)
+{
+ struct exynos_usbdrd_phy *phy_drd = prvdata->phy_drd;
+ struct debugfs_regset32 *regset = prvdata->regset;
+ const struct debugfs_reg32 *regs = regset->regs;
+ struct dentry *dir;
+ int ret, i;
+
+ for (i = 0; i < regset->nregs; i++, regs++) {
+ dir = debugfs_create_dir(regs->name, root);
+ if (!dir) {
+ dev_err(phy_drd->dev, "failed to create '%s' reg dir",
+ regs->name);
+ return -ENOMEM;
+ }
+ ret = debugfs_create_regfile(prvdata, regs, dir);
+ if (ret < 0) {
+ dev_err(phy_drd->dev, "failed to create bitfile for %s, error : %d\n",
+ regs->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+int exynos_usbdrd_dp_debugfs_init(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ struct dentry *root;
+ struct dentry *dir;
+ struct dentry *file;
+ u32 version = phy_drd->usbphy_sub_info.version;
+ int ret;
+
+ root = debugfs_create_dir("110a0000.usbdp", NULL);
+ if (!root) {
+ dev_err(dev, "failed to create root directory for USBPHY debugfs");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ prvdata_dp = devm_kmalloc(dev, sizeof(struct exynos_debugfs_prvdata), GFP_KERNEL);
+ if (!prvdata_dp) {
+ dev_err(dev, "failed to alloc private data for debugfs");
+ ret = -ENOMEM;
+ goto err1;
+ }
+ prvdata_dp->root = root;
+ prvdata_dp->phy_drd = phy_drd;
+
+ prvdata_dp->regset = devm_kmalloc(dev, sizeof(*prvdata_dp->regset), GFP_KERNEL);
+ if (!prvdata_dp->regset) {
+ dev_err(dev, "failed to alloc regmap");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ if (phy_drd->usbphy_sub_info.version == EXYNOS_USBCON_VER_04_0_0) {
+ /* for USB3PHY Lhotse */
+ prvdata_dp->regset->regs = exynos_usb3drd_dp_regs;
+ prvdata_dp->regset->nregs = ARRAY_SIZE(exynos_usb3drd_dp_regs);
+ }
+
+ prvdata_dp->regset->base = phy_drd->reg_phy2;
+
+ prvdata_dp->regmap = devm_kmalloc(dev, sizeof(*prvdata_dp->regmap), GFP_KERNEL);
+ if (!prvdata_dp->regmap) {
+ dev_err(dev, "failed to alloc regmap");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ if (version == EXYNOS_USBCON_VER_04_0_0) {
+ /* for USB3PHY Lhotse */
+ prvdata_dp->regmap->regs = exynos_usb3drd_dp_regmap;
+ prvdata_dp->regmap->nregs = ARRAY_SIZE(exynos_usb3drd_dp_regmap);
+ }
+
+ file = debugfs_create_file("regdump", 0444, root, prvdata_dp, &fops_regdump);
+ if (!file) {
+ dev_err(dev, "failed to create file for register dump");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ file = debugfs_create_file("regmap", 0444, root, prvdata_dp, &fops_regmap);
+ if (!file) {
+ dev_err(dev, "failed to create file for register dump");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ dir = debugfs_create_dir("regset", root);
+ if (!dir) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ ret = debugfs_create_regdir(prvdata_dp, dir);
+ if (ret < 0) {
+ dev_err(dev, "failed to create regfile, error = %d\n", ret);
+ goto err1;
+ }
+
+
+ return 0;
+
+err1:
+ debugfs_remove_recursive(root);
+err0:
+ return ret;
+}
--- /dev/null
+/*
+ * Samsung EXYNOS SoC series MIPI CSI/DSI D/C-PHY driver
+ *
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * 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 <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <soc/samsung/exynos-pmu.h>
+
+/* the maximum number of PHY for each module */
+#define EXYNOS_MIPI_PHYS_NUM 4
+
+#define EXYNOS_MIPI_PHY_ISO_BYPASS BIT(0)
+
+#define MIPI_PHY_MxSx_UNIQUE (0 << 1)
+#define MIPI_PHY_MxSx_SHARED (1 << 1)
+#define MIPI_PHY_MxSx_INIT_DONE (2 << 1)
+
+enum exynos_mipi_phy_owner {
+ EXYNOS_MIPI_PHY_OWNER_DSIM = 0,
+ EXYNOS_MIPI_PHY_OWNER_CSIS = 1,
+};
+
+/* per MIPI-PHY module */
+struct exynos_mipi_phy_data {
+ u8 flags;
+ int active_count;
+ spinlock_t slock;
+};
+
+#define MKVER(ma, mi) (((ma) << 16) | (mi))
+enum phy_infos {
+ VERSION,
+ TYPE,
+ LANES,
+ SPEED,
+ SETTLE,
+};
+
+struct exynos_mipi_phy_cfg {
+ u16 major;
+ u16 minor;
+ u32 type;
+ /* u32 max_speed */
+ int (*set)(void __iomem *regs, int option, u32 *info);
+};
+
+/* per DT MIPI-PHY node, can have multiple elements */
+struct exynos_mipi_phy {
+ struct device *dev;
+ spinlock_t slock;
+ struct regmap *reg_pmu;
+ struct regmap *reg_reset;
+ enum exynos_mipi_phy_owner owner;
+ struct mipi_phy_desc {
+ struct phy *phy;
+ struct exynos_mipi_phy_data *data;
+ unsigned int index;
+ unsigned int iso_offset;
+ unsigned int rst_bit;
+ void __iomem *regs;
+ } phys[EXYNOS_MIPI_PHYS_NUM];
+};
+
+/* 1: Isolation bypass, 0: Isolation enable */
+static int __set_phy_isolation(struct regmap *reg_pmu,
+ unsigned int offset, unsigned int on)
+{
+ unsigned int val;
+ int ret;
+
+ val = on ? EXYNOS_MIPI_PHY_ISO_BYPASS : 0;
+
+ if (reg_pmu)
+ ret = regmap_update_bits(reg_pmu, offset,
+ EXYNOS_MIPI_PHY_ISO_BYPASS, val);
+ else
+ ret = exynos_pmu_update(offset,
+ EXYNOS_MIPI_PHY_ISO_BYPASS, val);
+
+ if (ret)
+ pr_err("%s failed to %s PHY isolation 0x%x\n",
+ __func__, on ? "set" : "clear", offset);
+
+ pr_debug("%s off=0x%x, val=0x%x\n", __func__, offset, val);
+
+ return ret;
+}
+
+/* 1: Enable reset -> release reset, 0: Enable reset */
+static int __set_phy_reset(struct regmap *reg_reset,
+ unsigned int bit, unsigned int on)
+{
+ unsigned int cfg;
+ int ret = 0;
+
+ if (!reg_reset)
+ return 0;
+
+ ret = regmap_update_bits(reg_reset, 0, BIT(bit), 0);
+ if (ret)
+ pr_err("%s failed to reset PHY(%d)\n", __func__, bit);
+
+ if (on) {
+ ret = regmap_update_bits(reg_reset, 0, BIT(bit), BIT(bit));
+ if (ret)
+ pr_err("%s failed to release reset PHY(%d)\n",
+ __func__, bit);
+ }
+
+ regmap_read(reg_reset, 0, &cfg);
+ pr_debug("%s bit=%d, cfg=0x%x\n", __func__, bit, cfg);
+
+ return ret;
+}
+
+static int __set_phy_init(struct exynos_mipi_phy *state,
+ struct mipi_phy_desc *phy_desc, unsigned int on)
+{
+ unsigned int cfg;
+ int ret = 0;
+
+ if (state->reg_pmu)
+ ret = regmap_read(state->reg_pmu,
+ phy_desc->iso_offset, &cfg);
+ else
+ ret = exynos_pmu_read(phy_desc->iso_offset, &cfg);
+
+ if (ret) {
+ dev_err(state->dev, "%s Can't read 0x%x\n",
+ __func__, phy_desc->iso_offset);
+ ret = -EINVAL;
+ goto phy_exit;
+ }
+
+ /* Add INIT_DONE flag when ISO is already bypass(LCD_ON_UBOOT) */
+ if (cfg && EXYNOS_MIPI_PHY_ISO_BYPASS)
+ phy_desc->data->flags |= MIPI_PHY_MxSx_INIT_DONE;
+
+phy_exit:
+ return ret;
+}
+
+static int __set_phy_alone(struct exynos_mipi_phy *state,
+ struct mipi_phy_desc *phy_desc, unsigned int on)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ if (on) {
+ ret = __set_phy_isolation(state->reg_pmu,
+ phy_desc->iso_offset, on);
+ if (ret)
+ goto phy_exit;
+
+ ret = __set_phy_reset(state->reg_reset,
+ phy_desc->rst_bit, on);
+ } else {
+ ret = __set_phy_reset(state->reg_reset,
+ phy_desc->rst_bit, on);
+ if (ret)
+ goto phy_exit;
+
+ ret = __set_phy_isolation(state->reg_pmu,
+ phy_desc->iso_offset, on);
+ }
+
+phy_exit:
+ pr_debug("%s: isolation 0x%x, reset 0x%x\n", __func__,
+ phy_desc->iso_offset, phy_desc->rst_bit);
+
+ spin_unlock_irqrestore(&state->slock, flags);
+
+ return ret;
+}
+
+static int __set_phy_share(struct exynos_mipi_phy *state,
+ struct mipi_phy_desc *phy_desc, unsigned int on)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&phy_desc->data->slock, flags);
+
+ on ? ++(phy_desc->data->active_count) : --(phy_desc->data->active_count);
+
+ /* If phy is already initialization(power_on) */
+ if (state->owner == EXYNOS_MIPI_PHY_OWNER_DSIM &&
+ phy_desc->data->flags & MIPI_PHY_MxSx_INIT_DONE) {
+ phy_desc->data->flags &= (~MIPI_PHY_MxSx_INIT_DONE);
+ spin_unlock_irqrestore(&phy_desc->data->slock, flags);
+ return ret;
+ }
+
+ if (on) {
+ /* Isolation bypass when reference count is 1 */
+ if (phy_desc->data->active_count) {
+ ret = __set_phy_isolation(state->reg_pmu,
+ phy_desc->iso_offset, on);
+ if (ret)
+ goto phy_exit;
+ }
+
+ ret = __set_phy_reset(state->reg_reset,
+ phy_desc->rst_bit, on);
+ } else {
+ ret = __set_phy_reset(state->reg_reset,
+ phy_desc->rst_bit, on);
+ if (ret)
+ goto phy_exit;
+
+ /* Isolation enabled when reference count is zero */
+ if (phy_desc->data->active_count == 0)
+ ret = __set_phy_isolation(state->reg_pmu,
+ phy_desc->iso_offset, on);
+ }
+
+phy_exit:
+ pr_debug("%s: isolation 0x%x, reset 0x%x\n", __func__,
+ phy_desc->iso_offset, phy_desc->rst_bit);
+
+ spin_unlock_irqrestore(&phy_desc->data->slock, flags);
+
+ return ret;
+}
+
+static int __set_phy_state(struct exynos_mipi_phy *state,
+ struct mipi_phy_desc *phy_desc, unsigned int on)
+{
+ int ret = 0;
+
+ if (phy_desc->data->flags & MIPI_PHY_MxSx_SHARED)
+ ret = __set_phy_share(state, phy_desc, on);
+ else
+ ret = __set_phy_alone(state, phy_desc, on);
+
+ return ret;
+}
+
+static void update_bits(void __iomem *addr, unsigned int start,
+ unsigned int width, unsigned int val)
+{
+ unsigned int cfg;
+ unsigned int mask = (width >= 32) ? 0xffffffff : ((1U << width) - 1);
+
+ cfg = readl(addr);
+ cfg &= ~(mask << start);
+ cfg |= ((val & mask) << start);
+ writel(cfg, addr);
+}
+
+#define PHY_REF_SPEED (1500)
+static int __set_phy_cfg_0501_0000_dphy(void __iomem *regs, int option, u32 *cfg)
+{
+
+ int i;
+ u32 skew_cal_en = 0;
+ u32 skew_delay_sel = 0;
+ u32 hs_mode_sel = 1;
+
+ if (cfg[SPEED] >= PHY_REF_SPEED) {
+ skew_cal_en = 1;
+
+ if (cfg[SPEED] >= 3000)
+ skew_delay_sel = 1;
+ else if (cfg[SPEED] >= 2000)
+ skew_delay_sel = 2;
+ else
+ skew_delay_sel = 3;
+
+ hs_mode_sel = 0;
+ }
+
+ writel(0x2, regs + 0x0018);
+ update_bits(regs + 0x0084, 0, 8, 0x1);
+ for (i = 0; i <= cfg[LANES]; i++) {
+ update_bits(regs + 0x04e0 + (i * 0x400), 0, 1, skew_cal_en);
+ update_bits(regs + 0x043c + (i * 0x400), 5, 2, skew_delay_sel);
+ update_bits(regs + 0x04ac + (i * 0x400), 2, 1, hs_mode_sel);
+ update_bits(regs + 0x04b0 + (i * 0x400), 0, 8, cfg[SETTLE]);
+ }
+
+ return 0;
+}
+
+static int __set_phy_cfg_0502_0000_dphy(void __iomem *regs, int option, u32 *cfg)
+{
+
+ int i;
+ u32 settle_clk_sel = 1;
+ u32 skew_delay_sel = 0;
+
+ if (cfg[SPEED] >= PHY_REF_SPEED)
+ settle_clk_sel = 0;
+
+ if (cfg[SPEED] >= PHY_REF_SPEED && cfg[SPEED] < 4000) {
+ if (cfg[SPEED] >= 3000)
+ skew_delay_sel = 1;
+ else if (cfg[SPEED] >= 2000)
+ skew_delay_sel = 2;
+ else
+ skew_delay_sel = 3;
+ }
+
+ writel(0x00000001, regs + 0x0000); /* SC_GNR_CON0 */
+ writel(0x00001450, regs + 0x0004); /* SC_GNR_CON1 */
+ writel(0x00000004, regs + 0x0008); /* SC_ANA_CON0 */
+ writel(0x00009000, regs + 0x000c); /* SC_ANA_CON1 */
+ writel(0x00000005, regs + 0x0010); /* SC_ANA_CON2 */
+ writel(0x00000600, regs + 0x0014); /* SC_ANA_CON3 */
+ writel(0x00000301, regs + 0x0030); /* SC_TIME_CON0 */
+
+ for (i = 0; i <= cfg[LANES]; i++) {
+ writel(0x00000001, regs + 0x0100 + (i * 0x100)); /* SD_GNR_CON0 */
+ writel(0x00001450, regs + 0x0104 + (i * 0x100)); /* SD_GNR_CON1 */
+ writel(0x00000004, regs + 0x0108 + (i * 0x100)); /* SD_ANA_CON0 */
+ writel(0x00009000, regs + 0x010c + (i * 0x100)); /* SD_ANA_CON1 */
+ writel(0x00000005, regs + 0x0110 + (i * 0x100)); /* SD_ANA_CON2 */
+ update_bits(regs + 0x0110 + (i * 0x100), 8, 2, skew_delay_sel); /* SD_ANA_CON2 */
+ writel(0x00000600, regs + 0x0114 + (i * 0x100)); /* SD_ANA_CON3 */
+ writel(0x00000040, regs + 0x0124 + (i * 0x100)); /* SD_ANA_CON7 */
+ update_bits(regs + 0x0130 + (i * 0x100), 0, 8, cfg[SETTLE]); /* SD_TIME_CON0 */
+ update_bits(regs + 0x0130 + (i * 0x100), 8, 1, settle_clk_sel); /* SD_TIME_CON0 */
+ writel(0x00000003, regs + 0x0134 + (i * 0x100)); /* SD_TIME_CON1 */
+ writel(0x0000081a, regs + 0x0150 + (i * 0x100)); /* SD_DESKEW_CON4 */
+ }
+
+ return 0;
+}
+
+static int __set_phy_cfg_0502_0001_dphy(void __iomem *regs, int option, u32 *cfg)
+{
+
+ int i;
+ u32 settle_clk_sel = 1;
+ u32 skew_delay_sel = 0;
+
+ if (cfg[SPEED] >= PHY_REF_SPEED)
+ settle_clk_sel = 0;
+
+ if (cfg[SPEED] >= PHY_REF_SPEED && cfg[SPEED] < 4000) {
+ if (cfg[SPEED] >= 3000)
+ skew_delay_sel = 1;
+ else if (cfg[SPEED] >= 2000)
+ skew_delay_sel = 2;
+ else
+ skew_delay_sel = 3;
+ }
+
+ writel(0x00000001, regs + 0x0500); /* SC_GNR_CON0 */
+ writel(0x00001450, regs + 0x0504); /* SC_GNR_CON1 */
+ writel(0x00000004, regs + 0x0508); /* SC_ANA_CON0 */
+ writel(0x00009000, regs + 0x050c); /* SC_ANA_CON1 */
+ writel(0x00000005, regs + 0x0510); /* SC_ANA_CON2 */
+ writel(0x00000600, regs + 0x0514); /* SC_ANA_CON3 */
+ writel(0x00000301, regs + 0x0530); /* SC_TIME_CON0 */
+
+ for (i = 0; i <= cfg[LANES]; i++) {
+ writel(0x00000001, regs + 0x0000 + (i * 0x100)); /* SD_GNR_CON0 */
+ writel(0x00001450, regs + 0x0004 + (i * 0x100)); /* SD_GNR_CON1 */
+ writel(0x00000004, regs + 0x0008 + (i * 0x100)); /* SD_ANA_CON0 */
+ writel(0x00009000, regs + 0x000c + (i * 0x100)); /* SD_ANA_CON1 */
+ writel(0x00000005, regs + 0x0010 + (i * 0x100)); /* SD_ANA_CON2 */
+ update_bits(regs + 0x0010 + (i * 0x100), 8, 2, skew_delay_sel); /* SD_ANA_CON2 */
+ writel(0x00000600, regs + 0x0014 + (i * 0x100)); /* SD_ANA_CON3 */
+ writel(0x00000040, regs + 0x0024 + (i * 0x100)); /* SD_ANA_CON7 */
+ update_bits(regs + 0x0030 + (i * 0x100), 0, 8, cfg[SETTLE]); /* SD_TIME_CON0 */
+ update_bits(regs + 0x0030 + (i * 0x100), 8, 1, settle_clk_sel); /* SD_TIME_CON0 */
+ writel(0x00000003, regs + 0x0034 + (i * 0x100)); /* SD_TIME_CON1 */
+ writel(0x0000081a, regs + 0x0050 + (i * 0x100)); /* SD_DESKEW_CON4 */
+ }
+
+ return 0;
+}
+
+static const struct exynos_mipi_phy_cfg phy_cfg_table[] = {
+ {
+ .major = 0x0501,
+ .minor = 0x0000,
+ .type = 0xD,
+ .set = __set_phy_cfg_0501_0000_dphy,
+ },
+ {
+ .major = 0x0502,
+ .minor = 0x0000,
+ .type = 0xD,
+ .set = __set_phy_cfg_0502_0000_dphy,
+ },
+ {
+ .major = 0x0502,
+ .minor = 0x0001,
+ .type = 0xD,
+ .set = __set_phy_cfg_0502_0001_dphy,
+ },
+ { },
+};
+
+static int __set_phy_cfg(struct exynos_mipi_phy *state,
+ struct mipi_phy_desc *phy_desc, int option, void *info)
+{
+ u32 *cfg = (u32 *)info;
+ int i;
+ int ret = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(phy_cfg_table); i++) {
+ if ((cfg[VERSION] == MKVER(phy_cfg_table[i].major,
+ phy_cfg_table[i].minor))
+ && (cfg[TYPE] == phy_cfg_table[i].type)) {
+ ret = phy_cfg_table[i].set(phy_desc->regs,
+ option, cfg);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static struct exynos_mipi_phy_data mipi_phy_m4s4_top = {
+ .flags = MIPI_PHY_MxSx_SHARED,
+ .active_count = 0,
+ .slock = __SPIN_LOCK_UNLOCKED(mipi_phy_m4s4_top.slock),
+};
+
+static struct exynos_mipi_phy_data mipi_phy_m4s4_mod = {
+ .flags = MIPI_PHY_MxSx_SHARED,
+ .active_count = 0,
+ .slock = __SPIN_LOCK_UNLOCKED(mipi_phy_m4s4_mod.slock),
+};
+
+static struct exynos_mipi_phy_data mipi_phy_m4s4s4 = {
+ .flags = MIPI_PHY_MxSx_SHARED,
+ .active_count = 0,
+ .slock = __SPIN_LOCK_UNLOCKED(mipi_phy_m4s4s4.slock),
+};
+
+static struct exynos_mipi_phy_data mipi_phy_m4s0 = {
+ .flags = MIPI_PHY_MxSx_UNIQUE,
+ .active_count = 0,
+ .slock = __SPIN_LOCK_UNLOCKED(mipi_phy_m4s0.slock),
+};
+
+static struct exynos_mipi_phy_data mipi_phy_m2s4s4s2 = {
+ .flags = MIPI_PHY_MxSx_SHARED,
+ .active_count = 0,
+ .slock = __SPIN_LOCK_UNLOCKED(mipi_phy_m2s4s4s2.slock),
+};
+
+static struct exynos_mipi_phy_data mipi_phy_m1s2s2 = {
+ .flags = MIPI_PHY_MxSx_SHARED,
+ .active_count = 0,
+ .slock = __SPIN_LOCK_UNLOCKED(mipi_phy_m1s2s2.slock),
+};
+
+static struct exynos_mipi_phy_data mipi_phy_m0s4s4s4_mod = {
+ .flags = MIPI_PHY_MxSx_SHARED,
+ .active_count = 0,
+ .slock = __SPIN_LOCK_UNLOCKED(mipi_phy_m0s4s4s4.slock),
+};
+
+static const struct of_device_id exynos_mipi_phy_of_table[] = {
+ {
+ .compatible = "samsung,mipi-phy-m4s4-top",
+ .data = &mipi_phy_m4s4_top,
+ },
+ {
+ .compatible = "samsung,mipi-phy-m4s4-mod",
+ .data = &mipi_phy_m4s4_mod,
+ },
+ {
+ .compatible = "samsung,mipi-phy-m4s4s4",
+ .data = &mipi_phy_m4s4s4,
+ },
+ {
+ .compatible = "samsung,mipi-phy-m4s0",
+ .data = &mipi_phy_m4s0,
+ },
+ {
+ .compatible = "samsung,mipi-phy-m2s4s4s2",
+ .data = &mipi_phy_m2s4s4s2,
+ },
+ {
+ .compatible = "samsung,mipi-phy-m1s2s2",
+ .data = &mipi_phy_m1s2s2,
+ },
+ {
+ .compatible = "samsung,mipi-phy-m0s4s4s4-mod",
+ .data = &mipi_phy_m0s4s4s4_mod,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, exynos_mipi_phy_of_table);
+
+#define to_mipi_video_phy(desc) \
+ container_of((desc), struct exynos_mipi_phy, phys[(desc)->index])
+
+static int exynos_mipi_phy_init(struct phy *phy)
+{
+ struct mipi_phy_desc *phy_desc = phy_get_drvdata(phy);
+ struct exynos_mipi_phy *state = to_mipi_video_phy(phy_desc);
+
+ return __set_phy_init(state, phy_desc, 1);
+}
+
+static int exynos_mipi_phy_power_on(struct phy *phy)
+{
+ struct mipi_phy_desc *phy_desc = phy_get_drvdata(phy);
+ struct exynos_mipi_phy *state = to_mipi_video_phy(phy_desc);
+
+ return __set_phy_state(state, phy_desc, 1);
+}
+
+static int exynos_mipi_phy_power_off(struct phy *phy)
+{
+ struct mipi_phy_desc *phy_desc = phy_get_drvdata(phy);
+ struct exynos_mipi_phy *state = to_mipi_video_phy(phy_desc);
+
+ return __set_phy_state(state, phy_desc, 0);
+}
+
+static int exynos_mipi_phy_set(struct phy *phy, int option, void *info)
+{
+ struct mipi_phy_desc *phy_desc = phy_get_drvdata(phy);
+ struct exynos_mipi_phy *state = to_mipi_video_phy(phy_desc);
+
+ return __set_phy_cfg(state, phy_desc, option, info);
+}
+
+static struct phy *exynos_mipi_phy_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct exynos_mipi_phy *state = dev_get_drvdata(dev);
+
+ if (WARN_ON(args->args[0] >= EXYNOS_MIPI_PHYS_NUM))
+ return ERR_PTR(-ENODEV);
+
+ return state->phys[args->args[0]].phy;
+}
+
+static struct phy_ops exynos_mipi_phy_ops = {
+ .init = exynos_mipi_phy_init,
+ .power_on = exynos_mipi_phy_power_on,
+ .power_off = exynos_mipi_phy_power_off,
+ .set = exynos_mipi_phy_set,
+ .owner = THIS_MODULE,
+};
+
+static int exynos_mipi_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct exynos_mipi_phy *state;
+ struct phy_provider *phy_provider;
+ struct exynos_mipi_phy_data *phy_data;
+ const struct of_device_id *of_id;
+ unsigned int iso[EXYNOS_MIPI_PHYS_NUM];
+ unsigned int rst[EXYNOS_MIPI_PHYS_NUM];
+ struct resource *res;
+ unsigned int i;
+ int ret = 0, elements = 0;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ state->dev = &pdev->dev;
+
+ of_id = of_match_device(of_match_ptr(exynos_mipi_phy_of_table), dev);
+ if (!of_id)
+ return -EINVAL;
+
+ phy_data = (struct exynos_mipi_phy_data *)of_id->data;
+
+ dev_set_drvdata(dev, state);
+ spin_lock_init(&state->slock);
+
+ /* PMU isolation (optional) */
+ state->reg_pmu = syscon_regmap_lookup_by_phandle(node,
+ "samsung,pmu-syscon");
+ if (IS_ERR(state->reg_pmu)) {
+ dev_err(dev, "failed to lookup PMU regmap, use PMU interface\n");
+ state->reg_pmu = NULL;
+ }
+
+ elements = of_property_count_u32_elems(node, "isolation");
+ if ((elements < 0) || (elements > EXYNOS_MIPI_PHYS_NUM))
+ return -EINVAL;
+
+ ret = of_property_read_u32_array(node, "isolation", iso,
+ elements);
+ if (ret) {
+ dev_err(dev, "cannot get PHY isolation offset\n");
+ return ret;
+ }
+
+ /* SYSREG reset (optional) */
+ state->reg_reset = syscon_regmap_lookup_by_phandle(node,
+ "samsung,reset-sysreg");
+ if (IS_ERR(state->reg_reset)) {
+ state->reg_reset = NULL;
+ } else {
+ ret = of_property_read_u32_array(node, "reset", rst, elements);
+ if (ret) {
+ dev_err(dev, "cannot get PHY reset bit\n");
+ return ret;
+ }
+ }
+
+ of_property_read_u32(node, "owner", &state->owner);
+
+ for (i = 0; i < elements; i++) {
+ state->phys[i].iso_offset = iso[i];
+ state->phys[i].rst_bit = rst[i];
+ dev_info(dev, "%s: isolation 0x%x\n", __func__,
+ state->phys[i].iso_offset);
+ if (state->reg_reset)
+ dev_info(dev, "%s: reset %d\n", __func__,
+ state->phys[i].rst_bit);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (res) {
+ state->phys[i].regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(state->phys[i].regs))
+ return PTR_ERR(state->phys[i].regs);
+ }
+ }
+
+ for (i = 0; i < elements; i++) {
+ struct phy *generic_phy = devm_phy_create(dev, NULL,
+ &exynos_mipi_phy_ops);
+ if (IS_ERR(generic_phy)) {
+ dev_err(dev, "failed to create PHY\n");
+ return PTR_ERR(generic_phy);
+ }
+
+ state->phys[i].index = i;
+ state->phys[i].phy = generic_phy;
+ state->phys[i].data = phy_data;
+ phy_set_drvdata(generic_phy, &state->phys[i]);
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev,
+ exynos_mipi_phy_of_xlate);
+
+ if (IS_ERR(phy_provider))
+ dev_err(dev, "failed to create exynos mipi-phy\n");
+ else
+ dev_err(dev, "creating exynos-mipi-phy\n");
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver exynos_mipi_phy_driver = {
+ .probe = exynos_mipi_phy_probe,
+ .driver = {
+ .name = "exynos-mipi-phy",
+ .of_match_table = of_match_ptr(exynos_mipi_phy_of_table),
+ .suppress_bind_attrs = true,
+ }
+};
+module_platform_driver(exynos_mipi_phy_driver);
+
+MODULE_DESCRIPTION("Samsung EXYNOS SoC MIPI CSI/DSI D/C-PHY driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * phy-exynos-usb3p1-reg.h
+ *
+ * Created on: Oct 27, 2016
+ * Author: sung-hyun na
+ * jee-woong oh
+ * dae-man ko
+ */
+
+#ifndef DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USB3P1_REG_H_
+#define DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USB3P1_REG_H_
+
+#define EXYNOS_USBCON_CTRL_VER (0x00)
+
+#define EXYNOS_USBCON_LINK_CTRL (0x04)
+#define LINKCTRL_PIPE3_FORCE_RX_ELEC_IDLE (0x1 << 18)
+#define LINKCTRL_PIPE3_FORCE_PHY_STATUS (0x1 << 17)
+#define LINKCTRL_PIPE3_FORCE_EN (0x1 << 16)
+#define LINKCTRL_DIS_QACT_BUSPEND (0x1 << 13)
+#define LINKCTRL_DIS_QACT_LINKGATE (0x1 << 12)
+#define LINKCTRL_DIS_QACT_ID0 (0x1 << 11)
+#define LINKCTRL_DIS_QACT_VBUS_VALID (0x1 << 10)
+#define LINKCTRL_DIS_QACT_BVALID (0x1 << 9)
+#define LINKCTRL_FORCE_QACT (0x1 << 8)
+#define LINKCTRL_BUS_FILTER_BYPASS(_x) ((_x & 0xf) << 4)
+#define LINKCTRL_BUS_FILTER_BYPASS_MASK (0xf << 4)
+#define LINKCTRL_HOST_SYSTEM_ERR (0x1 << 2)
+#define LINKCTRL_LINK_PME (0x1 << 1)
+#define LINKCTRL_PME_GENERATION (0x1 << 0)
+
+#define EXYNOS_USBCON_LINK_PORT (0x08)
+#define LINKPORT_HOST_NUM_U3(_x) ((_x & 0xf) << 16)
+#define LINKPORT_HOST_NUM_U2(_x) ((_x & 0xf) << 12)
+#define LINKPORT_HOST_U3_PORT_DISABLE (0x1 << 9)
+#define LINKPORT_HOST_U2_PORT_DISABLE (0x1 << 8)
+#define LINKPORT_HOST_PORT_POWER_CON_PRESENT (0x1 << 6)
+#define LINKPORT_HUB_PORT_SET_OCD_U3 (0x1 << 5)
+#define LINKPORT_HUB_PORT_SET_OCD_U2 (0x1 << 4)
+#define LINKPORT_HUB_PORT_SEL_OCD_U3 (0x1 << 3)
+#define LINKPORT_HUB_PORT_SEL_OCD_U2 (0x1 << 2)
+#define LINKPORT_HUB_PERM_ATTACH_U3 (0x1 << 1)
+#define LINKPORT_HUB_PERM_ATTACH_U2 (0x1 << 0)
+
+#define EXYNOS_USBCON_LINK_DEBUG_L (0x0C)
+#define EXYNOS_USBCON_LINK_DEBUG_H (0x10)
+
+#define EXYNOS_USBCON_LTSTATE_HIS (0x14)
+#define LTSTATE_LINKTRN_DONE (0x1 << 31)
+#define LTSTATE_HIS4(_x) ((_x & 0xf) << 16)
+#define LTSTATE_HIS3(_x) ((_x & 0xf) << 12)
+#define LTSTATE_HIS2(_x) ((_x & 0xf) << 8)
+#define LTSTATE_HIS1(_x) ((_x & 0xf) << 4)
+#define LTSTATE_HIS0(_x) ((_x & 0xf) << 0)
+
+#define EXYNOS_USBCON_CLKRST (0x20)
+#define CLKRST_USBAUDIO_CLK_GATE_EN (0x1 << 9)
+#define CLKRST_USBAUDIO_CLK_SEL (0x1 << 8)
+#define CLKRST_LINK_PCLK_SEL (0x1 << 7)
+#define CLKRST_PHYCLOCKSEL (0x1 << 6)
+#define CLKRST_PHY30_SW_RST (0x1 << 3)
+#define CLKRST_PHY30_RST_SEL (0x1 << 2)
+#define CLKRST_PHY20_SW_RST (0x1 << 13)
+#define CLKRST_PHY20_RST_SEL (0x1 << 12)
+#define CLKRST_PHY_SW_RST (0x1 << 3)
+#define CLKRST_PHY_RST_SEL (0x1 << 2)
+#define CLKRST_REFCLK_SEL (0x1 << 4)
+#define CLKRST_PORT_RST (0x1 << 1)
+#define CLKRST_LINK_SW_RST (0x1 << 0)
+
+#define EXYNOS_USBCON_PWR (0x24)
+
+#define PWR_PIPE3_POWERDONW (0xf << 4)
+#define PWR_FORCE_POWERDOWN_EN (0x1 << 3)
+#define RSVD1 (0x7 << 0)
+#define PWR_FORCE_POWERDONW (0x1 << 2)
+#define PWR_TEST_POWERDOWN_SSP (0x1 << 1)
+#define PWR_TEST_POWERDOWN_HSP (0x1 << 0)
+
+#define EXYNOS_USBCON_DUALPHYSEL (0x28)
+#define DUALPHYSEL_PHYSEL_CTRL (0x1 << 0)
+#define DUALPHYSEL_PHYSEL_SSPHY (0x1 << 1)
+#define DUALPHYSEL_PHYSEL_PIPECLK (0x1 << 4)
+#define DUALPHYSEL_PHYSEL_PIPERST (0x1 << 8)
+
+#define EXYNOS_USBCON_SSP_PLL (0x30)
+#define SSP_PLL_MPLL_MULTIPLIER_MASK (0x7F << 24)
+#define SSP_PLL_MPLL_MULTIPLIER(_x) ((_x & 0x7f) << 24)
+#define SSP_PLL_SSC_REF_CLK_SEL_MASK (0x1ff << 12)
+#define SSP_PLL_SSC_REF_CLK_SEL(_x) ((_x & 0x1ff) << 12)
+#define SSP_PLL_SSC_EN (0x1 << 11)
+#define SSP_PLL_SSC_RANGE_MASK (0x7 << 8)
+#define SSP_PLL_SSC_RANGE(_x) ((_x & 0x7) << 8)
+#define SSP_PLL_REF_SSP_EN (0x1 << 7)
+#define SSP_PLL_REF_CLKDIV2 (0x1 << 6)
+#define SSP_PLL_FSEL_MASK (0x3f << 0)
+#define SSP_PLL_FSEL(_x) ((_x & 0x3f) << 0)
+
+#define EXYNOS_USBCON_SSP_PARACON0 (0x34)
+#define SSP_PARACON0_TX0_TERM_OFFSET_MASK (0x1f << 25)
+#define SSP_PARACON0_TX0_TERM_OFFSET(_x) ((_x & 0x1f) << 25)
+#define SSP_PARACON0_PCS_TX_SWING_FULL_MASK (0x7f << 16)
+#define SSP_PARACON0_PCS_TX_SWING_FULL(_x) ((_x & 0x7f) << 16)
+#define SSP_PARACON0_PCS_TX_DEEMPH_6DB_MASK (0x3f << 8)
+#define SSP_PARACON0_PCS_TX_DEEMPH_6DB(_x) ((_x & 0x3f) << 8)
+#define SSP_PARACON0_PCS_TX_DEEMPH_3P5DB_MASK (0x3f << 0)
+#define SSP_PARACON0_PCS_TX_DEEMPH_3P5DB(_x) ((_x & 0x3f) << 0)
+
+#define EXYNOS_USBCON_SSP_PARACON1 (0x38)
+#define SSP_PARACON1_TX_VBOOST_LVL_SSTX_MASK (0x7 << 28)
+#define SSP_PARACON1_TX_VBOOST_LVL_SSTX(_x) ((_x & 0x7) << 28)
+#define SSP_PARACON1_TX_VBOOST_LVL_MASK (0x7 << 24)
+#define SSP_PARACON1_TX_VBOOST_LVL(_x) ((_x & 0x7) << 24)
+#define SSP_PARACON1_LOS_LEVEL_MASK (0x1f << 16)
+#define SSP_PARACON1_LOS_LEVEL(_x) ((_x & 0x1f) << 16)
+#define SSP_PARACON1_LOS_BIAS_MASK (0x7 << 12)
+#define SSP_PARACON1_LOS_BIAS(_x) ((_x & 0x7) << 12)
+#define SSP_PARACON1_PCS_RX_LOS_MASK_VAL_MASK (0x3ff << 0)
+#define SSP_PARACON1_PCS_RX_LOS_MASK_VAL(_x) ((_x & 0x3ff) << 0)
+
+#define EXYNOS_USBCON_SSP_TEST (0x3C)
+#define SSP_TEST_TX_EYE_HEIGHT_CNTL_EN_MASK (0x1 << 28)
+#define SSP_TEST_TX_EYE_HEIGHT_CNTL_EN(_x) ((_x & 0x1) << 28)
+#define SSP_TEST_PIPE_TX_DEEMPH_UPDATE_DELAY_MASK (0xf << 24)
+#define SSP_TEST_PIPE_TX_DEEMPH_UPDATE_DELAY(_x) ((_x & 0xf) << 24)
+#define SSP_TEST_PCS_TX_SWING_FULL_SSTX_MASK (0x7f << 16)
+#define SSP_TEST_PCS_TX_SWING_FULL_SSTX(_x) ((_x & 0x7f) << 16)
+#define SSP_TEST_RTUNE_ACK (0x1 << 3)
+#define SSP_TEST_RTUNE_REQ (0x1 << 2)
+#define SSP_TEST_LANE0_TX2RX_LOOPBK (0x1 << 1)
+#define SSP_TEST_LOOPBACKENB (0x1 << 0)
+
+#define EXYNOS_USBCON_SSP_CRCTL0 (0x40)
+#define SSP_CCTRL0_CR_DATA_IN_MASK (0xffffU << 16)
+#define SSP_CCTRL0_CR_DATA_IN(_x) ((_x & 0xffffU) << 16)
+#define SSP_CRCTRL0_CR_WRITE (0x1 << 3)
+#define SSP_CRCTRL0_CR_READ (0x1 << 2)
+#define SSP_CRCTRL0_CR_CAP_DATA (0x1 << 1)
+#define SSP_CRCTRL0_CR_CAP_ADDR (0x1 << 0)
+
+#define EXYNOS_USBCON_SSP_CRCTL1 (0x44)
+#define SSP_CRCTL1_CR_DATA_OUT_MASK (0xffffU << 16)
+#define SSP_CRCTL1_CR_DATA_OUT(_x) ((_x & 0xffffU) << 16)
+#define SSP_CRCTL1_CR_ACK (0x1 << 0)
+
+#define EXYNOS_USBCON_COMBO_PMA_CTRL (0x48)
+/* S5E9820 added */
+#define PMA_REF_SOC_PLL_SSC (0x1 << 16)
+#define PMA_ROPLL_REF_REQ_MASK (0x3 << 12)
+#define PMA_ROPLL_REF_REQ_SET(_x) ((_x & 0x3) << 12)
+#define PMA_ROPLL_REF_REQ_GET(_x) ((_x & (0x3 << 12)) >> 12)
+/* S5E9820 added */
+#define PMA_PLL_REF_REQ_MASK (0x3 << 10)
+#define PMA_PLL_REF_REQ_SET(_x) ((_x & 0x3) << 10)
+#define PMA_PLL_REF_REQ_GET(_x) ((_x & (0x3 << 10)) >> 10)
+#define PMA_REF_FREQ_MASK (0x3 << 8)
+#define PMA_REF_FREQ_SET(_x) ((_x & 0x3) << 8)
+#define PMA_REF_FREQ_GET(_x) ((_x & (0x3 << 8)) >> 8)
+#define PMA_LOW_PWRN (0x1 << 4)
+#define PMA_TRSV_SW_RST (0x1 << 3)
+#define PMA_CMN_SW_RST (0x1 << 2)
+#define PMA_INIT_SW_RST (0x1 << 1)
+#define PMA_APB_SW_RST (0x1 << 0)
+
+
+#define EXYNOS_USBCON_UTMI (0x50)
+#define UTMI_OPMODE_CTRL_EN (0x1 << 8)
+#define UTMI_FORCE_OPMODE_MASK (0x3 << 6)
+#define UTMI_FORCE_OPMODE_SET(_x) ((_x & 0x3) << 6)
+#define UTMI_FORCE_VBUSVALID (0x1 << 5)
+#define UTMI_FORCE_BVALID (0x1 << 4)
+#define UTMI_DP_PULLDOWN (0x1 << 3)
+#define UTMI_DM_PULLDOWN (0x1 << 2)
+#define UTMI_FORCE_SUSPEND (0x1 << 1)
+#define UTMI_FORCE_SLEEP (0x1 << 0)
+
+#define EXYNOS_USBCON_HSP (0x54)
+#define HSP_AUTORSM_ENB (0x1 << 29)
+#define HSP_RETENABLE_EN (0x1 << 28)
+#define HSP_FSLS_SPEED_SEL (0x1 << 25)
+#define HSP_FSV_OUT_EN (0x1 << 24)
+#define HSP_HS_XCVR_EXT_CTL (0x1 << 22)
+#define HSP_HS_RXDAT (0x1 << 21)
+#define HSP_HS_SQUELCH (0x1 << 20)
+#define HSP_FSVMINUS (0x1 << 17)
+#define HSP_FSVPLUS (0x1 << 16)
+#define HSP_FSVPLUS_GET(_x) ((_x & (0x1 << 16)) >> 16)
+#define HSP_VBUSVLDEXTSEL (0x1 << 13)
+#define HSP_VBUSVLDEXT (0x1 << 12)
+#define HSP_EN_UTMISUSPEND (0x1 << 9)
+#define HSP_COMMONONN (0x1 << 8)
+#define HSP_VATESTENB (0x1 << 6)
+#define HSP_CHGDET (0x1 << 5)
+#define HSP_CHGDET_GET(_x) ((_x & (0x1 << 5)) >> 5)
+#define HSP_VDATSRCENB (0x1 << 4)
+#define HSP_VDATDETENB (0x1 << 3)
+#define HSP_CHRGSEL (0x1 << 2)
+#define HSP_ACAENB (0x1 << 1)
+#define HSP_DCDENB (0x1 << 0)
+
+#define EXYNOS_USBCON_HSP_TUNE (0x58)
+#define HSP_TUNE_TXVREF_MASK ((unsigned) 0xf << 28)
+#define HSP_TUNE_TXVREF_SET(_x) ((unsigned) (_x & 0xf) << 28)
+#define HSP_TUNE_TXVREF_GET(_x) ((_x & (0xfU << 28)) >> 28)
+#define HSP_TUNE_TXRISE_MASK (0x3 << 24)
+#define HSP_TUNE_TXRISE_SET(_x) ((_x & 0x3) << 24)
+#define HSP_TUNE_TXRISE_GET(_x) ((_x & (0x3 << 24)) >> 24)
+#define HSP_TUNE_TXRES_MASK (0x3 << 21)
+#define HSP_TUNE_TXRES_SET(_x) ((_x & 0x3) << 21)
+#define HSP_TUNE_TXRES_GET(_x) ((_x & (0x3 << 21)) >> 21)
+#define HSP_TUNE_TXPREEMPA_PLUS (0x1 << 20)
+#define HSP_TUNE_TXPREEMPA_PLUS_GET(_x) ((_x & (0x1 << 20)) >> 20)
+#define HSP_TUNE_TXPREEMPA_MASK (0x3 << 18)
+#define HSP_TUNE_TXPREEMPA_SET(_x) ((_x & 0x3) << 18)
+#define HSP_TUNE_TXPREEMPA_GET(_x) ((_x & (0x3 << 18)) >> 18)
+#define HSP_TUNE_HSXV_MASK (0x3 << 16)
+#define HSP_TUNE_HSXV_SET(_x) ((_x & 0x3) << 16)
+#define HSP_TUNE_HSXV_GET(_x) ((_x & (0x3 << 16)) >> 16)
+#define HSP_TUNE_TXFSLS_MASK (0xf << 12)
+#define HSP_TUNE_TXFSLS_SET(_x) ((_x & 0xf) << 12)
+#define HSP_TUNE_TXFSLS_GET(_x) ((_x & (0xf << 12)) >> 12)
+#define HSP_TUNE_SQRX_MASK (0x7 << 8)
+#define HSP_TUNE_SQRX_SET(_x) ((_x & 0x7) << 8)
+#define HSP_TUNE_SQRX_GET(_x) ((_x & (0x7 << 8)) >> 8)
+#define HSP_TUNE_OTG_MASK (0x7 << 4)
+#define HSP_TUNE_OTG_SET(_x) ((_x & 0x7) << 4)
+#define HSP_TUNE_OTG_GET(_x) ((_x & (0x7 << 4)) >> 4)
+#define HSP_TUNE_COMPDIS_MASK (0x7 << 0)
+#define HSP_TUNE_COMPDIS_SET(_x) ((_x & 0x7) << 0)
+#define HSP_TUNE_COMPDIS_GET(_x) ((_x & (0x7 << 0)) >> 0)
+
+#define EXYNOS_USBCON_HSP_TEST (0x5c)
+#define HSP_TEST_HS_RXDAT (0x1 << 26)
+#define HSP_TEST_HS_SQUELCH (0x1 << 25)
+#define HSP_TEST_SIDDQ (0x1 << 24)
+#define HSP_TEST_LINESTATE_MASK (0x3 << 20)
+#define HSP_TEST_LINESTATE_SET(_x) ((_x & 0x3) << 20)
+#define HSP_TEST_LINESTATE_GET(_x) ((_x & (0x3 << 20)) >> 20)
+#define HSP_TEST_DATA_OUT_MASK (0xf << 16)
+#define HSP_TEST_DATA_OUT_SET(_x) ((_x & 0xf) << 16)
+#define HSP_TEST_DATA_OUT_GET(_x) ((_x & (0xf << 16)) >> 16)
+#define HSP_TEST_CLK (0x1 << 13)
+#define HSP_TEST_DATA_OUT_SEL (0x1 << 12)
+#define HSP_TEST_DATA_ADDR_MASK (0xf << 8)
+#define HSP_TEST_DATA_ADDR_SET(_x) ((_x & 0xf) << 8)
+#define HSP_TEST_DATA_ADDR_GET(_x) ((_x & (0xf << 8)) >> 8)
+#define HSP_TEST_DATA_IN_MASK (0xff << 0)
+#define HSP_TEST_DATA_IN_SET(_x) ((_x & 0xff) << 0)
+#define HSP_TEST_DATA_IN_GET(_x) ((_x & (0xff << 0)) >> 0)
+
+#define EXYNOS_USBCON_HSP_PLL_TUNE (0x60)
+#define HSP_PLL_BTUNE (0x1 << 8)
+#define HSP_PLL_ITUNE_MASK (0x3 << 4)
+#define HSP_PLL_ITUNE_IN_SET(_x) ((_x & 0x3) << 4)
+#define HSP_PLL_ITUNE_IN_GET(_x) ((_x & (0x3 << 4)) >> 4)
+#define HSP_PLL_PTUNE_MASK (0xf << 0)
+#define HSP_PLL_PTUNE_IN_SET(_x) ((_x & 0xf) << 0)
+#define HSP_PLL_PTUNE_IN_GET(_x) ((_x & (0xf << 0)) >> 0)
+
+/* Remote Wake-up Advisro (ReWA) */
+#define EXYNOS_USBCON_REWA_ENABLE (0x100)
+#define REWA_ENABLE_SS_REWA_EN (0x1 << 8)
+#define REWA_ENABLE_HS_REWA_EN (0x1 << 0)
+
+#define EXYNOS_USBCON_HSREWA_INTR (0x104)
+#define HSREWA_INTR_WAKEUP_REQ_MASK (0x1 << 12)
+#define HSREWA_INTR_TIMEOUT_MASK (0x1 << 8)
+#define HSREWA_INTR_EVT_MASK (0x1 << 4)
+#define HSREWA_INTR_WAKEUP_MASK (0x1 << 0)
+
+#define EXYNOS_USBCON_HSREWA_CTRL (0x108)
+#define HSREWA_CTRL_DIG_BYPASS_CON_EN (0x1 << 28)
+#define HSREWA_CTRL_DPDM_MON_SEL (0x1 << 24)
+#define HSREWA_CTRL_HS_LINK_READY (0x1 << 20)
+#define HSREWA_CTRL_HS_SYS_VALID (0x1 << 16)
+#define HSREWA_CTRL_HS_REWA_ERR (0x1 << 4)
+#define HSREWA_CTRL_HS_REWA_DONE (0x1 << 0)
+
+#define EXYNOS_USBCON_HSREWA_REFTO (0x10C)
+
+#define EXYNOS_USBCON_HSREWA_HSTK (0x110)
+
+#define EXYNOS_USBCON_HSREWA_CNT (0x114)
+
+#define EXYNOS_USBCON_HSREWA_INT1_EVT (0x118)
+#define HSREWA_CTRL_HS_EVT_ERR_SUS (0x1 << 18)
+#define HSREWA_CTRL_HS_EVT_ERR_DEV_K (0x1 << 17)
+#define HSREWA_CTRL_HS_EVT_DISCON (0x1 << 16)
+#define HSREWA_CTRL_HS_EVT_BYPASS_DIS (0x1 << 2)
+#define HSREWA_CTRL_HS_EVT_RET_DIS (0x1 << 1)
+#define HSREWA_CTRL_HS_EVT_RET_EN (0x1 << 0)
+
+#define EXYNOS_USBCON_HSREWA_INT1_MASK (0x11C)
+
+
+#endif /* DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USB3P1_REG_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#ifdef __KERNEL__
+
+#ifndef __EXCITE__
+#include <linux/delay.h>
+#include <linux/io.h>
+#endif
+
+#include <linux/platform_device.h>
+
+#else
+
+#include "types.h"
+#ifndef __BOOT__
+#include "customfunctions.h"
+#include "mct.h"
+#else
+#include <string.h>
+#include <util.h>
+#include <pwm.h>
+#endif
+
+#endif
+
+#include "phy-samsung-usb-cal.h"
+
+#include "phy-exynos-usb3p1.h"
+#include "phy-exynos-usb3p1-reg.h"
+//#include "../../../../include/usb/usb_config.h"
+
+static int exynos_usb3p1_get_tune_param(struct exynos_usbphy_info *info,
+ char *param_name)
+{
+ int cnt, ret;
+ char *name;
+
+ if (!info->tune_param)
+ return -1;
+
+ for (cnt = 0, ret = -1;
+ info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST;
+ cnt++) {
+ name = info->tune_param[cnt].name;
+ if (strcmp(name, param_name))
+ continue;
+ ret = info->tune_param[cnt].value;
+ break;
+ }
+
+ return ret;
+}
+
+#if !defined(CONFIG_BOARD_ZEBU)
+static void exynos_cal_usbphy_q_ch(void *regs_base, u8 enable)
+{
+ u32 phy_resume;
+
+ if (enable) {
+ /* WA for Q-channel: disable all q-act from usb */
+ phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
+ phy_resume |= LINKCTRL_DIS_QACT_ID0;
+ phy_resume |= LINKCTRL_DIS_QACT_VBUS_VALID;
+ phy_resume |= LINKCTRL_DIS_QACT_BVALID;
+ phy_resume |= LINKCTRL_DIS_QACT_LINKGATE;
+ phy_resume &= ~LINKCTRL_FORCE_QACT;
+ udelay(500);
+ writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL);
+ udelay(500);
+ phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
+ phy_resume |= LINKCTRL_FORCE_QACT;
+ udelay(500);
+ writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL);
+ } else {
+ phy_resume = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
+ phy_resume &= ~LINKCTRL_FORCE_QACT;
+ phy_resume |= LINKCTRL_DIS_QACT_ID0;
+ phy_resume |= LINKCTRL_DIS_QACT_VBUS_VALID;
+ phy_resume |= LINKCTRL_DIS_QACT_BVALID;
+ phy_resume |= LINKCTRL_DIS_QACT_LINKGATE;
+ writel(phy_resume, regs_base + EXYNOS_USBCON_LINK_CTRL);
+ }
+}
+#endif
+
+static void link_vbus_filter_en(struct exynos_usbphy_info *info,
+ u8 enable)
+{
+ u32 phy_resume;
+
+ phy_resume = readl(info->regs_base + EXYNOS_USBCON_LINK_CTRL);
+ if (enable)
+ phy_resume &= ~LINKCTRL_BUS_FILTER_BYPASS_MASK;
+ else
+ phy_resume |= LINKCTRL_BUS_FILTER_BYPASS(0xf);
+ writel(phy_resume, info->regs_base + EXYNOS_USBCON_LINK_CTRL);
+}
+
+static void phy_power_en(struct exynos_usbphy_info *info, u8 en)
+{
+ u32 reg;
+ int main_version;
+
+ main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
+ if (main_version == EXYNOS_USBCON_VER_05_0_0) {
+ void *__iomem reg_base;
+
+ if (info->used_phy_port == 1)
+ reg_base = info->regs_base_2nd;
+ else
+ reg_base = info->regs_base;
+
+ /* 3.0 PHY Power Down control */
+ reg = readl(reg_base + EXYNOS_USBCON_PWR);
+ if (en) {
+ /* apply to KC asb vector */
+ if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
+ reg &= ~(PWR_PIPE3_POWERDONW);
+ reg &= ~(PWR_FORCE_POWERDOWN_EN);
+ } else {
+ reg &= ~(PWR_TEST_POWERDOWN_HSP);
+ reg &= ~(PWR_TEST_POWERDOWN_SSP);
+ writel(reg, reg_base + EXYNOS_USBCON_PWR);
+ udelay(1000);
+ reg |= (PWR_TEST_POWERDOWN_HSP);
+ }
+ } else {
+ if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
+ reg |= (PWR_PIPE3_POWERDONW);
+ reg |= (PWR_FORCE_POWERDOWN_EN);
+ } else {
+ reg |= (PWR_TEST_POWERDOWN_SSP);
+ }
+ }
+ writel(reg, reg_base + EXYNOS_USBCON_PWR);
+
+ } else if (main_version == EXYNOS_USBCON_VER_03_0_0) {
+ /* 2.0 PHY Power Down Control */
+ reg = readl(info->regs_base + EXYNOS_USBCON_HSP_TEST);
+ if (en)
+ reg &= ~HSP_TEST_SIDDQ;
+ else
+ reg |= HSP_TEST_SIDDQ;
+ writel(reg, info->regs_base + EXYNOS_USBCON_HSP_TEST);
+ }
+}
+
+static void phy_sw_rst_high(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ int main_version;
+ u32 clkrst;
+
+ main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
+ if ((main_version == EXYNOS_USBCON_VER_05_0_0) &&
+ (info->used_phy_port == 1))
+ regs_base = info->regs_base_2nd;
+
+ clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST);
+ if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
+ clkrst |= CLKRST_PHY20_SW_RST;
+ clkrst |= CLKRST_PHY20_RST_SEL;
+ } else {
+ clkrst |= CLKRST_PHY_SW_RST;
+ clkrst |= CLKRST_PHY_RST_SEL;
+ clkrst |= CLKRST_PORT_RST;
+ }
+ writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST);
+}
+
+static void phy_sw_rst_low(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ int main_version;
+ u32 clkrst;
+
+ main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
+ if ((main_version == EXYNOS_USBCON_VER_05_0_0) &&
+ (info->used_phy_port == 1))
+ regs_base = info->regs_base_2nd;
+
+ clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST);
+ if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
+ clkrst |= CLKRST_PHY20_RST_SEL;
+ clkrst &= ~CLKRST_PHY20_SW_RST;
+ clkrst &= ~CLKRST_PORT_RST;
+
+ } else {
+ clkrst |= CLKRST_PHY_RST_SEL;
+ clkrst &= ~CLKRST_PHY_SW_RST;
+ clkrst &= ~CLKRST_PORT_RST;
+
+ }
+ writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST);
+}
+
+void phy_exynos_usb_v3p1_pma_ready(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+
+ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ reg &= ~PMA_LOW_PWRN;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+
+ udelay(1);
+
+ reg |= PMA_APB_SW_RST;
+ reg |= PMA_INIT_SW_RST;
+ reg |= PMA_CMN_SW_RST;
+ reg |= PMA_TRSV_SW_RST;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+
+ udelay(1);
+
+ reg &= ~PMA_APB_SW_RST;
+ reg &= ~PMA_INIT_SW_RST;
+ reg &= ~PMA_PLL_REF_REQ_MASK;
+ reg &= ~PMA_REF_FREQ_MASK;
+ reg |= PMA_REF_FREQ_SET(0x1);
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+
+ reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
+ reg &= ~LINKCTRL_PIPE3_FORCE_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL);
+}
+/* S5E9820 - SS GEN2 PMA INIT */
+void phy_exynos_usb_v3p1_g2_pma_ready(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+
+ /* Change pipe pclk to pipe3 */
+ reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
+ reg |= CLKRST_LINK_PCLK_SEL;
+ writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
+
+ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ reg &= ~PMA_ROPLL_REF_REQ_MASK;
+ reg &= ~PMA_ROPLL_REF_REQ_MASK;
+ reg &= ~PMA_PLL_REF_REQ_MASK;
+ reg |= PMA_REF_FREQ_SET(1);
+ reg |= PMA_LOW_PWRN;
+ reg |= PMA_TRSV_SW_RST;
+ reg |= PMA_CMN_SW_RST;
+ reg |= PMA_INIT_SW_RST;
+ reg |= PMA_APB_SW_RST;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ pr_info("clkrst = 0x%x\n", reg);
+
+ udelay(1);
+ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ reg &= ~PMA_LOW_PWRN;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ udelay(1);
+
+ // release overide
+ reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
+ reg &= ~LINKCTRL_PIPE3_FORCE_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL);
+
+ udelay(1);
+
+ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ reg &= ~PMA_APB_SW_RST;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+}
+
+
+void phy_exynos_usb_v3p1_pma_sw_rst_release(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+
+ /* Reset Release for USB/DP PHY */
+ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ reg &= ~PMA_CMN_SW_RST;
+ reg &= ~PMA_TRSV_SW_RST;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+
+ udelay(1000);
+
+ /* Change pipe pclk to pipe3 */
+ reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
+ reg |= CLKRST_LINK_PCLK_SEL;
+ writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
+}
+
+void phy_exynos_usb_v3p1_g2_pma_sw_rst_release(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+
+ /* Reset Release for USB/DP PHY */
+ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ reg &= ~PMA_INIT_SW_RST;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+
+ udelay(1); // Spec : wait for 200ns
+
+ /* run pll */
+ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ reg &= ~PMA_TRSV_SW_RST;
+ reg &= ~PMA_CMN_SW_RST;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+
+ udelay(1000);
+
+ /* Change pipe pclk to pipe3 */
+ /* add by LT case */
+ reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
+ reg |= CLKRST_LINK_PCLK_SEL;
+ writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
+
+}
+
+void phy_exynos_usb_v3p1_pipe_ready(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+
+ reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
+ reg &= ~LINKCTRL_PIPE3_FORCE_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL);
+
+ reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
+ reg &= ~CLKRST_LINK_PCLK_SEL;
+ writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
+}
+
+void phy_exynos_usb_v3p1_pipe_ovrd(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+
+ /* force pipe3 signal for link */
+ reg = readl(regs_base + EXYNOS_USBCON_LINK_CTRL);
+ reg |= LINKCTRL_PIPE3_FORCE_EN;
+ reg &= ~LINKCTRL_PIPE3_FORCE_PHY_STATUS;
+ reg |= LINKCTRL_PIPE3_FORCE_RX_ELEC_IDLE;
+ writel(reg, regs_base + EXYNOS_USBCON_LINK_CTRL);
+
+ /* PMA Disable */
+ reg = readl(regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+ reg |= PMA_LOW_PWRN;
+ writel(reg, regs_base + EXYNOS_USBCON_COMBO_PMA_CTRL);
+}
+
+void phy_exynos_usb3p1_rewa_ready(struct exynos_usbphy_info *info);
+void phy_exynos_usb_v3p1_late_enable(struct exynos_usbphy_info *info);
+
+void phy_exynos_usb_v3p1_enable(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+ u32 reg_hsp;
+ bool ss_only_cap;
+ int main_version;
+
+ main_version = info->version & EXYNOS_USBCON_VER_MAJOR_VER_MASK;
+ ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
+
+ if (main_version == EXYNOS_USBCON_VER_03_0_0) {
+#if !defined(CONFIG_BOARD_ZEBU)
+ /* Set force q-channel */
+ exynos_cal_usbphy_q_ch(regs_base, 1);
+#endif
+
+#ifndef __BOOT__
+ /* Link Reset */
+ if (main_version == EXYNOS_USBCON_VER_03_0_0) {
+ reg = readl(info->regs_base + EXYNOS_USBCON_CLKRST);
+ reg |= CLKRST_LINK_SW_RST;
+ writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
+
+ udelay(10);
+
+ reg &= ~CLKRST_LINK_SW_RST;
+ writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
+ }
+#endif
+ }
+
+ /* Set PHY POR High */
+ phy_sw_rst_high(info);
+
+ if (!ss_only_cap) {
+ reg = readl(regs_base + EXYNOS_USBCON_UTMI);
+ reg &= ~UTMI_FORCE_SUSPEND;
+ reg &= ~UTMI_FORCE_SLEEP;
+ reg &= ~UTMI_DP_PULLDOWN;
+ reg &= ~UTMI_DM_PULLDOWN;
+ writel(reg, regs_base + EXYNOS_USBCON_UTMI);
+
+ /* set phy clock & control HS phy */
+ reg = readl(regs_base + EXYNOS_USBCON_HSP);
+
+ if (info->common_block_disable) {
+ reg |= HSP_EN_UTMISUSPEND;
+ reg |= HSP_COMMONONN;
+ } else
+ reg &= ~HSP_COMMONONN;
+ writel(reg, regs_base + EXYNOS_USBCON_HSP);
+ } else {
+ void *ss_reg_base;
+
+ if (info->used_phy_port == 1)
+ ss_reg_base = info->regs_base_2nd;
+ else
+ ss_reg_base = info->regs_base;
+
+ /* Change pipe pclk to pipe3 */
+ reg = readl(ss_reg_base + EXYNOS_USBCON_CLKRST);
+ reg |= CLKRST_LINK_PCLK_SEL;
+ writel(reg, ss_reg_base + EXYNOS_USBCON_CLKRST);
+ }
+ udelay(100);
+
+ /* Follow setting sequence for USB Link */
+ /* 1. Set VBUS Valid and DP-Pull up control
+ * by VBUS pad usage */
+ link_vbus_filter_en(info, false);
+ reg = readl(regs_base + EXYNOS_USBCON_UTMI);
+ reg_hsp = readl(regs_base + EXYNOS_USBCON_HSP);
+ reg |= UTMI_FORCE_BVALID;
+ reg |= UTMI_FORCE_VBUSVALID;
+ reg_hsp |= HSP_VBUSVLDEXTSEL;
+ reg_hsp |= HSP_VBUSVLDEXT;
+
+ writel(reg, regs_base + EXYNOS_USBCON_UTMI);
+ writel(reg_hsp, regs_base + EXYNOS_USBCON_HSP);
+
+ /* Set PHY tune para */
+ phy_exynos_usb_v3p1_tune(info);
+
+ /* Enable PHY Power Mode */
+ phy_power_en(info, 1);
+
+ /* before POR low, 10us delay is needed. */
+ udelay(10);
+
+ /* Set PHY POR Low */
+ phy_sw_rst_low(info);
+
+ /* after POR low and delay 75us, PHYCLOCK is guaranteed. */
+ udelay(75);
+
+ if (ss_only_cap) {
+ phy_exynos_usb_v3p1_late_enable(info);
+ return;
+ }
+
+ /* Select PHY MUX */
+ if (info->dual_phy) {
+ u32 physel;
+
+ physel = readl(regs_base + EXYNOS_USBCON_DUALPHYSEL);
+ if (info->used_phy_port == 0) {
+ physel &= ~DUALPHYSEL_PHYSEL_CTRL;
+ physel &= ~DUALPHYSEL_PHYSEL_SSPHY;
+ physel &= ~DUALPHYSEL_PHYSEL_PIPECLK;
+ physel &= ~DUALPHYSEL_PHYSEL_PIPERST;
+ } else {
+ physel |= DUALPHYSEL_PHYSEL_CTRL;
+ physel |= DUALPHYSEL_PHYSEL_SSPHY;
+ physel |= DUALPHYSEL_PHYSEL_PIPECLK;
+ physel |= DUALPHYSEL_PHYSEL_PIPERST;
+ }
+ writel(physel, regs_base + EXYNOS_USBCON_DUALPHYSEL);
+ }
+
+ /* 2. OVC io usage */
+ reg = readl(regs_base + EXYNOS_USBCON_LINK_PORT);
+ if (info->use_io_for_ovc) {
+ reg &= ~LINKPORT_HUB_PORT_SEL_OCD_U3;
+ reg &= ~LINKPORT_HUB_PORT_SEL_OCD_U2;
+ } else {
+ reg |= LINKPORT_HUB_PORT_SEL_OCD_U3;
+ reg |= LINKPORT_HUB_PORT_SEL_OCD_U2;
+ }
+ writel(reg, regs_base + EXYNOS_USBCON_LINK_PORT);
+
+ /* Enable ReWA */
+ if (info->hs_rewa)
+ phy_exynos_usb3p1_rewa_ready(info);
+}
+
+enum exynos_usbcon_cr {
+ USBCON_CR_ADDR = 0,
+ USBCON_CR_DATA = 1,
+ USBCON_CR_READ = 18,
+ USBCON_CR_WRITE = 19,
+};
+
+static u16 phy_exynos_usb_v3p1_cr_access(struct exynos_usbphy_info *info,
+ enum exynos_usbcon_cr cr_bit, u16 data)
+{
+ void __iomem *base;
+
+ u32 ssp_crctl0 = 0;
+ u32 ssp_crctl1 = 0;
+ u32 loop;
+ u32 loop_cnt;
+
+ if (info->used_phy_port != -1) {
+ if (info->used_phy_port == 0)
+ base = info->regs_base;
+ else
+ base = info->regs_base_2nd;
+ } else
+ base = info->regs_base;
+
+ /*Clear CR port register*/
+ ssp_crctl0 = readl(base + EXYNOS_USBCON_SSP_CRCTL0);
+ ssp_crctl0 &= ~(0xf);
+ ssp_crctl0 &= ~(0xffffU << 16);
+ writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0);
+
+ /*Set Data for cr port*/
+ ssp_crctl0 &= ~SSP_CCTRL0_CR_DATA_IN_MASK;
+ ssp_crctl0 |= SSP_CCTRL0_CR_DATA_IN(data);
+ writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0);
+
+ if (cr_bit == USBCON_CR_ADDR)
+ loop = 1;
+ else
+ loop = 2;
+
+ for (loop_cnt = 0; loop_cnt < loop; loop_cnt++) {
+ u32 trigger_bit = 0;
+ u32 handshake_cnt = 2;
+ /* Trigger cr port */
+ if (cr_bit == USBCON_CR_ADDR)
+ trigger_bit = SSP_CRCTRL0_CR_CAP_ADDR;
+ else {
+ if (loop_cnt == 0)
+ trigger_bit = SSP_CRCTRL0_CR_CAP_DATA;
+ else {
+ if (cr_bit == USBCON_CR_READ)
+ trigger_bit = SSP_CRCTRL0_CR_READ;
+ else
+ trigger_bit = SSP_CRCTRL0_CR_WRITE;
+ }
+ }
+ /* Handshake Procedure */
+ do {
+ u32 usec = 100;
+ if (handshake_cnt == 2)
+ ssp_crctl0 |= trigger_bit;
+ else
+ ssp_crctl0 &= ~trigger_bit;
+ writel(ssp_crctl0, base + EXYNOS_USBCON_SSP_CRCTL0);
+
+ /* Handshake */
+ do {
+ ssp_crctl1 = readl(base + EXYNOS_USBCON_SSP_CRCTL1);
+ if ((handshake_cnt == 2) && (ssp_crctl1 & SSP_CRCTL1_CR_ACK))
+ break;
+ else if ((handshake_cnt == 1) && !(ssp_crctl1 & SSP_CRCTL1_CR_ACK))
+ break;
+
+ udelay(1);
+ } while (usec-- > 0);
+
+#ifndef __BOOT__
+ if (!usec)
+ pr_err("CRPORT handshake timeout1 (0x%08x)\n", ssp_crctl0);
+#endif
+
+ udelay(5);
+ handshake_cnt--;
+ } while (handshake_cnt != 0);
+ udelay(50);
+
+ }
+ return (u16) ((ssp_crctl1 & SSP_CRCTL1_CR_DATA_OUT_MASK) >> 16);
+}
+
+void phy_exynos_usb_v3p1_cal_cr_write(struct exynos_usbphy_info *info, u16 addr, u16 data)
+{
+ phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_ADDR, addr);
+ phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_WRITE, data);
+}
+
+u16 phy_exynos_usb_v3p1_cal_cr_read(struct exynos_usbphy_info *info, u16 addr)
+{
+ phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_ADDR, addr);
+ return phy_exynos_usb_v3p1_cr_access(info, USBCON_CR_READ, 0);
+}
+
+void phy_exynos_usb_v3p1_cal_usb3phy_tune_fix_rxeq(struct exynos_usbphy_info *info)
+{
+ u16 reg;
+ int rxeq_val;
+
+ rxeq_val = exynos_usb3p1_get_tune_param(info, "rx_eq_fix_val");
+ if (rxeq_val == -1)
+ return;
+
+ reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006);
+ reg &= ~(1 << 6);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
+
+ udelay(10);
+
+ reg |= (1 << 7);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
+
+ udelay(10);
+
+ reg &= ~(0x7 << 0x8);
+ reg |= ((rxeq_val & 0x7) << 8);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
+
+ udelay(10);
+
+ reg |= (1 << 11);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
+#ifndef __BOOT__
+ pr_info("Reg RX_OVRD_IN_HI : 0x%x\n",
+ phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006));
+ pr_info("Reg RX_CDR_CDR_FSM_DEBUG : 0x%x\n",
+ phy_exynos_usb_v3p1_cal_cr_read(info, 0x101c));
+#endif
+}
+
+static void set_ss_tx_impedance(struct exynos_usbphy_info *info)
+{
+ u16 rtune_debug, tx_ovrd_in_hi;
+ u8 tx_imp;
+
+ /* obtain calibration code for 45Ohm impedance */
+ rtune_debug = phy_exynos_usb_v3p1_cal_cr_read(info, 0x3);
+ /* set SUP.DIG.RTUNE_DEBUG.TYPE = 2 */
+ rtune_debug &= ~(0x3 << 3);
+ rtune_debug |= (0x2 << 3);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
+
+ /* read SUP.DIG.RTUNE_STAT (0x0004[9:0]) */
+ tx_imp = phy_exynos_usb_v3p1_cal_cr_read(info, 0x4);
+ /* current_tx_cal_code[9:0] = SUP.DIG.RTUNE_STAT (0x0004[9:0]) */
+ tx_imp += 8;
+ /* tx_cal_code[9:0] = current_tx_cal_code[9:0] + 8(decimal)
+ * NOTE, max value is 63;
+ * i.e. if tx_cal_code[9:0] > 63, tx_cal_code[9:0]==63; */
+ if (tx_imp > 63)
+ tx_imp = 63;
+
+ /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET_OVRD = 1 */
+ tx_ovrd_in_hi = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1001);
+ tx_ovrd_in_hi |= (1 << 7);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi);
+
+ /* SUP.DIG.RTUNE_DEBUG.MAN_TUNE = 0 */
+ rtune_debug &= ~(1 << 1);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
+
+ /* set SUP.DIG.RTUNE_DEBUG.VALUE = tx_cal_code[9:0] */
+ rtune_debug &= ~(0x1ff << 5);
+ rtune_debug |= (tx_imp << 5);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
+
+ /* set SUP.DIG.RTUNE_DEBUG.SET_VAL = 1 */
+ rtune_debug |= (1 << 2);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
+
+ /* set SUP.DIG.RTUNE_DEBUG.SET_VAL = 0 */
+ rtune_debug &= ~(1 << 2);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x3, rtune_debug);
+
+ /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET = 1 */
+ tx_ovrd_in_hi |= (1 << 6);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi);
+
+ /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET = 0 */
+ tx_ovrd_in_hi &= ~(1 << 6);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1001, tx_ovrd_in_hi);
+}
+
+void phy_exynos_usb_v3p1_cal_usb3phy_tune_adaptive_eq(
+ struct exynos_usbphy_info *info, u8 eq_fix)
+{
+ u16 reg;
+
+ reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006);
+ if (eq_fix) {
+ reg |= (1 << 6);
+ reg &= ~(1 << 7);
+ } else {
+ reg &= ~(1 << 6);
+ reg |= (1 << 7);
+ }
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
+}
+
+void phy_exynos_usb_v3p1_usb3phy_tune_chg_rxeq(
+ struct exynos_usbphy_info *info, u8 eq_val)
+{
+ u16 reg;
+
+ reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x1006);
+ reg &= ~(0x7 << 0x8);
+ reg |= ((eq_val & 0x7) << 8);
+ reg |= (1 << 11);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1006, reg);
+}
+
+enum exynos_usbphy_tif {
+ USBCON_TIF_RD_STS,
+ USBCON_TIF_RD_OVRD,
+ USBCON_TIF_WR_OVRD,
+};
+
+static u8 phy_exynos_usb_v3p1_tif_access(struct exynos_usbphy_info *info,
+ enum exynos_usbphy_tif access_type, u8 addr, u8 data)
+{
+ void __iomem *base;
+ u32 hsp_test;
+
+ base = info->regs_base;
+ hsp_test = readl(base + EXYNOS_USBCON_HSP_TEST);
+ /* Set TEST DATA OUT SEL */
+ if (access_type == USBCON_TIF_RD_STS)
+ hsp_test &= ~HSP_TEST_DATA_OUT_SEL;
+ else
+ hsp_test |= HSP_TEST_DATA_OUT_SEL;
+ hsp_test &= ~HSP_TEST_DATA_IN_MASK;
+ hsp_test &= ~HSP_TEST_DATA_ADDR_MASK;
+ hsp_test |= HSP_TEST_DATA_ADDR_SET(addr);
+ writel(hsp_test, base + EXYNOS_USBCON_HSP_TEST);
+
+ udelay(10);
+
+ hsp_test = readl(base + EXYNOS_USBCON_HSP_TEST);
+ if (access_type != USBCON_TIF_WR_OVRD)
+ return HSP_TEST_DATA_OUT_GET(hsp_test);
+
+ hsp_test |= HSP_TEST_DATA_IN_SET((data | 0xf0));
+ hsp_test |= HSP_TEST_CLK;
+ writel(hsp_test, base + EXYNOS_USBCON_HSP_TEST);
+
+ udelay(10);
+
+ hsp_test = readl(base + EXYNOS_USBCON_HSP_TEST);
+ hsp_test &= ~HSP_TEST_CLK;
+ writel(hsp_test, base + EXYNOS_USBCON_HSP_TEST);
+
+ hsp_test = readl(base + EXYNOS_USBCON_HSP_TEST);
+ return HSP_TEST_DATA_OUT_GET(hsp_test);
+}
+
+u8 phy_exynos_usb_v3p1_tif_ov_rd(struct exynos_usbphy_info *info, u8 addr)
+{
+ return phy_exynos_usb_v3p1_tif_access(info, USBCON_TIF_RD_OVRD, addr, 0x0);
+}
+
+u8 phy_exynos_usb_v3p1_tif_ov_wr(struct exynos_usbphy_info *info, u8 addr, u8 data)
+{
+ return phy_exynos_usb_v3p1_tif_access(info, USBCON_TIF_WR_OVRD, addr, data);
+}
+
+u8 phy_exynos_usb_v3p1_tif_sts_rd(struct exynos_usbphy_info *info, u8 addr)
+{
+ return phy_exynos_usb_v3p1_tif_access(info, USBCON_TIF_RD_STS, addr, 0x0);
+}
+
+void phy_exynos_usb_v3p1_late_enable(struct exynos_usbphy_info *info)
+{
+ u32 version = info->version;
+
+
+ if (version > EXYNOS_USBCON_VER_05_0_0) {
+ int tune_val;
+ u16 cr_reg;
+
+ /*Set RXDET_MEAS_TIME[11:4] each reference clock*/
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1010, 0x80);
+
+ tune_val = exynos_usb3p1_get_tune_param(info, "rx_decode_mode");
+ if (tune_val != -1)
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1026, 0x1);
+
+ tune_val = exynos_usb3p1_get_tune_param(info, "cr_lvl_ctrl_en");
+ if (tune_val != -1) {
+ /* Enable override los_bias, los_level and
+ * tx_vboost_lvl, Set los_bias to 0x5 and
+ * los_level to 0x9 */
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x15, 0xA409);
+ /* Set TX_VBOOST_LEVLE to tune->tx_boost_level */
+ tune_val = exynos_usb3p1_get_tune_param(info, "tx_vboost_lvl");
+ if (tune_val != -1) {
+ cr_reg = phy_exynos_usb_v3p1_cal_cr_read(info, 0x12);
+ cr_reg &= ~(0x7 << 13);
+ cr_reg |= ((tune_val & 0x7) << 13);
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x12, cr_reg);
+ }
+ // Set swing_full to LANEN_DIG_TX_OVRD_DRV_LO
+ cr_reg = 0x4000;
+ tune_val = exynos_usb3p1_get_tune_param(info, "pcs_tx_deemph_3p5db");
+ if (tune_val != -1) {
+ cr_reg |= (tune_val << 7);
+ tune_val = exynos_usb3p1_get_tune_param(info, "pcs_tx_swing_full");
+ if (tune_val != -1)
+ cr_reg |= tune_val;
+ else
+ cr_reg = 0;
+ } else
+ cr_reg = 0;
+ if (cr_reg)
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x1002, cr_reg);
+ }
+ /* to set the charge pump proportional current */
+ tune_val = exynos_usb3p1_get_tune_param(info, "mpll_charge_pump");
+ if (tune_val != -1)
+ phy_exynos_usb_v3p1_cal_cr_write(info, 0x30, 0xC0);
+
+ tune_val = exynos_usb3p1_get_tune_param(info, "rx_eq_fix_val");
+ if (tune_val != -1)
+ phy_exynos_usb_v3p1_cal_usb3phy_tune_fix_rxeq(info);
+
+ tune_val = exynos_usb3p1_get_tune_param(info, "decrese_ss_tx_imp");
+ if (tune_val != -1)
+ set_ss_tx_impedance(info);
+ }
+}
+
+void phy_exynos_usb_v3p1_disable(struct exynos_usbphy_info *info)
+{
+ u32 reg;
+ void __iomem *regs_base = info->regs_base;
+
+ /* set phy clock & control HS phy */
+ reg = readl(regs_base + EXYNOS_USBCON_UTMI);
+ reg |= UTMI_FORCE_SUSPEND;
+ reg |= UTMI_FORCE_SLEEP;
+ writel(reg, regs_base + EXYNOS_USBCON_UTMI);
+
+ /* Disable PHY Power Mode */
+ phy_power_en(info, 0);
+
+#if !defined(CONFIG_BOARD_ZEBU)
+ /* clear force q-channel */
+ exynos_cal_usbphy_q_ch(regs_base, 0);
+#endif
+}
+
+u64 phy_exynos_usb3p1_get_logic_trace(struct exynos_usbphy_info *info)
+{
+ u64 ret;
+ void __iomem *regs_base = info->regs_base;
+
+ ret = readl(regs_base + EXYNOS_USBCON_LINK_DEBUG_L);
+ ret |= ((u64) readl(regs_base + EXYNOS_USBCON_LINK_DEBUG_H)) << 32;
+
+ return ret;
+}
+
+void phy_exynos_usb3p1_pcs_reset(struct exynos_usbphy_info *info)
+{
+ u32 clkrst;
+ void __iomem *regs_base = info->regs_base;
+
+ clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST);
+ if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
+ /* TODO : How to pcs reset
+ * clkrst |= CLKRST_PHY30_RST_SEL;
+ * clkrst |= CLKRST_PHY30_SW_RST;
+ */
+ } else {
+ clkrst |= CLKRST_PHY_SW_RST;
+ clkrst |= CLKRST_PHY_RST_SEL;
+ }
+ writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST);
+
+ udelay(1);
+
+ clkrst = readl(regs_base + EXYNOS_USBCON_CLKRST);
+ if (EXYNOS_USBCON_VER_MINOR(info->version) >= 0x1) {
+ clkrst |= CLKRST_PHY30_RST_SEL;
+ clkrst &= ~CLKRST_PHY30_SW_RST;
+ } else {
+ clkrst |= CLKRST_PHY_RST_SEL;
+ clkrst &= ~CLKRST_PHY_SW_RST;
+ }
+ writel(clkrst, regs_base + EXYNOS_USBCON_CLKRST);
+}
+
+void phy_exynos_usb_v3p1_enable_dp_pullup(struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 phyutmi;
+
+ phyutmi = readl(regs_base + EXYNOS_USBCON_HSP);
+ phyutmi |= HSP_VBUSVLDEXT;
+ writel(phyutmi, regs_base + EXYNOS_USBCON_HSP);
+}
+
+void phy_exynos_usb_v3p1_disable_dp_pullup(struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 phyutmi;
+
+ phyutmi = readl(regs_base + EXYNOS_USBCON_HSP);
+ phyutmi &= ~HSP_VBUSVLDEXT;
+ writel(phyutmi, regs_base + EXYNOS_USBCON_HSP);
+}
+
+void phy_exynos_usb_v3p1_config_host_mode(struct exynos_usbphy_info *info)
+{
+ u32 reg;
+ void __iomem *regs_base = info->regs_base;
+
+ /* DP/DM Pull Down Control */
+ reg = readl(regs_base + EXYNOS_USBCON_UTMI);
+ reg |= UTMI_DP_PULLDOWN;
+ reg |= UTMI_DM_PULLDOWN;
+ reg &= ~UTMI_FORCE_BVALID;
+ reg &= ~UTMI_FORCE_VBUSVALID;
+ writel(reg, regs_base + EXYNOS_USBCON_UTMI);
+
+ /* Disable Pull-up Register */
+ reg = readl(regs_base + EXYNOS_USBCON_HSP);
+ reg &= ~HSP_VBUSVLDEXTSEL;
+ reg &= ~HSP_VBUSVLDEXT;
+ writel(reg, regs_base + EXYNOS_USBCON_HSP);
+}
+
+void phy_exynos_usb_v3p1_tune(struct exynos_usbphy_info *info)
+{
+ u32 hsp_tune, ssp_tune0, ssp_tune1, ssp_tune2, cnt;
+
+ bool ss_only_cap;
+
+ ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
+
+ if (!info->tune_param)
+ return;
+
+ if (!ss_only_cap) {
+ /* hsphy tuning */
+ void __iomem *regs_base = info->regs_base;
+
+ hsp_tune = readl(regs_base + EXYNOS_USBCON_HSP_TUNE);
+
+ cnt = 0;
+ for (; info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST; cnt++) {
+ char *para_name;
+ int val;
+
+ val = info->tune_param[cnt].value;
+ if (val == -1)
+ continue;
+ para_name = info->tune_param[cnt].name;
+ if (!strcmp(para_name, "compdis")) {
+ hsp_tune &= ~HSP_TUNE_COMPDIS_MASK;
+ hsp_tune |= HSP_TUNE_COMPDIS_SET(val);
+ } else if (!strcmp(para_name, "otg")) {
+ hsp_tune &= ~HSP_TUNE_OTG_MASK;
+ hsp_tune |= HSP_TUNE_OTG_SET(val);
+ } else if (!strcmp(para_name, "rx_sqrx")) {
+ hsp_tune &= ~HSP_TUNE_SQRX_MASK;
+ hsp_tune |= HSP_TUNE_SQRX_SET(val);
+ } else if (!strcmp(para_name, "tx_fsls")) {
+ hsp_tune &= ~HSP_TUNE_TXFSLS_MASK;
+ hsp_tune |= HSP_TUNE_TXFSLS_SET(val);
+ } else if (!strcmp(para_name, "tx_hsxv")) {
+ hsp_tune &= ~HSP_TUNE_HSXV_MASK;
+ hsp_tune |= HSP_TUNE_HSXV_SET(val);
+ } else if (!strcmp(para_name, "tx_pre_emp")) {
+ hsp_tune &= ~HSP_TUNE_TXPREEMPA_MASK;
+ hsp_tune |= HSP_TUNE_TXPREEMPA_SET(val);
+ } else if (!strcmp(para_name, "tx_pre_emp_plus")) {
+ if (val)
+ hsp_tune |= HSP_TUNE_TXPREEMPA_PLUS;
+ else
+ hsp_tune &= ~HSP_TUNE_TXPREEMPA_PLUS;
+ } else if (!strcmp(para_name, "tx_res")) {
+ hsp_tune &= ~HSP_TUNE_TXRES_MASK;
+ hsp_tune |= HSP_TUNE_TXRES_SET(val);
+ } else if (!strcmp(para_name, "tx_rise")) {
+ hsp_tune &= ~HSP_TUNE_TXRISE_MASK;
+ hsp_tune |= HSP_TUNE_TXRISE_SET(val);
+ } else if (!strcmp(para_name, "tx_vref")) {
+ hsp_tune &= ~HSP_TUNE_TXVREF_MASK;
+ hsp_tune |= HSP_TUNE_TXVREF_SET(val);
+ } else if (!strcmp(para_name, "tx_res_ovrd"))
+ phy_exynos_usb_v3p1_tif_ov_wr(info, 0x6, val);
+ }
+ writel(hsp_tune, regs_base + EXYNOS_USBCON_HSP_TUNE);
+ } else {
+ /* ssphy tuning */
+ void __iomem *ss_reg_base;
+
+ if (info->used_phy_port == 1)
+ ss_reg_base = info->regs_base_2nd;
+ else
+ ss_reg_base = info->regs_base;
+
+ ssp_tune0 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON0);
+ ssp_tune1 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON1);
+ ssp_tune2 = readl(ss_reg_base + EXYNOS_USBCON_SSP_TEST);
+
+ cnt = 0;
+ for (; info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST; cnt++) {
+ char *para_name;
+ int val;
+
+ val = info->tune_param[cnt].value;
+ if (val == -1)
+ continue;
+ para_name = info->tune_param[cnt].name;
+ if (!strcmp(para_name, "pcs_tx_swing_full")) {
+ ssp_tune0 &= ~SSP_PARACON0_PCS_TX_SWING_FULL_MASK;
+ ssp_tune0 |= SSP_PARACON0_PCS_TX_SWING_FULL(val);
+ } else if (!strcmp(para_name, "pcs_tx_deemph_6db")) {
+ ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_6DB_MASK;
+ ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_6DB(val);
+ } else if (!strcmp(para_name, "pcs_tx_deemph_3p5db")) {
+ ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_3P5DB_MASK;
+ ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_3P5DB(val);
+ } else if (!strcmp(para_name, "tx_vboost_lvl_sstx")) {
+ ssp_tune1 &= ~SSP_PARACON1_TX_VBOOST_LVL_SSTX_MASK;
+ ssp_tune1 |= SSP_PARACON1_TX_VBOOST_LVL_SSTX(val);
+ } else if (!strcmp(para_name, "tx_vboost_lvl")) {
+ ssp_tune1 &= ~SSP_PARACON1_TX_VBOOST_LVL_MASK;
+ ssp_tune1 |= SSP_PARACON1_TX_VBOOST_LVL(val);
+ } else if (!strcmp(para_name, "los_level")) {
+ ssp_tune1 &= ~SSP_PARACON1_LOS_LEVEL_MASK;
+ ssp_tune1 |= SSP_PARACON1_LOS_LEVEL(val);
+ } else if (!strcmp(para_name, "los_bias")) {
+ ssp_tune1 &= ~SSP_PARACON1_LOS_BIAS_MASK;
+ ssp_tune1 |= SSP_PARACON1_LOS_BIAS(val);
+ } else if (!strcmp(para_name, "pcs_rx_los_mask_val")) {
+ ssp_tune1 &= ~SSP_PARACON1_PCS_RX_LOS_MASK_VAL_MASK;
+ ssp_tune1 |= SSP_PARACON1_PCS_RX_LOS_MASK_VAL(val);
+ /* SSP TEST setting : 0x135e/f_0000 + 0x3c */
+ } else if (!strcmp(para_name, "tx_eye_height_cntl_en")) {
+ ssp_tune2 &= ~SSP_TEST_TX_EYE_HEIGHT_CNTL_EN_MASK;
+ ssp_tune2 |= SSP_TEST_TX_EYE_HEIGHT_CNTL_EN(val);
+ } else if (!strcmp(para_name, "pipe_tx_deemph_update_delay")) {
+ ssp_tune2 &= ~SSP_TEST_PIPE_TX_DEEMPH_UPDATE_DELAY_MASK;
+ ssp_tune2 |= SSP_TEST_PIPE_TX_DEEMPH_UPDATE_DELAY(val);
+ } else if (!strcmp(para_name, "pcs_tx_swing_full_sstx")) {
+ ssp_tune2 &= ~SSP_TEST_PCS_TX_SWING_FULL_SSTX_MASK;
+ ssp_tune2 |= SSP_TEST_PCS_TX_SWING_FULL_SSTX(val);
+ }
+
+ } /* for */
+ writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_SSP_PARACON0);
+ writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_SSP_PARACON1);
+ writel(ssp_tune2, ss_reg_base + EXYNOS_USBCON_SSP_TEST);
+ } /* else */
+}
+
+void phy_exynos_usb_v3p1_tune_each(struct exynos_usbphy_info *info,
+ char *para_name, int val)
+{
+#ifndef __BOOT__
+ u32 hsp_tune, ssp_tune0, ssp_tune1;
+
+ bool ss_only_cap;
+
+ ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
+
+ if (!info->tune_param)
+ return;
+
+ if (!ss_only_cap) {
+ void __iomem *regs_base = info->regs_base;
+
+ hsp_tune = readl(regs_base + EXYNOS_USBCON_HSP_TUNE);
+ if (!strcmp(para_name, "compdis")) {
+ hsp_tune &= ~HSP_TUNE_COMPDIS_MASK;
+ hsp_tune |= HSP_TUNE_COMPDIS_SET(val);
+ } else if (!strcmp(para_name, "otg")) {
+ hsp_tune &= ~HSP_TUNE_OTG_MASK;
+ hsp_tune |= HSP_TUNE_OTG_SET(val);
+ } else if (!strcmp(para_name, "rx_sqrx")) {
+ hsp_tune &= ~HSP_TUNE_SQRX_MASK;
+ hsp_tune |= HSP_TUNE_SQRX_SET(val);
+ } else if (!strcmp(para_name, "tx_fsls")) {
+ hsp_tune &= ~HSP_TUNE_TXFSLS_MASK;
+ hsp_tune |= HSP_TUNE_TXFSLS_SET(val);
+ } else if (!strcmp(para_name, "tx_hsxv")) {
+ hsp_tune &= ~HSP_TUNE_HSXV_MASK;
+ hsp_tune |= HSP_TUNE_HSXV_SET(val);
+ } else if (!strcmp(para_name, "tx_pre_emp")) {
+ hsp_tune &= ~HSP_TUNE_TXPREEMPA_MASK;
+ hsp_tune |= HSP_TUNE_TXPREEMPA_SET(val);
+ } else if (!strcmp(para_name, "tx_pre_emp_plus")) {
+ if (val)
+ hsp_tune |= HSP_TUNE_TXPREEMPA_PLUS;
+ else
+ hsp_tune &= ~HSP_TUNE_TXPREEMPA_PLUS;
+ } else if (!strcmp(para_name, "tx_res")) {
+ hsp_tune &= ~HSP_TUNE_TXRES_MASK;
+ hsp_tune |= HSP_TUNE_TXRES_SET(val);
+ } else if (!strcmp(para_name, "tx_rise")) {
+ hsp_tune &= ~HSP_TUNE_TXRISE_MASK;
+ hsp_tune |= HSP_TUNE_TXRISE_SET(val);
+ } else if (!strcmp(para_name, "tx_vref")) {
+ hsp_tune &= ~HSP_TUNE_TXVREF_MASK;
+ hsp_tune |= HSP_TUNE_TXVREF_SET(val);
+ } else if (!strcmp(para_name, "tx_res_ovrd"))
+ phy_exynos_usb_v3p1_tif_ov_wr(info, 0x6, val);
+ writel(hsp_tune, regs_base + EXYNOS_USBCON_HSP_TUNE);
+ } else {
+ /* ssphy tuning */
+ void __iomem *ss_reg_base;
+
+ if (info->used_phy_port == 1)
+ ss_reg_base = info->regs_base_2nd;
+ else
+ ss_reg_base = info->regs_base;
+
+ ssp_tune0 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON0);
+ ssp_tune1 = readl(ss_reg_base + EXYNOS_USBCON_SSP_PARACON1);
+
+ if (!strcmp(para_name, "tx0_term_offset")) {
+ ssp_tune0 &= ~SSP_PARACON0_TX0_TERM_OFFSET_MASK;
+ ssp_tune0 |= SSP_PARACON0_TX0_TERM_OFFSET(val);
+ } else if (!strcmp(para_name, "pcs_tx_swing_full")) {
+ ssp_tune0 &= ~SSP_PARACON0_PCS_TX_SWING_FULL_MASK;
+ ssp_tune0 |= SSP_PARACON0_PCS_TX_SWING_FULL(val);
+ } else if (!strcmp(para_name, "pcs_tx_deemph_6db")) {
+ ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_6DB_MASK;
+ ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_6DB(val);
+ } else if (!strcmp(para_name, "pcs_tx_deemph_3p5db")) {
+ ssp_tune0 &= ~SSP_PARACON0_PCS_TX_DEEMPH_3P5DB_MASK;
+ ssp_tune0 |= SSP_PARACON0_PCS_TX_DEEMPH_3P5DB(val);
+ } else if (!strcmp(para_name, "tx_vboost_lvl")) {
+ ssp_tune1 &= ~SSP_PARACON1_TX_VBOOST_LVL_MASK;
+ ssp_tune1 |= SSP_PARACON1_TX_VBOOST_LVL(val);
+ } else if (!strcmp(para_name, "los_level")) {
+ ssp_tune1 &= ~SSP_PARACON1_LOS_LEVEL_MASK;
+ ssp_tune1 |= SSP_PARACON1_LOS_LEVEL(val);
+ } else if (!strcmp(para_name, "los_bias")) {
+ ssp_tune1 &= ~SSP_PARACON1_LOS_BIAS_MASK;
+ ssp_tune1 |= SSP_PARACON1_LOS_BIAS(val);
+ } else if (!strcmp(para_name, "pcs_rx_los_mask_val")) {
+ ssp_tune1 &= ~SSP_PARACON1_PCS_RX_LOS_MASK_VAL_MASK;
+ ssp_tune1 |= SSP_PARACON1_PCS_RX_LOS_MASK_VAL(val);
+ }
+ writel(ssp_tune0, ss_reg_base + EXYNOS_USBCON_SSP_PARACON0);
+ writel(ssp_tune1, ss_reg_base + EXYNOS_USBCON_SSP_PARACON1);
+ } /* else */
+#endif
+}
+
+void phy_exynos_usb_v3p1_rd_tune_each_from_reg(struct exynos_usbphy_info *info,
+ u32 tune, char *para_name, int *val)
+{
+ bool ss_only_cap;
+
+ ss_only_cap = (info->version & EXYNOS_USBCON_VER_SS_CAP) >> 4;
+
+ if (!info->tune_param)
+ return;
+
+ if (!ss_only_cap) { /* hsphy tuning */
+ if (!strcmp(para_name, "compdis"))
+ *val = HSP_TUNE_COMPDIS_GET(tune);
+ else if (!strcmp(para_name, "otg"))
+ *val = HSP_TUNE_OTG_GET(tune);
+ else if (!strcmp(para_name, "rx_sqrx"))
+ *val = HSP_TUNE_SQRX_GET(tune);
+ else if (!strcmp(para_name, "tx_fsls"))
+ *val = HSP_TUNE_TXFSLS_GET(tune);
+ else if (!strcmp(para_name, "tx_hsxv"))
+ *val = HSP_TUNE_HSXV_GET(tune);
+ else if (!strcmp(para_name, "tx_pre_emp"))
+ *val = HSP_TUNE_TXPREEMPA_GET(tune);
+ else if (!strcmp(para_name, "tx_pre_emp_plus"))
+ *val = HSP_TUNE_TXPREEMPA_PLUS_GET(tune);
+ else if (!strcmp(para_name, "tx_res"))
+ *val = HSP_TUNE_TXRES_GET(tune);
+ else if (!strcmp(para_name, "tx_rise"))
+ *val = HSP_TUNE_TXRISE_GET(tune);
+ else if (!strcmp(para_name, "tx_vref"))
+ *val = HSP_TUNE_TXVREF_GET(tune);
+ else
+ *val = -1;
+ }
+}
+
+void phy_exynos_usb_v3p1_wr_tune_reg(struct exynos_usbphy_info *info, u32 val)
+{
+ void __iomem *regs_base = info->regs_base;
+
+ writel(val, regs_base + EXYNOS_USBCON_HSP_TUNE);
+}
+
+void phy_exynos_usb_v3p1_rd_tune_reg(struct exynos_usbphy_info *info, u32 *val)
+{
+ void __iomem *regs_base = info->regs_base;
+
+ if (!val)
+ return;
+
+ *val = readl(regs_base + EXYNOS_USBCON_HSP_TUNE);
+}
+
+void phy_exynos_usb3p1_rewa_ready(struct exynos_usbphy_info *info)
+{
+ u32 reg;
+ void __iomem *regs_base = info->regs_base;
+
+ /* Disable ReWA */
+ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
+ reg &= ~REWA_ENABLE_HS_REWA_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE);
+
+ /* Config ReWA Operation */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+ /* Select line state check circuit
+ * 0 : FSVPLUS/FSMINUS
+ * 1 : LINE STATE
+ * */
+ reg &= ~HSREWA_CTRL_DPDM_MON_SEL;
+ /* Select Drive K circuit
+ * 0 : Auto Resume in the PHY
+ * 1 : BYPASS mode by ReWA
+ * */
+ reg |= HSREWA_CTRL_DIG_BYPASS_CON_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+
+ /* Set Driver K Time */
+ reg = 0x1;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_HSTK);
+
+ /* Set Timeout counter Driver K Time */
+ reg = 0xff00;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_REFTO);
+
+ /* Disable All events source for abnormal event generation */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
+ reg |= HSREWA_CTRL_HS_EVT_ERR_SUS |
+ HSREWA_CTRL_HS_EVT_ERR_DEV_K |
+ HSREWA_CTRL_HS_EVT_DISCON |
+ HSREWA_CTRL_HS_EVT_BYPASS_DIS |
+ HSREWA_CTRL_HS_EVT_RET_DIS |
+ HSREWA_CTRL_HS_EVT_RET_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
+}
+
+int phy_exynos_usb3p1_rewa_enable(struct exynos_usbphy_info *info)
+{
+ int cnt;
+ u32 reg;
+ void __iomem *regs_base = info->regs_base;
+
+ /* Clear the system valid flag */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+ reg &= ~HSREWA_CTRL_HS_SYS_VALID;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+
+ /* Enable ReWA */
+ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
+ reg |= REWA_ENABLE_HS_REWA_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE);
+
+ /* Check Status : Wait ReWA Status is retention enabled */
+ for (cnt = 10000; cnt != 0; cnt--) {
+
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT);
+
+ /* non suspend status*/
+ if (reg & HSREWA_CTRL_HS_EVT_ERR_SUS)
+ return HS_REWA_EN_STS_NOT_SUSPEND;
+ /* Disconnect Status */
+ if (reg & HSREWA_CTRL_HS_EVT_DISCON)
+ return HS_REWA_EN_STS_DISCONNECT;
+ /* Success ReWA Enable */
+ if (reg & HSREWA_CTRL_HS_EVT_RET_EN)
+ break;
+#if !defined(CONFIG_BOARD_ZEBU)
+ udelay(30);
+#endif
+ }
+
+ if (!cnt)
+ return HS_REWA_EN_STS_NOT_SUSPEND;
+
+ /* Set the INT1 for detect K and Disconnect */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
+ reg &= ~HSREWA_CTRL_HS_EVT_DISCON &
+ ~HSREWA_CTRL_HS_EVT_ERR_DEV_K;
+ reg |= HSREWA_CTRL_HS_EVT_ERR_SUS |
+ HSREWA_CTRL_HS_EVT_BYPASS_DIS |
+ HSREWA_CTRL_HS_EVT_RET_DIS |
+ HSREWA_CTRL_HS_EVT_RET_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
+
+ /* Enable All interrupt source and disnable Wake-up event */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INTR);
+ reg &= ~HSREWA_INTR_WAKEUP_REQ_MASK &
+ ~HSREWA_INTR_EVT_MASK &
+ ~HSREWA_INTR_WAKEUP_MASK &
+ ~HSREWA_INTR_TIMEOUT_MASK;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INTR);
+
+ udelay(100);
+
+ return HS_REWA_EN_STS_ENALBED;
+}
+
+int phy_exynos_usb3p1_rewa_req_sys_valid(struct exynos_usbphy_info *info)
+{
+ int cnt;
+ u32 reg;
+ void __iomem *regs_base = info->regs_base;
+
+ /* Mask All Interrupt source for the INT1 */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
+ reg &= ~HSREWA_CTRL_HS_EVT_DISCON &
+ ~HSREWA_CTRL_HS_EVT_ERR_DEV_K &
+ ~HSREWA_CTRL_HS_EVT_ERR_SUS &
+ ~HSREWA_CTRL_HS_EVT_BYPASS_DIS &
+ ~HSREWA_CTRL_HS_EVT_RET_DIS &
+ ~HSREWA_CTRL_HS_EVT_RET_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INT1_MASK);
+
+ /* Enable All interrupt source and disnable Wake-up event */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INTR);
+ reg |= HSREWA_INTR_WAKEUP_REQ_MASK;
+ reg |= HSREWA_INTR_TIMEOUT_MASK;
+ reg |= HSREWA_INTR_EVT_MASK;
+ reg |= HSREWA_INTR_WAKEUP_MASK;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_INTR);
+
+ /* Set the system valid flag */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+ reg |= HSREWA_CTRL_HS_SYS_VALID;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+
+ for (cnt = 10000; cnt != 0; cnt--) {
+
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT);
+
+ /* Disconnect Status */
+ if (reg & HSREWA_CTRL_HS_EVT_DISCON)
+ return HS_REWA_EN_STS_DISCONNECT;
+ /* Success ReWA Enable */
+ if (reg & HSREWA_CTRL_HS_EVT_RET_EN)
+ break;
+#if !defined(CONFIG_BOARD_ZEBU)
+ udelay(30);
+#endif
+ }
+
+ return HS_REWA_EN_STS_DISABLED;
+}
+
+int phy_exynos_usb3p1_rewa_disable(struct exynos_usbphy_info *info)
+{
+ int cnt;
+ u32 reg;
+ void __iomem *regs_base = info->regs_base;
+
+ /* Check ReWA Already diabled
+ * If ReWA was disabled states, disabled sequence is already done */
+ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
+ if (!(reg & REWA_ENABLE_HS_REWA_EN))
+ return 0;
+
+ /* Set Link ready to notify ReWA */
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+ reg |= HSREWA_CTRL_HS_LINK_READY;
+ writel(reg, regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+ /* Wait Bypass Disable */
+ for (cnt = 10000; cnt != 0; cnt--) {
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_INT1_EVT);
+ /* Success ReWA Enable */
+ if (reg & HSREWA_CTRL_HS_EVT_BYPASS_DIS)
+ break;
+#if !defined(CONFIG_BOARD_ZEBU)
+ udelay(30);
+#endif
+ }
+ if (!cnt)
+ return -1;
+ /* Wait ReWA Done */
+ for (cnt = 1000; cnt != 0; cnt--) {
+ reg = readl(regs_base + EXYNOS_USBCON_HSREWA_CTRL);
+
+ /* Success ReWA Enable */
+ if (reg & HSREWA_CTRL_HS_REWA_DONE)
+ break;
+#if !defined(CONFIG_BOARD_ZEBU)
+ udelay(30);
+#endif
+ }
+ if (!cnt)
+ return -1;
+ /* Disable ReWA */
+ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
+ reg &= ~REWA_ENABLE_HS_REWA_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE);
+
+ return 0;
+}
+
+int phy_exynos_usb3p1_rewa_cancel(struct exynos_usbphy_info *info)
+{
+ int ret;
+ u32 reg;
+ void __iomem *regs_base = info->regs_base;
+
+ /* Check ReWA Already diabled
+ * If ReWA was disabled states, disabled sequence is already done */
+ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
+ if (!(reg & REWA_ENABLE_HS_REWA_EN))
+ return 0;
+
+ ret = phy_exynos_usb3p1_rewa_req_sys_valid(info);
+
+ /* Disable ReWA */
+ reg = readl(regs_base + EXYNOS_USBCON_REWA_ENABLE);
+ reg &= ~REWA_ENABLE_HS_REWA_EN;
+ writel(reg, regs_base + EXYNOS_USBCON_REWA_ENABLE);
+
+ udelay(100);
+
+ return ret;
+}
+
+void phy_exynos_usb3p1_usb3phy_dp_altmode_set_ss_disable(
+ struct exynos_usbphy_info *usbphy_info, int dp_phy_port)
+{
+ void __iomem *regs_base;
+ u32 reg;
+
+ if (dp_phy_port == 0)
+ regs_base = usbphy_info->regs_base;
+ else
+ regs_base = usbphy_info->regs_base_2nd;
+
+ /* Reset Mux Select */
+ /* Assert phy_reset */
+ reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
+#if defined(CONFIG_USB_DP_COMBO_GEN2)
+ reg |= CLKRST_PHY20_SW_RST;
+ reg |= CLKRST_PHY20_RST_SEL;
+#else
+ reg |= CLKRST_PHY_SW_RST;
+ reg |= CLKRST_PHY_RST_SEL;
+#endif
+ writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
+
+ udelay(100);
+
+ /* Deassert test_powerdown_ssp */
+ /* Deassert test_powerdown_hsp */
+ reg = readl(regs_base + EXYNOS_USBCON_PWR);
+
+#if !defined(CONFIG_USB_DP_COMBO_GEN2)
+ reg &= ~(PWR_TEST_POWERDOWN_HSP);
+ reg &= ~(PWR_TEST_POWERDOWN_SSP);
+#endif
+ writel(reg, regs_base + EXYNOS_USBCON_PWR);
+
+ udelay(100);
+}
+
+void phy_exynos_usb3p1_usb3phy_dp_altmode_clear_ss_disable(
+ struct exynos_usbphy_info *usbphy_info, int dp_phy_port)
+{
+ void __iomem *regs_base;
+ u32 reg;
+
+ if (dp_phy_port == 0)
+ regs_base = usbphy_info->regs_base;
+ else
+ regs_base = usbphy_info->regs_base_2nd;
+
+ /* Assert test_powerdown_ssp */
+ /* Assert test_powerdown_hsp */
+ reg = readl(regs_base + EXYNOS_USBCON_PWR);
+#if !defined(CONFIG_USB_DP_COMBO_GEN2)
+ reg |= (PWR_TEST_POWERDOWN_HSP);
+ reg |= (PWR_TEST_POWERDOWN_SSP);
+#endif
+ writel(reg, regs_base + EXYNOS_USBCON_PWR);
+
+ udelay(100);
+
+ reg = readl(regs_base + EXYNOS_USBCON_CLKRST);
+#if defined(CONFIG_USB_DP_COMBO_GEN2)
+ reg |= CLKRST_PHY20_RST_SEL;
+ reg &= ~CLKRST_PHY20_SW_RST;
+#else
+ reg |= CLKRST_PHY_RST_SEL;
+ reg &= ~CLKRST_PHY_SW_RST;
+#endif
+ writel(reg, regs_base + EXYNOS_USBCON_CLKRST);
+
+ udelay(100);
+}
+
+void phy_exynos_usb3p1_set_fs_vplus_vminus(
+ struct exynos_usbphy_info *usbphy_info, u32 fsls_speed_sel, u32 fsv_out_en)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 hsp_ctrl;
+
+ if (fsv_out_en) {
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ if (fsls_speed_sel)
+ hsp_ctrl |= HSP_FSLS_SPEED_SEL;
+ else
+ hsp_ctrl &= ~HSP_FSLS_SPEED_SEL;
+ hsp_ctrl |= HSP_FSV_OUT_EN;
+ writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
+ } else {
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ hsp_ctrl &= ~HSP_FSLS_SPEED_SEL;
+ hsp_ctrl &= ~HSP_FSV_OUT_EN;
+ writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
+ }
+
+}
+
+u8 phy_exynos_usb3p1_bc_data_contact_detect(struct exynos_usbphy_info *usbphy_info)
+{
+ bool ret = false;
+ u32 utmi_ctrl, hsp_ctrl;
+ u32 cnt;
+ u32 fsvplus;
+ void __iomem *regs_base = usbphy_info->regs_base;
+
+ // set UTMI_CTRL
+ utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
+ utmi_ctrl |= UTMI_OPMODE_CTRL_EN;
+ utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
+ utmi_ctrl |= UTMI_FORCE_OPMODE_SET(1);
+ utmi_ctrl &= ~UTMI_DP_PULLDOWN;
+ utmi_ctrl |= UTMI_DM_PULLDOWN;
+ utmi_ctrl |= UTMI_FORCE_SUSPEND;
+ writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
+
+ // Data contact Detection Enable
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ hsp_ctrl &= ~HSP_VDATSRCENB;
+ hsp_ctrl &= ~HSP_VDATDETENB;
+ hsp_ctrl |= HSP_DCDENB;
+ writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
+
+ for (cnt = 8; cnt != 0; cnt--) {
+ // TDCD_TIMEOUT, 300ms~900ms
+ mdelay(40);
+
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ fsvplus = HSP_FSVPLUS_GET(hsp_ctrl);
+
+ if (!fsvplus)
+ break;
+ }
+
+ if (fsvplus == 1 && cnt == 0)
+ ret = false;
+ else {
+ mdelay(10); // TDCD_DBNC, 10ms
+
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ fsvplus = HSP_FSVPLUS_GET(hsp_ctrl);
+
+ if (!fsvplus)
+ ret = true;
+ else
+ ret = false;
+ }
+
+ hsp_ctrl &= ~HSP_DCDENB;
+ writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
+
+ // restore UTMI_CTRL
+ utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
+ utmi_ctrl &= ~UTMI_OPMODE_CTRL_EN;
+ utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
+ utmi_ctrl &= ~UTMI_DM_PULLDOWN;
+ utmi_ctrl &= ~UTMI_FORCE_SUSPEND;
+ writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
+
+ return ret;
+}
+
+enum exynos_usb_bc phy_exynos_usb3p1_bc_battery_charger_detection(struct exynos_usbphy_info *usbphy_info)
+{
+ u32 utmi_ctrl, hsp_ctrl;
+ u32 chgdet;
+ enum exynos_usb_bc chg_port = BC_SDP;
+ void __iomem *regs_base = usbphy_info->regs_base;
+
+ /** Step 1. Primary Detection :: SDP / DCP or CDP
+ * voltage sourcing on the D+ line and sensing on the D- line
+ **/
+ // set UTMI_CTRL
+ utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
+ utmi_ctrl |= UTMI_OPMODE_CTRL_EN;
+ utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
+ utmi_ctrl |= UTMI_FORCE_OPMODE_SET(1);
+ utmi_ctrl &= ~UTMI_DP_PULLDOWN;
+ utmi_ctrl &= ~UTMI_DM_PULLDOWN;
+ utmi_ctrl |= UTMI_FORCE_SUSPEND;
+ writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
+
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ hsp_ctrl &= ~HSP_CHRGSEL;
+ hsp_ctrl |= HSP_VDATSRCENB;
+ hsp_ctrl |= HSP_VDATDETENB;
+ writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
+
+ mdelay(40); // TVDMSRC_ON, 40ms
+
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ chgdet = HSP_CHGDET_GET(hsp_ctrl);
+ if (!chgdet) {
+ /*
+ * IF CHGDET pin is not set,
+ * Standard Downstream Por
+ */
+ chg_port = BC_SDP;
+ } else {
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ hsp_ctrl &= ~HSP_VDATSRCENB;
+ hsp_ctrl &= ~HSP_VDATDETENB;
+ writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
+
+ mdelay(20);
+
+ /* ELSE Maybe DCP or CDP but DCP is primary charger */
+
+ /*
+ * Step 2.1 Secondary Detection :: DCP or CDP
+ * voltage sourcing on the D- line and sensing on the D+ line
+ */
+ hsp_ctrl |= HSP_CHRGSEL;
+ hsp_ctrl |= HSP_VDATSRCENB;
+ hsp_ctrl |= HSP_VDATDETENB;
+ writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
+
+ mdelay(40); // TVDMSRC_ON, 40ms
+
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ chgdet = HSP_CHGDET_GET(hsp_ctrl);
+
+ if (!chgdet)
+ chg_port = BC_CDP;
+ else
+ chg_port = BC_DCP;
+ }
+
+ hsp_ctrl = readl(regs_base + EXYNOS_USBCON_HSP);
+ hsp_ctrl &= ~HSP_VDATSRCENB;
+ hsp_ctrl &= ~HSP_VDATDETENB;
+ writel(hsp_ctrl, regs_base + EXYNOS_USBCON_HSP);
+
+ // restore UTMI_CTRL
+ utmi_ctrl = readl(regs_base + EXYNOS_USBCON_UTMI);
+ utmi_ctrl &= ~UTMI_OPMODE_CTRL_EN;
+ utmi_ctrl &= ~UTMI_FORCE_OPMODE_MASK;
+ utmi_ctrl &= ~UTMI_FORCE_SUSPEND;
+ writel(utmi_ctrl, regs_base + EXYNOS_USBCON_UTMI);
+
+ return chg_port;
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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 DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USB3P1_H_
+#define DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USB3P1_H_
+
+#define HS_REWA_INTR_SRC_RET_EN (1 << 0)
+#define HS_REWA_INTR_SRC_RET_DIS (1 << 1)
+#define HS_REWA_INTR_SRC_BYPASS_DIS (1 << 2)
+#define HS_REWA_INTR_SRC_DISCON (1 << 16)
+#define HS_REWA_INTR_SRC_ERR_DEV_K (1 << 17)
+#define HS_REWA_INTR_SRC_ERR_SUS (1 << 18)
+
+#define HS_REWA_EN_STS_ENALBED 0
+#define HS_REWA_EN_STS_DISABLED 1
+#define HS_REWA_EN_STS_DISCONNECT 2
+#define HS_REWA_EN_STS_NOT_SUSPEND -1
+
+
+/* initialted */
+extern void phy_exynos_usb_v3p1_enable(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_disable(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb3p1_pcs_reset(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb3p1_sw_rst(struct exynos_usbphy_info *info);
+extern u64 phy_exynos_usb3p1_get_logic_trace(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_enable_dp_pullup(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_disable_dp_pullup(struct exynos_usbphy_info *info);
+/* USB/DP PHY control */
+extern void phy_exynos_usb_v3p1_pma_ready(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_g2_pma_ready(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_pma_sw_rst_release(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_g2_pma_sw_rst_release(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_pipe_ovrd(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_pipe_ready(struct exynos_usbphy_info *info);
+/* Tune */
+extern void phy_exynos_usb_v3p1_config_host_mode(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_tune_host(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_tune_dev(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_tune(struct exynos_usbphy_info *info);
+extern void phy_exynos_usb_v3p1_tune_each(struct exynos_usbphy_info *info, char *para_name, int val);
+extern void phy_exynos_usb_v3p1_rd_tune_each_from_reg(struct exynos_usbphy_info *info, u32 tune, char *para_name, int *val);
+extern void phy_exynos_usb_v3p1_wr_tune_reg(struct exynos_usbphy_info *info, u32 val);
+extern void phy_exynos_usb_v3p1_rd_tune_reg(struct exynos_usbphy_info *info, u32 *val);
+/* High Speed Remote Wake-up Advisor(HS ReWA) */
+extern int phy_exynos_usb3p1_rewa_enable(struct exynos_usbphy_info *info);
+extern int phy_exynos_usb3p1_rewa_disable(struct exynos_usbphy_info *info);
+extern int phy_exynos_usb3p1_rewa_req_sys_valid(struct exynos_usbphy_info *info);
+extern int phy_exynos_usb3p1_rewa_cancel(struct exynos_usbphy_info *info);
+/* 3.0 PHY CR Port Access */
+extern void phy_exynos_usb_v3p1_cal_cr_write(struct exynos_usbphy_info *info, u16 addr, u16 data);
+extern u16 phy_exynos_usb_v3p1_cal_cr_read(struct exynos_usbphy_info *info, u16 addr);
+/* 2.0 PHY Test I/F Access */
+extern u8 phy_exynos_usb_v3p1_tif_ov_rd(struct exynos_usbphy_info *info, u8 addr);
+extern u8 phy_exynos_usb_v3p1_tif_ov_wr(struct exynos_usbphy_info *info, u8 addr, u8 data);
+extern u8 phy_exynos_usb_v3p1_tif_sts_rd(struct exynos_usbphy_info *info, u8 addr);
+/* FS_VPLUS/VMINUS interrupt */
+extern void phy_exynos_usb3p1_set_fs_vplus_vminus(struct exynos_usbphy_info *usbphy_info,
+ u32 fsls_speed_sel, u32 fsv_out_en);
+/* BC 1.2 */
+extern u8 phy_exynos_usb3p1_bc_data_contact_detect(struct exynos_usbphy_info *usbphy_info);
+extern enum exynos_usb_bc phy_exynos_usb3p1_bc_battery_charger_detection(struct exynos_usbphy_info *usbphy_info);
+
+#endif /* DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USB3P1_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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 _PHY_EXYNOS_USBDP_R_CMN_H_
+#define _PHY_EXYNOS_USBDP_R_CMN_H_
+
+#define EXYNOS_USBDP_COMBO_CMN_R00 (0x0000)
+#define USBDP_CMN00_SLAVE_ADDR_MASK USBDP_COMBO_R_MSK(0, 8)
+#define USBDP_CMN00_SLAVE_ADDR_CLR USBDP_COMBO_R_CLR(0, 8)
+#define USBDP_CMN00_SLAVE_ADDR_SET(_x) USBDP_COMBO_R_SET(_x, 0, 8)
+#define USBDP_CMN00_SLAVE_ADDR_GET(_R) USBDP_COMBO_R_GET(_R, 0, 8)
+
+#define EXYNOS_USBDP_COM_CMN_R01 (0x0004)
+#define USBDP_CMN01_BIAS_REXT_EN USBDP_COMBO_R_MSK(7, 1)
+#define USBDP_CMN01_BIAS_CLK_SEL_MASK USBDP_COMBO_R_MSK(5, 2)
+#define USBDP_CMN01_BIAS_CLK_SEL_SET(_x) USBDP_COMBO_R_SET(_x, 5, 2)
+#define USBDP_CMN01_BIAS_CLK_SEL_GET(_r) USBDP_COMBO_R_GET(_r, 5, 2)
+#define USBDP_CMN01_BIAS_LADDER_SEL_MASK USBDP_COMBO_R_MSK(2, 3)
+#define USBDP_CMN01_BIAS_LADDER_SEL_SET(_x) USBDP_COMBO_R_SET(_x, 2, 3)
+#define USBDP_CMN01_BIAS_LADDER_SEL_GET(_r) USBDP_COMBO_R_GET(_x, 2, 3)
+#define USBDP_CMN01_BIAS_LADDER_EN USBDP_COMBO_R_MSK(1, 1)
+#define USBDP_CMN01_BIAS_CLK_EN USBDP_COMBO_R_MSK(0, 1)
+
+#define EXYNOS_USBDP_COM_CMN_R02 (0x0008)
+#define USBDP_CMN02_MAN_BYPASS_LPF_EN (1 << 7)
+#define USBDP_CMN02_DIV_SEL_MASK (0x3 << 5)
+#define USBDP_CMN02_DIV_SEL_SET(_x) ((_x & 0x3) << 5)
+#define USBDP_CMN02_DIV_SEL_GET(_r) ((_r & 0x3) >> 5)
+#define USBDP_CMN02_BIAS_RES_CTRL_MASK (0x7 << 2)
+#define USBDP_CMN02_BIAS_RES_CTRL_SET(_x) ((_x & 0x7) << 2)
+#define USBDP_CMN02_BIAS_RES_CTRL_GET(_r) ((_r & 0x7) >> 2)
+#define USBDP_CMN02_BIAS_VBG_SEL_MASK (0x3 << 0)
+#define USBDP_CMN02_BIAS_VBG_SEL_SET(_x) ((_x & 0x3) << 0)
+#define USBDP_CMN02_BIAS_VBG_SEL_GET(_r) ((_r & 0x3) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R03 (0x000C)
+#define USBDP_CMN03_REFCLK_OUT_SOC_EN (1 << 7)
+#define USBDP_CMN03_CD_SW_CLKQ_VDD (1 << 6)
+#define USBDP_CMN03_CD_SW_CLKQ_PN (1 << 5)
+#define USBDP_CMN03_CD_SW_CLKI_PN (1 << 4)
+#define USBDP_CMN03_CD_SW_CLKI_CLKQ (1 << 3)
+#define USBDP_CMN03_CD_EN_PWM_CLK (1 << 2)
+#define USBDP_CMN03_CD_EN_CLKQ (1 << 1)
+#define USBDP_CMN03_MAN_BIAS_BYPASS_LPF (1 << 0)
+
+#define EXYNOS_USBDP_COM_CMN_R04 (0x0010)
+#define USBDP_CMN04_CD_HSCLK_DIV1_PSTR_MASK (0xf << 4)
+#define USBDP_CMN04_CD_HSCLK_DIV1_PSTR_SET(_x) ((_x & 0xf) << 4)
+#define USBDP_CMN04_CD_HSCLK_DIV1_PSTR_GET(_r) ((_r & 0xf) >> 4)
+#define USBDP_CMN04_CD_HSCLK_DIV1_NSTR_MASK (0xf << 0)
+#define USBDP_CMN04_CD_HSCLK_DIV1_NSTR_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN04_CD_HSCLK_DIV1_NSTR_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R05 (0x0014)
+#define USBDP_CMN05_CD_HSCLK_DIV5_PSTR_MASK (0xf << 4)
+#define USBDP_CMN05_CD_HSCLK_DIV5_PSTR_SET(_x) ((_x & 0xf) << 4)
+#define USBDP_CMN05_CD_HSCLK_DIV5_PSTR_GET(_r) ((_r & 0xf) >> 4)
+#define USBDP_CMN05_CD_HSCLK_DIV5_NSTR_MASK (0xf << 0)
+#define USBDP_CMN05_CD_HSCLK_DIV5_NSTR_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN05_CD_HSCLK_DIV5_NSTR_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R06 (0x0018)
+#define USBDP_CMN06_CD_HSCLK_DIV10_PSTR_MASK (0xf << 4)
+#define USBDP_CMN06_CD_HSCLK_DIV10_PSTR_SET(_x) ((_x & 0xf) << 4)
+#define USBDP_CMN06_CD_HSCLK_DIV10_PSTR_GET(_r) ((_r & 0xf) >> 4)
+#define USBDP_CMN06_CD_HSCLK_DIV10_NSTR_MASK (0xf << 0)
+#define USBDP_CMN06_CD_HSCLK_DIV10_NSTR_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN06_CD_HSCLK_DIV10_NSTR_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R07 (0x001C)
+#define USBDP_CMN07_REFCLK_HYS_100M_IN_MASK (0x3 << 6)
+#define USBDP_CMN07_REFCLK_HYS_100M_IN_SET(_x) ((_x & 0x3) << 6)
+#define USBDP_CMN07_REFCLK_HYS_100M_IN_GET(_r) ((_r & 0x3) >> 6)
+#define USBDP_CMN07_MAN_PLL_REF_CLK_SEL_MASK (0x3 << 4)
+#define USBDP_CMN07_MAN_PLL_REF_CLK_SEL_SET(_x) ((_x & 0x3) << 4)
+#define USBDP_CMN07_MAN_PLL_REF_CLK_SEL_GET(_r) ((_r & 0x3) >> 4)
+#define USBDP_CMN07_MAN_REFCLK_OUT_SOURCE (1 << 3)
+#define USBDP_CMN07_MAN_REFCLK_OUT_EN (1 << 2)
+#define USBDP_CMN07_MAN_REFCLK_IN_EN (1 << 1)
+#define USBDP_CMN07_REFCLK_CTRL_EN (1 << 0)
+
+#define EXYNOS_USBDP_COM_CMN_R08 (0x0020)
+#define USBDP_CMN08_REFCLK_OUT_STR_MASK (0x7 << 5)
+#define USBDP_CMN08_REFCLK_OUT_STR_SET(_x) ((_x & 0x7) << 5)
+#define USBDP_CMN08_REFCLK_OUT_STR_GET(_r) ((_r & 0x7) >> 5)
+#define USBDP_CMN08_REFCLK_LOCKDONE_SEL (1 << 4)
+#define USBDP_CMN08_MAN_PLL_REF_DIV_MASK (0xf << 0)
+#define USBDP_CMN08_MAN_PLL_REF_DIV_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN08_MAN_PLL_REF_DIV_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R09 (0x0024)
+#define USBDP_CMN09_REFCLK_TUNECODE_100M_IN_MASK (0xf << 0)
+#define USBDP_CMN09_REFCLK_TUNECODE_100M_IN_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN09_REFCLK_TUNECODE_100M_IN_GET(_r) ((_r & 0xf) >> 0)
+#define USBDP_CMN09_REFCLK_VCM_PN_MASK (0xf << 0)
+#define USBDP_CMN09_REFCLK_VCM_PN_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN09_REFCLK_VCM_PN_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R0A (0x0028)
+#define USBDP_CMN0A_PLL_AFC_FAST_LOCK_SETTLE_NO_MASK (0x7 << 5)
+#define USBDP_CMN0A_PLL_AFC_FAST_LOCK_SETTLE_NO_SET(_x) ((_x & 0x7) << 5)
+#define USBDP_CMN0A_PLL_AFC_FAST_LOCK_SETTLE_NO_GET(_r) ((_r & 0x7) >> 5)
+#define USBDP_CMN0A_PLL_AFC_FAST_LOCK_BYPASS (1 << 4)
+#define USBDP_CMN0A_PLL_AFC_BSEL (1 << 3)
+#define USBDP_CMN0A_AFC_PRESET_VCO_CNT_MASK (0x7 << 0)
+#define USBDP_CMN0A_AFC_PRESET_VCO_CNT_SET(_x) ((_x & 0x7) << 0)
+#define USBDP_CMN0A_AFC_PRESET_VCO_CNT_GET(_r) ((_r & 0x7) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R0B (0x002C)
+#define USBDP_CMN0B_PLL_AFC_VFORCE (1 << 7)
+#define USBDP_CMN0B_PLL_SLOW_LOCK_BYPASS (1 << 6)
+#define USBDP_CMN0B_PLL_AFC_LOCK_PPM_SET_MASK (0x1f << 1)
+#define USBDP_CMN0B_PLL_AFC_LOCK_PPM_SET_SET(_x) ((_x & 0x1f) << 1)
+#define USBDP_CMN0B_PLL_AFC_LOCK_PPM_SET_GET(_r) ((_r & 0x1f) >> 1)
+#define USBDP_CMN0B_PLL_AFC_NON_CONTINUOUS_MODE (1 << 0)
+
+#define EXYNOS_USBDP_COM_CMN_R0C (0x0030)
+#define USBDP_CMN0C_PLL_AFC_TOL_MASK (0xf << 4)
+#define USBDP_CMN0C_PLL_AFC_TOL_SET(_x) ((_x & 0xf) << 4)
+#define USBDP_CMN0C_PLL_AFC_TOL_GET(_r) ((_r & 0xf) >> 4)
+#define USBDP_CMN0C_PLL_AFC_STB_NUM_MASK (0xf << 0)
+#define USBDP_CMN0C_PLL_AFC_STB_NUM_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN0C_PLL_AFC_STB_NUM_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R0D (0x0034)
+#define USBDP_CMN0D_PLL_AFC_VCO_CNT_WAIT_NO_MASK (0xf << 0)
+#define USBDP_CMN0D_PLL_AFC_VCO_CNT_WAIT_NO_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN0D_PLL_AFC_VCO_CNT_WAIT_NO_GET(_r) ((_r & 0xf) >> 0)
+#define USBDP_CMN0D_LC_VCO_MODE (1 << 4)
+#define USBDP_CMN0D_PLL_AFC_MAN_BSEL_M_MASK (0xf << 0)
+#define USBDP_CMN0D_PLL_AFC_MAN_BSEL_M_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN0D_PLL_AFC_MAN_BSEL_M_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R0E (0x0038)
+#define USBDP_CMN0E_PLL_AFC_ANA_CPI_CTRL_MASK (0x7 << 5)
+#define USBDP_CMN0E_PLL_AFC_ANA_CPI_CTRL_SET(_x) ((_x & 0x7) << 5)
+#define USBDP_CMN0E_PLL_AFC_ANA_CPI_CTRL_GET(_r) ((_r & 0x7) >> 5)
+#define USBDP_CMN0E_PLL_AFC_VCO_CNT_RUN_NO_MASK (0x1f << 0)
+#define USBDP_CMN0E_PLL_AFC_VCO_CNT_RUN_NO_SET(_x) ((_x & 0x1f) << 0)
+#define USBDP_CMN0E_PLL_AFC_VCO_CNT_RUN_NO_GET(_r) ((_r & 0x1f) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R0F (0x003C)
+#define USBDP_CMN0F_PLL_ANA_EN_PI (1 << 7)
+#define USBDP_CMN0F_PLL_ANA_DCC_EN_MASK (0xf << 3)
+#define USBDP_CMN0F_PLL_ANA_DCC_EN_SET(_x) ((_x & 0xf) << 3)
+#define USBDP_CMN0F_PLL_ANA_DCC_EN_GET(_r) ((_r & 0xf) >> 3)
+#define USBDP_CMN0F_PLL_ANA_CPP_CTRL_MASK (0x7 << 0)
+#define USBDP_CMN0F_PLL_ANA_CPP_CTRL_SET(_x) ((_x & 0x7) << 0)
+#define USBDP_CMN0F_PLL_ANA_CPP_CTRL_GET(_r) ((_r & 0x7) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R10 (0x0040)
+#define USBDP_CMN10_PLL_ANA_VCI_SEL_MASK (0x7 << 5)
+#define USBDP_CMN10_PLL_ANA_VCI_SEL_SET(_x) ((_x & 0x7) << 5)
+#define USBDP_CMN10_PLL_ANA_VCI_SEL_GET(_r) ((_r & 0x7) >> 5)
+#define USBDP_CMN10_MAN_CMN_PLL_SET_EN (1 << 4)
+#define USBDP_CMN10_PLL_ANA_LPF_RSEL_MASK (0xf << 0)
+#define USBDP_CMN10_PLL_ANA_LPF_RSEL_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN10_PLL_ANA_LPF_RSEL_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R11 (0x0044)
+#define USBDP_CMN11_PLL_ATB_SEL_MASK (0x7 << 5)
+#define USBDP_CMN11_PLL_ATB_SEL_SET(_x) ((_x & 0x7) << 5)
+#define USBDP_CMN11_PLL_ATB_SEL_GET(_r) ((_r & 0x7) >> 5)
+#define USBDP_CMN11_PLL_ANA_TEST_EN (1 << 4)
+#define USBDP_CMN11_PLL_ANA_PI_STR_MASK (0xf << 0)
+#define USBDP_CMN11_PLL_ANA_PI_STR_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN11_PLL_ANA_PI_STR_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R12 (0x0048)
+#define USBDP_CMN12_PLL_EN_100M (1 << 7)
+#define USBDP_CMN12_PLL_BYPASS_CLK_SEL_MASK (0x3 << 5)
+#define USBDP_CMN12_PLL_BYPASS_CLK_SEL_SET(_x) ((_x & 0x3) << 5)
+#define USBDP_CMN12_PLL_BYPASS_CLK_SEL_GET(_r) ((_r & 0x3) >> 5)
+#define USBDP_CMN12_PLL_CKFB_MON_EN (1 << 4)
+#define USBDP_CMN12_PLL_CK_MON_EN (1 << 3)
+#define USBDP_CMN12_PLL_CHOPPER_CLK_EN (1 << 2)
+#define USBDP_CMN12_PLL_ANA_VCO_PI_CTRL_MASK (0xf << 0)
+#define USBDP_CMN12_PLL_ANA_VCO_PI_CTRL_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN12_PLL_ANA_VCO_PI_CTRL_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R13 (0x004C)
+#define USBDP_CMN13_PLL_AFC_EN (1 << 7)
+#define USBDP_CMN13_PLL_DIV_NUM_MASK (0x1f << 2)
+#define USBDP_CMN13_PLL_DIV_NUM_SET(_x) ((_x & 0x1f) << 2)
+#define USBDP_CMN13_PLL_DIV_NUM_GET(_r) ((_r & 0x1f) >> 2)
+#define USBDP_CMN13_PLL_EDGE_OPT_MASK (0x3 << 0)
+#define USBDP_CMN13_PLL_EDGE_OPT_SET(_x) ((_x & 0x3) << 0)
+#define USBDP_CMN13_PLL_EDGE_OPT_GET(_r) ((_r & 0x3) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R14 (0x0050)
+#define USBDP_CMN14_PLL_FLD_RP_CODE_2_0_MASK (0x7 << 5)
+#define USBDP_CMN14_PLL_FLD_RP_CODE_2_0_SET(_x) ((_x & 0x7) << 5)
+#define USBDP_CMN14_PLL_FLD_RP_CODE_2_0_GET(_r) ((_r & 0x7) >> 5)
+#define USBDP_CMN14_PLL_FLD_RP_CODE_MASK (0x7 << 2)
+#define USBDP_CMN14_PLL_FLD_RP_CODE_SET(_x) ((_x & 0x7) << 2)
+#define USBDP_CMN14_PLL_FLD_RP_CODE_GET(_r) ((_r & 0x7) >> 2)
+#define USBDP_CMN14_PLL_FLD_CK_DIV_MASK (0x3 << 0)
+#define USBDP_CMN14_PLL_FLD_CK_DIV_SET(_x) ((_x & 0x3) << 0)
+#define USBDP_CMN14_PLL_FLD_CK_DIV_GET(_r) ((_r & 0x3) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R15 (0x0054)
+#define USBDP_CMN15_PLL_PH_FIX (1 << 7)
+#define USBDP_CMN15_PLL_PCG_CLK_OUT_EN (1 << 6)
+#define USBDP_CMN15_PLL_FLD_TG_CODE_8_3_MASK (0x3f << 0)
+#define USBDP_CMN15_PLL_FLD_TG_CODE_8_3_SET(_x) ((_x & 0x3f) << 0)
+#define USBDP_CMN15_PLL_FLD_TG_CODE_8_3_GET(_r) ((_r & 0x3f) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R16 (0x0058)
+#define USBDP_CMN16_PLL_PH_SEL_MASK (0xf << 4)
+#define USBDP_CMN16_PLL_PH_SEL_SET(_x) ((_x & 0xf) << 4)
+#define USBDP_CMN16_PLL_PH_SEL_GET(_r) ((_r & 0xf) >> 4)
+#define USBDP_CMN16_PLL_FLD_TOL_MASK (0xf << 0)
+#define USBDP_CMN16_PLL_FLD_TOL_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN16_PLL_FLD_TOL_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R17 (0x005C)
+#define USBDP_CMN17_PLL_RING_CLK_DIV2 (1 << 7)
+#define USBDP_CMN17_PLL_PH_SEL_3B (1 << 6)
+#define USBDP_CMN17_PLL_MOD_CODE_MASK (0x3f << 0)
+#define USBDP_CMN17_PLL_MOD_CODE_SET(_x) ((_x & 0x3f) << 0)
+#define USBDP_CMN17_PLL_MOD_CODE_GET(_r) ((_r & 0x3f) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R18 (0x0060)
+#define USBDP_CMN18_MAN_PLL_PMS_M_MASK (0xff << 0)
+#define USBDP_CMN18_MAN_PLL_PMS_M_SET(_x) ((_x & 0xff) << 0)
+#define USBDP_CMN18_MAN_PLL_PMS_M_GET(_r) ((_r & 0xff) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R19 (0x0064)
+#define USBDP_CMN19_MAN_PLL_PMS_S_MASK (0xf << 4)
+#define USBDP_CMN19_MAN_PLL_PMS_S_SET(_x) ((_x & 0xf) << 4)
+#define USBDP_CMN19_MAN_PLL_PMS_S_GET(_r) ((_r & 0xf) >> 4)
+#define USBDP_CMN19_MAN_PLL_PMS_P_MASK (0xf << 0)
+#define USBDP_CMN19_MAN_PLL_PMS_P_SET(_x) ((_x & 0xf) << 0)
+#define USBDP_CMN19_MAN_PLL_PMS_P_GET(_r) ((_r & 0xf) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R1A (0x0068)
+#define USBDP_CMN1A_MAN_PLL_SDC_LC_0 (1 << 7)
+#define USBDP_CMN1A_MAN_PLL_SDC_K_CODE_MASK (0x3f << 1)
+#define USBDP_CMN1A_MAN_PLL_SDC_K_CODE_SET(_x) ((_x & 0x3f) << 1)
+#define USBDP_CMN1A_MAN_PLL_SDC_K_CODE_GET(_r) ((_r & 0x3f) >> 1)
+#define USBDP_CMN1A_MAN_PLL_SDC_N2 (1 << 0)
+
+#define EXYNOS_USBDP_COM_CMN_R1B (0x006C)
+#define USBDP_CMN1B_MAN_PLL_SDC_N_MASK (0x7 << 1)
+#define USBDP_CMN1B_MAN_PLL_SDC_N_SET(_x) ((_x & 0x7) << 1)
+#define USBDP_CMN1B_MAN_PLL_SDC_N_GET(_r) ((_r & 0x7) >> 1)
+#define USBDP_CMN1B_MAN_PLL_SDC_LC_5_1_MASK (0x1f << 0)
+#define USBDP_CMN1B_MAN_PLL_SDC_LC_5_1_SET(_x) ((_x & 0x1f) << 0)
+#define USBDP_CMN1B_MAN_PLL_SDC_LC_5_1_GET(_r) ((_r & 0x1f) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R1C (0x0070)
+#define USBDP_CMN1C_MAN_PLL_SDM_K_CODE_MASK (0xff << 0)
+#define USBDP_CMN1C_MAN_PLL_SDM_K_CODE_SET(_x) ((_x & 0xff) << 0)
+#define USBDP_CMN1C_MAN_PLL_SDM_K_CODE_GET(_r) ((_r & 0xff) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R1D (0x0074)
+#define USBDP_CMN1D_PLL_ANA_LPF_CISEL_MASK (0xff << 0)
+#define USBDP_CMN1D_PLL_ANA_LPF_CISEL_SET(_x) ((_x & 0xff) << 0)
+#define USBDP_CMN1D_PLL_ANA_LPF_CISEL_GET(_r) ((_r & 0xff) >> 0)
+
+#define EXYNOS_USBDP_COM_CMN_R1E (0x0078)
+#define USBDP_CMN1E_MAN_PLL_SDM_LC_CLR USBDP_COMBO_REG_CLR(0, 8)
+#define USBDP_CMN1E_MAN_PLL_SDM_LC_MASK USBDP_COMBO_REG_MSK(0, 8)
+#define USBDP_CMN1E_MAN_PLL_SDM_LC_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 8)
+#define USBDP_CMN1E_MAN_PLL_SDM_LC_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 8)
+
+#define EXYNOS_USBDP_COM_CMN_R1F (0x007C)
+#define USBDP_CMN1F_MAN_HS_MODE USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_CMN1F_MAN_HS_MODE_EN USBDP_COMBO_REG_MSK(6, 1)
+#define USBDP_CMN1F_CD_RSTN_DLY_CODE_MASK USBDP_COMBO_REG_MSK(3, 3)
+#define USBDP_CMN1F_CD_RSTN_DLY_CODE_SET(_x) USBDP_COMBO_REG_SET(_x, 3, 3)
+#define USBDP_CMN1F_CD_RSTN_DLY_CODE_GET(_R) USBDP_COMBO_REG_GET(_R, 3, 3)
+#define USBDP_CMN1F_PLL_ANA_LPF_CSEL_MASK USBDP_COMBO_REG_MSK(0, 3)
+#define USBDP_CMN1F_PLL_ANA_LPF_CSEL_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 3)
+#define USBDP_CMN1F_PLL_ANA_LPF_CSEL_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 3)
+
+#define EXYNOS_USBDP_COM_CMN_R20 (0x0080)
+#define USBDP_CMN1F_MAN_MDIV_RSTN USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_CMN1F_MAN_PDIV_RSTN USBDP_COMBO_REG_MSK(6, 1)
+#define USBDP_CMN1F_MAN_RSTN_CTRL USBDP_COMBO_REG_MSK(5, 1)
+
+#define EXYNOS_USBDP_COM_CMN_R21 (0x0084)
+
+#define EXYNOS_USBDP_COM_CMN_R22 (0x0088)
+
+#define EXYNOS_USBDP_COM_CMN_R23 (0x008C)
+
+#define EXYNOS_USBDP_COM_CMN_R24 (0x0090)
+#define USBDP_CMN24_PLL_AGMC_MAN_GMSEL_MASK USBDP_COMBO_REG_MSK(4, 4)
+#define USBDP_CMN24_PLL_AGMC_MAN_GMSEL_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 4)
+#define USBDP_CMN24_PLL_AGMC_MAN_GMSEL_GET(_R) USBDP_COMBO_REG_GET(_R, 4, 4)
+#define USBDP_CMN24_PLL_AGMC_GMSEL_EN USBDP_COMBO_REG_MSK(3, 1)
+#define USBDP_CMN24_PLL_DIG_CLK_SEL USBDP_COMBO_REG_MSK(2, 1)
+#define USBDP_CMN24_NDIV_RSTN USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_CMN24_SSC_EN USBDP_COMBO_REG_MSK(0, 1)
+
+#define EXYNOS_USBDP_COM_CMN_R25 (0x0094)
+#define USBDP_CMN25_PLL_AGMC_TG_CODE_MASK USBDP_COMBO_REG_MSK(0, 8)
+#define USBDP_CMN25_PLL_AGMC_TG_CODE_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 8)
+#define USBDP_CMN25_PLL_AGMC_TG_CODE_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 8)
+
+#define EXYNOS_USBDP_COM_CMN_R26 (0x0098)
+#define USBDP_CMN26_PLL_ANA_LC_GM_COMP_CTRL USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_CMN26_PLL_AGMC_COMP_EN USBDP_COMBO_REG_MSK(6, 1)
+#define USBDP_CMN26_PLL_AGMC_GM_ADD_MASK USBDP_COMBO_REG_MSK(4, 2)
+#define USBDP_CMN26_PLL_AGMC_GM_ADD_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 2)
+#define USBDP_CMN26_PLL_AGMC_GM_ADD_GET(_R) USBDP_COMBO_REG_GET(_R, 4, 2)
+#define USBDP_CMN26_PLL_AGMC_FROM_MAX_GM USBDP_COMBO_REG_MSK(3, 1)
+#define USBDP_CMN26_PLL_AFC_FROM_PRE_CODE USBDP_COMBO_REG_MSK(2, 1)
+#define USBDP_CMN26_PLL_AFC_MAN_BSEL_L_MASK USBDP_COMBO_REG_MSK(0, 2)
+#define USBDP_CMN26_PLL_AFC_MAN_BSEL_L_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 2)
+#define USBDP_CMN26_PLL_AFC_MAN_BSEL_L_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 2)
+
+#define EXYNOS_USBDP_COM_CMN_R27 (0x009C)
+#define USBDP_CMN27_PLL_ANA_LC_VREF_BYPASS_EN USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_CMN27_PLL_ANA_LC_VCDO_CAP_OFFSET_SEL_MASK USBDP_COMBO_REG_MSK(4, 3)
+#define USBDP_CMN27_PLL_ANA_LC_VCDO_CAP_OFFSET_SEL_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 3)
+#define USBDP_CMN27_PLL_ANA_LC_VCDO_CAP_OFFSET_SEL_GET(_R) USBDP_COMBO_REG_GET(_R, 4, 3)
+#define USBDP_CMN27_PLL_ANA_LC_VCO_BUFF_EN USBDP_COMBO_REG_MSK(3, 1)
+#define USBDP_CMN27_PLL_ANA_LC_GM_COMP_VCI_SEL_MASK USBDP_COMBO_REG_MSK(0, 3)
+#define USBDP_CMN27_PLL_ANA_LC_GM_COMP_VCI_SEL_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 3)
+#define USBDP_CMN27_PLL_ANA_LC_GM_COMP_VCI_SEL_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 3)
+
+#define EXYNOS_USBDP_COM_CMN_R28 (0x00A0)
+
+#define EXYNOS_USBDP_COM_CMN_R29 (0x00A4)
+
+#define EXYNOS_USBDP_COM_CMN_R2A (0x00A8)
+
+#define EXYNOS_USBDP_COM_CMN_R2B (0x00AC)
+
+#define EXYNOS_USBDP_COM_CMN_R2C (0x00B0)
+#define USBDP_CMN2C_NO_DELAY_PLL_LOCK_DONE_EN USBDP_COMBO_REG_MSK(4, 1)
+#define USBDP_CMN2C_BGR_ATB_SEL USBDP_COMBO_REG_MSK(3, 1)
+#define USBDP_CMN2C_MAN_USBDP_MODE_MASK USBDP_COMBO_REG_MSK(1, 2)
+#define USBDP_CMN2C_MAN_USBDP_MODE_SET(_x) USBDP_COMBO_REG_SET(_x, 1, 2)
+#define USBDP_CMN2C_MAN_USBDP_MODE_GET(_x) USBDP_COMBO_REG_GET(_x, 1, 2)
+#define USBDP_CMN2C_MAN_USBDP_MODE_EN USBDP_COMBO_REG_MSK(0, 1)
+
+#define EXYNOS_USBDP_COM_CMN_R2D (0x00B4)
+#define USBDP_CMN2D_USB_TX1_SEL USBDP_COMBO_REG_MSK(5, 1)
+#define USBDP_CMN2D_USB_TX3_SEL USBDP_COMBO_REG_MSK(4, 1)
+#define USBDP_CMN2D_LCPLL_SSCDIV_MASK USBDP_COMBO_REG_MSK(0, 4)
+#define USBDP_CMN2D_LCPLL_SSCDIV_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 4)
+#define USBDP_CMN2D_LCPLL_SSCDIV_GET(_x) USBDP_COMBO_REG_GET(_x, 0, 4)
+
+#define EXYNOS_USBDP_COM_CMN_R2E (0x00B8)
+
+#define EXYNOS_USBDP_COM_CMN_R2F (0x00BC)
+#define USBDP_CMN2F_PLL_MON_SEL_BAND_M_MASK USBDP_COMBO_REG_MSK(4, 4)
+#define USBDP_CMN2F_PLL_MON_SEL_BAND_M_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 4)
+#define USBDP_CMN2F_PLL_MON_SEL_BAND_M_GET(_x) USBDP_COMBO_REG_GET(_x, 4, 4)
+#define USBDP_CMN2F_PLL_MON_SEL_BAND_I_MASK USBDP_COMBO_REG_MSK(2, 2)
+#define USBDP_CMN2F_PLL_MON_SEL_BAND_I_SET(_x) USBDP_COMBO_REG_SET(_x, 2, 2)
+#define USBDP_CMN2F_PLL_MON_SEL_BAND_I_GET(_x) USBDP_COMBO_REG_GET(_x, 2, 2)
+#define USBDP_CMN2F_PLL_AFC_DONE USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_CMN2F_PLL_LOCK_DONE USBDP_COMBO_REG_MSK(0, 1)
+
+#endif /* _PHY_EXYNOS_USBDP_R_CMN_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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 DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USBDP_REG_DP_H_
+#define DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USBDP_REG_DP_H_
+
+#define EXYNOS_USBDP_COM_DP_RB3 (0x0ACC)
+#define USBDP_DPB3_CMN_DUMMY_CTRL_7 USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_DPB3_CMN_DUMMY_CTRL_6 USBDP_COMBO_REG_MSK(6, 1)
+#define USBDP_DPB3_CMN_DUMMY_CTRL_5 USBDP_COMBO_REG_MSK(5, 1)
+#define USBDP_DPB3_CMN_DUMMY_CTRL_4 USBDP_COMBO_REG_MSK(4, 1)
+#define USBDP_DPB3_CMN_DUMMY_CTRL_3 USBDP_COMBO_REG_MSK(3, 1)
+#define USBDP_DPB3_CMN_DUMMY_CTRL_2 USBDP_COMBO_REG_MSK(2, 1)
+#define USBDP_DPB3_CMN_DUMMY_CTRL_1 USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_DPB3_CMN_DUMMY_CTRL_0 USBDP_COMBO_REG_MSK(0, 1)
+
+
+#endif /* DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USBDP_REG_DP_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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 _CAL_PHY_EXYNOS_USBDP_REG_PCS_H_
+#define _CAL_PHY_EXYNOS_USBDP_REG_PCS_H_
+
+#define USBDP_PCSREG_FRONT_END_MODE_VEC 0x0200
+#define USBDP_PCSREG_RUN_LENGTH_TH USBDP_COMBO_REG_MSK(4, 1)
+#define USBDP_PCSREG_EN_DRAIN_AFTER_RX_VAL_FALL USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_PCSREG_EN_REALIGN USBDP_COMBO_REG_MSK(0, 1)
+
+#define USBDP_PCSREG_DET_COMP_EN_SET 0x0300
+#define USBDP_PCSREG_COMP_EN_ASSERT_MASK USBDP_COMBO_REG_MSK(0, 6)
+#define USBDP_PCSREG_COMP_EN_ASSERT_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 6)
+#define USBDP_PCSREG_COMP_EN_ASSERT_GET(_x) USBDP_COMBO_REG_GET(_R, 0, 6)
+
+
+
+
+#endif /* _CAL_PHY_EXYNOS_USBDP_REG_PCS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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 DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USBDP_REG_TRSV_H_
+#define DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USBDP_REG_TRSV_H_
+
+#define EXYNOS_USBDP_COM_TRSV_R01 (0x00c4)
+#define USBDP_TRSV01_RXAFE_LEQ_ISEL_GEN2_MASK USBDP_COMBO_REG_MSK(6, 2)
+#define USBDP_TRSV01_RXAFE_LEQ_ISEL_GEN2_SET(_x) USBDP_COMBO_REG_SET(_x, 6, 2)
+#define USBDP_TRSV01_RXAFE_LEQ_ISEL_GEN2_GET(_R) USBDP_COMBO_REG_GET(_R, 6, 2)
+#define USBDP_TRSV01_RXAFE_LEQ_ISEL_GEN1_MASK USBDP_COMBO_REG_MSK(4, 2)
+#define USBDP_TRSV01_RXAFE_LEQ_ISEL_GEN1_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 2)
+#define USBDP_TRSV01_RXAFE_LEQ_ISEL_GEN1_GET(_R) USBDP_COMBO_REG_GET(_R, 4, 2)
+#define USBDP_TRSV01_RXAFE_CTLE_SEL_MASK USBDP_COMBO_REG_MSK(2, 2)
+#define USBDP_TRSV01_RXAFE_CTLE_SEL_SET(_x) USBDP_COMBO_REG_SET(_x, 2, 2)
+#define USBDP_TRSV01_RXAFE_CTLE_SEL_GET(_R) USBDP_COMBO_REG_GET(_R, 2, 2)
+#define USBDP_TRSV01_RXAFE_SCLBUF_EN_MASK USBDP_COMBO_REG_MSK(0, 2)
+#define USBDP_TRSV01_RXAFE_SCLBUF_EN_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 2)
+#define USBDP_TRSV01_RXAFE_SCLBUF_EN_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 2)
+
+#define EXYNOS_USBDP_COM_TRSV_R02 (0x00c8)
+#define USBDP_TRSV02_RXAFE_LEQ_CSEL_GEN2_MASK USBDP_COMBO_REG_MSK(4, 4)
+#define USBDP_TRSV02_RXAFE_LEQ_CSEL_GEN2_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 4)
+#define USBDP_TRSV02_RXAFE_LEQ_CSEL_GEN2_GET(_R) USBDP_COMBO_REG_GET(_R, 4, 4)
+#define USBDP_TRSV02_RXAFE_LEQ_CSEL_GEN1_MASK USBDP_COMBO_REG_MSK(0, 4)
+#define USBDP_TRSV02_RXAFE_LEQ_CSEL_GEN1_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 4)
+#define USBDP_TRSV02_RXAFE_LEQ_CSEL_GEN1_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 4)
+
+#define EXYNOS_USBDP_COM_TRSV_R03 (0x00cc)
+#define USBDP_TRSV03_RXAFE_TERM_MODE USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_TRSV03_RXAFE_EQ_AMP3BUF_EN USBDP_COMBO_REG_MSK(6, 1)
+#define USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN2_MASK USBDP_COMBO_REG_MSK(3, 3)
+#define USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN2_SET(_x) USBDP_COMBO_REG_SET(_x, 3, 3)
+#define USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN2_GET(_R) USBDP_COMBO_REG_GET(_R, 3, 3)
+#define USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN1_MASK USBDP_COMBO_REG_MSK(0, 3)
+#define USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN1_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 3)
+#define USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN1_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 3)
+
+#define EXYNOS_USBDP_COM_TRSV_R04 (0x00d0)
+#define USBDP_TRSV04_RXAFE_TUNE_MASK USBDP_COMBO_REG_MSK(4, 4)
+#define USBDP_TRSV04_RXAFE_TUNE_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 4)
+#define USBDP_TRSV04_RXAFE_TUNE_GET(_R) USBDP_COMBO_REG_GET(_R, 4, 4)
+#define USBDP_TRSV04_RXAFE_SQ_VFFSET_CTRL_MASK USBDP_COMBO_REG_MSK(0, 4)
+#define USBDP_TRSV04_RXAFE_SQ_VFFSET_CTRL_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 4)
+#define USBDP_TRSV04_RXAFE_SQ_VFFSET_CTRL_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 4)
+
+#define EXYNOS_USBDP_COM_TRSV_R0A (0x00E8)
+#define USBDP_TRSV0A_APB_CAL_OFFSET_DIFP_MASK USBDP_COMBO_REG_MSK(4, 4)
+#define USBDP_TRSV0A_APB_CAL_OFFSET_DIFP_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 4)
+#define USBDP_TRSV0A_APB_CAL_OFFSET_DIFP_GET(_R) USBDP_COMBO_REG_GET(_R, 4, 4)
+
+#define EXYNOS_USBDP_COM_TRSV_R0B (0x00EC)
+#define USBDP_TRSV0B_APB_CAL_OFFSET_DIFN_MASK USBDP_COMBO_REG_MSK(0, 4)
+#define USBDP_TRSV0B_APB_CAL_OFFSET_DIFN_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 4)
+#define USBDP_TRSV0B_APB_CAL_OFFSET_DIFN_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 4)
+
+#define EXYNOS_USBDP_COM_TRSV_R0C (0x00F0)
+#define USBDP_TRSV0C_MAN_TX_DE_EMP_LVL_MASK USBDP_COMBO_REG_MSK(4, 4)
+#define USBDP_TRSV0C_MAN_TX_DE_EMP_LVL_SET(_x) USBDP_COMBO_REG_SET(_x, 4, 4)
+#define USBDP_TRSV0C_MAN_TX_DE_EMP_LVL_GET(_R) USBDP_COMBO_REG_GET(_R, 4, 4)
+#define USBDP_TRSV0C_MAN_TX_DRVR_LVL_MASK USBDP_COMBO_REG_MSK(0, 4)
+#define USBDP_TRSV0C_MAN_TX_DRVR_LVL_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 4)
+#define USBDP_TRSV0C_MAN_TX_DRVR_LVL_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 4)
+
+#define EXYNOS_USBDP_COM_TRSV_R0D (0x00F4)
+#define USBDP_TRSV0D_TX_RCV_COMB_CTRL_MASK USBDP_COMBO_REG_MSK(6, 2)
+#define USBDP_TRSV0D_TX_RCV_COMB_CTRL_SET(_x) USBDP_COMBO_REG_SET(_x, 6, 2)
+#define USBDP_TRSV0D_TX_RCV_COMB_CTRL_GET(_R) USBDP_COMBO_REG_GET(_R, 6, 2)
+#define USBDP_TRSV0D_TX_LINE_LB_SEL USBDP_COMBO_REG_MSK(5, 1)
+#define USBDP_TRSV0D_TX_LINE_LB_EN USBDP_COMBO_REG_MSK(4, 1)
+#define USBDP_TRSV0D_TXHSCK_MON_EN USBDP_COMBO_REG_MSK(3, 1)
+#define USBDP_TRSV0D_EDP_MODE_SEL USBDP_COMBO_REG_MSK(2, 1)
+#define USBDP_TRSV0D_TX_DESKEW_BYPASS USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_TRSV0D_MAN_DRVR_DE_EMP_LVL_MAN_EN USBDP_COMBO_REG_MSK(0, 1)
+
+#define EXYNOS_USBDP_COM_TRSV_R23 (0x014C)
+#define USBDP_TRSV23_DATA_CLEAR_BY_SIGVAL USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_TRSV23_DESKEW_CHK_BYPASS USBDP_COMBO_REG_MSK(6, 1)
+#define USBDP_TRSV23_TX_FIX_DB USBDP_COMBO_REG_MSK(5, 1)
+#define USBDP_TRSV23_TX_FIX_DA USBDP_COMBO_REG_MSK(4, 1)
+#define USBDP_TRSV23_FBB_H_BW_DIFF_MASK USBDP_COMBO_REG_MSK(0, 4)
+#define USBDP_TRSV23_FBB_H_BW_DIFF_SET(_x) USBDP_COMBO_REG_SET(_x, 0, 4)
+#define USBDP_TRSV23_FBB_H_BW_DIFF_GET(_R) USBDP_COMBO_REG_GET(_R, 0, 4)
+
+#define EXYNOS_USBDP_COM_TRSV_R24 (0x0150)
+#define USBDP_TRSV24_MAN_TX_RCV_DET_EN USBDP_COMBO_REG_MSK(7, 1)
+
+#define EXYNOS_USBDP_COM_TRSV_R27 (0x015C)
+#define USBDP_TRSV27_MAN_EN_CTRL USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_TRSV27_MAN_TX_SER_RSTN USBDP_COMBO_REG_MSK(6, 1)
+#define USBDP_TRSV27_MAN_DESKEW_RSTN USBDP_COMBO_REG_MSK(5, 1)
+#define USBDP_TRSV27_MAN_CDR_AFC_RSTN USBDP_COMBO_REG_MSK(4, 1)
+#define USBDP_TRSV27_MAN_CDR_AFC_INIT_RSTN USBDP_COMBO_REG_MSK(3, 1)
+#define USBDP_TRSV27_MAN_VALID_RSTN USBDP_COMBO_REG_MSK(2, 1)
+#define USBDP_TRSV27_MAN_CDR_DES_RSTN USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_TRSV27_MAN_RSTN_EN USBDP_COMBO_REG_MSK(0, 1)
+
+#define EXYNOS_USBDP_COM_TRSV_R34 (0x0190)
+#define USBDP_TRSV34_SIGVAL_FILT_DLY_CODE_MASK USBDP_COMBO_REG_MSK(2, 2)
+#define USBDP_TRSV34_SIGVAL_FILT_DLY_CODE_SET(_x) USBDP_COMBO_REG_SET(_x, 2, 2)
+#define USBDP_TRSV34_SIGVAL_FILT_DLY_CODE_GET(_R) USBDP_COMBO_REG_GET(_R, 2, 2)
+#define USBDP_TRSV34_OUT_SIGVAL_FILT_SEL USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_TRSV34_INT_SIGVAL_FILT_SEL USBDP_COMBO_REG_MSK(0, 1)
+
+#define EXYNOS_USBDP_COM_TRSV_R38 (0x01A0)
+#define USBDP_TRSV38_SFR_RX_LFPS_LPF_CTRL_MASK USBDP_COMBO_REG_MSK(5, 2)
+#define USBDP_TRSV38_SFR_RX_LFPS_LPF_CTRL_SET(_x) USBDP_COMBO_REG_SET(_x, 5, 2)
+#define USBDP_TRSV38_SFR_RX_LFPS_LPF_CTRL_GET(_R) USBDP_COMBO_REG_GET(_R, 5, 2)
+#define USBDP_TRSV38_SFR_RX_LFPS_TH_CTRL_MASK USBDP_COMBO_REG_MSK(2, 3)
+#define USBDP_TRSV38_SFR_RX_LFPS_TH_CTRL_SET(_x) USBDP_COMBO_REG_SET(_x, 2, 3)
+#define USBDP_TRSV38_SFR_RX_LFPS_TH_CTRL_GET(_R) USBDP_COMBO_REG_GET(_R, 2, 3)
+#define USBDP_TRSV38_SFR_RX_LFPS_COMP_I_CTRL USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_TRSV38_RX_LFPS_DET_EN USBDP_COMBO_REG_MSK(0, 1)
+
+#define EXYNOS_USBDP_COM_TRSV_R4B (0x01EC)
+#define USBDP_TRSV4B_CDR_AFC_DONE USBDP_COMBO_REG_MSK(5, 1)
+#define USBDP_TRSV4B_RX_CAL_DONE USBDP_COMBO_REG_MSK(4, 1)
+#define USBDP_TRSV4B_CDR_FLD_PLL_MODE_DONE USBDP_COMBO_REG_MSK(3, 1)
+#define USBDP_TRSV4B_BIST_COMP_TEST USBDP_COMBO_REG_MSK(2, 1)
+#define USBDP_TRSV4B_BIST_ERR_INJ_TEST USBDP_COMBO_REG_MSK(1, 1)
+#define USBDP_TRSV4B_BIST_COMP_START USBDP_COMBO_REG_MSK(0, 1)
+
+#define EXYNOS_USBDP_COM_TRSV_R59 (0x0224)
+#define USBDP_TRSV59_TX_JEQ_EN USBDP_COMBO_REG_MSK(7, 1)
+#define USBDP_TRSV59_TX_DRV_PLL_REF_MON_EN USBDP_COMBO_REG_MSK(6, 1)
+#define USBDP_TRSV59_TX_DRV_IDRV_IUP_CTRL_MASK USBDP_COMBO_REG_MSK(3, 3)
+#define USBDP_TRSV59_TX_DRV_IDRV_IUP_CTRL_SET(_x) USBDP_COMBO_REG_SET(_x, 3, 3)
+#define USBDP_TRSV59_TX_DRV_IDRV_IUP_CTRL_GET(_R) USBDP_COMBO_REG_GET(_R, 3, 3)
+#define USBDP_TRSV59_TX_DRV_IDRV_EN USBDP_COMBO_REG_MSK(2, 1)
+#define USBDP_TRSV59_TX_DRV_ACCDRV_EN USBDP_COMBO_REG_MSK(1, 1)
+
+#define EXYNOS_USBDP_COM_TRSV_R5A (0x0228)
+#define USBDP_TRSV5A_TX_DRV_ACCDRV_CTRL_MASK USBDP_COMBO_REG_MSK(5, 3)
+#define USBDP_TRSV5A_TX_DRV_ACCDRV_CTRL_SET(_x) USBDP_COMBO_REG_SET(_x, 5, 3)
+#define USBDP_TRSV5A_TX_DRV_ACCDRV_CTRL_GET(_R) USBDP_COMBO_REG_GET(_R, 5, 3)
+
+#endif /* DRIVER_USB_USBPHY_CAL_PHY_EXYNOS_USBDP_REG_TRSV_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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 _PHY_EXYNOS_USBDP_REG_H_
+#define _PHY_EXYNOS_USBDP_REG_H_
+
+#define USBDP_COMBO_BIT_MASK(_bw) ((1 << _bw) - 1)
+
+#define USBDP_COMBO_REG_MSK(_pos, _B) (USBDP_COMBO_BIT_MASK(_B) << _pos)
+#define USBDP_COMBO_REG_CLR(_pos, _B) (~(USBDP_COMBO_BIT_MASK(_B) << _pos))
+#define USBDP_COMBO_REG_SET(_x, _pos, _B) ((_x & USBDP_COMBO_BIT_MASK(_B)) << _pos)
+#define USBDP_COMBO_REG_GET(_x, _pos, _B) ((_x & (USBDP_COMBO_BIT_MASK(_B) << _pos)) >> _pos)
+
+#include "phy-exynos-usbdp-reg-cmn.h"
+#include "phy-exynos-usbdp-reg-trsv.h"
+#include "phy-exynos-usbdp-reg-dp.h"
+#include "phy-exynos-usbdp-reg-pcs.h"
+
+#endif /* _PHY_EXYNOS_USBDP_REG_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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 <linux/delay.h>
+#include <linux/io.h>
+#include "phy-samsung-usb-cal.h"
+#include "phy-exynos-usbdp-reg.h"
+
+void phy_exynos_usbdp_tune_each(struct exynos_usbphy_info *info, char *name,
+ int val)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+
+ if (!name)
+ return;
+
+ if (!strcmp(name, "sstx_deemph")) {
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R0C);
+ reg &= ~USBDP_TRSV0C_MAN_TX_DE_EMP_LVL_MASK;
+ reg |= USBDP_TRSV0C_MAN_TX_DE_EMP_LVL_SET(val);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R0C);
+ } else if (!strcmp(name, "sstx_amp")) {
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R0C);
+ reg &= ~USBDP_TRSV0C_MAN_TX_DRVR_LVL_MASK;
+ reg |= USBDP_TRSV0C_MAN_TX_DRVR_LVL_SET(val);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R0C);
+ } else if (!strcmp(name, "ssrx_los")) {
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R0A);
+ reg &= ~USBDP_TRSV0A_APB_CAL_OFFSET_DIFP_MASK;
+ reg |= USBDP_TRSV0A_APB_CAL_OFFSET_DIFP_SET(val);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R0A);
+
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R0B);
+ reg &= ~USBDP_TRSV0B_APB_CAL_OFFSET_DIFN_MASK;
+ reg |= USBDP_TRSV0B_APB_CAL_OFFSET_DIFN_SET(val);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R0B);
+ } else if (!strcmp(name, "ssrx_ctle")) {
+ reg = USBDP_TRSV02_RXAFE_LEQ_CSEL_GEN2_SET(0x2);
+ reg |= USBDP_TRSV02_RXAFE_LEQ_CSEL_GEN1_SET(0x7);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R02);
+
+ reg = USBDP_TRSV03_RXAFE_TERM_MODE;
+ reg &= ~USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN2_MASK;
+ reg |= USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN2_SET(0x7);
+ reg &= ~USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN1_MASK;
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R03);
+
+ reg = USBDP_TRSV04_RXAFE_TUNE_SET(val);
+ reg |= USBDP_TRSV04_RXAFE_SQ_VFFSET_CTRL_SET(val);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R04);
+ }
+}
+
+void phy_exynos_usbdp_tune(struct exynos_usbphy_info *info)
+{
+ u32 cnt = 0;
+
+ for (; info->tune_param[cnt].value != EXYNOS_USB_TUNE_LAST; cnt++) {
+ char *para_name;
+ int val;
+
+ val = info->tune_param[cnt].value;
+ if (val == -1)
+ continue;
+ para_name = info->tune_param[cnt].name;
+ if (!para_name)
+ break;
+ phy_exynos_usbdp_tune_each(info, para_name, val);
+ }
+}
+
+void phy_exynos_usbdp_ilbk(struct exynos_usbphy_info *info)
+{
+
+}
+
+void phy_exynos_usbdp_enable(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg, reg_2c, reg_2d, reg_dp_b3;
+
+ /* Recevier Detection Off */
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R24);
+ reg |= USBDP_TRSV24_MAN_TX_RCV_DET_EN;
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R24);
+
+ /* Set Proper Value for PCS to avoid abnormal RX Detect */
+ reg = readl(info->regs_base_2nd + USBDP_PCSREG_FRONT_END_MODE_VEC);
+ reg |= USBDP_PCSREG_EN_REALIGN;
+ writel(reg, info->regs_base_2nd + USBDP_PCSREG_FRONT_END_MODE_VEC);
+
+ reg = USBDP_PCSREG_COMP_EN_ASSERT_SET(0x3f);
+ writel(reg, info->regs_base_2nd + USBDP_PCSREG_DET_COMP_EN_SET);
+
+ reg = 0;
+ reg |= USBDP_CMN0E_PLL_AFC_VCO_CNT_RUN_NO_SET(0x4);
+ reg |= USBDP_CMN0E_PLL_AFC_ANA_CPI_CTRL_SET(0x2);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_CMN_R0E);
+
+ reg = 0;
+ reg |= USBDP_CMN0F_PLL_ANA_EN_PI;
+ reg |= USBDP_CMN0F_PLL_ANA_DCC_EN_SET(0xf);
+ reg |= USBDP_CMN0F_PLL_ANA_CPP_CTRL_SET(0x7);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_CMN_R0F);
+
+ reg = 0;
+ reg |= USBDP_CMN10_PLL_ANA_VCI_SEL_SET(0x6);
+ reg |= USBDP_CMN10_PLL_ANA_LPF_RSEL_SET(0x8);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_CMN_R10);
+
+ reg = 0;
+ reg |= USBDP_CMN25_PLL_AGMC_TG_CODE_SET(0x30);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_CMN_R25);
+
+ reg = 0;
+ reg |= USBDP_CMN26_PLL_AGMC_COMP_EN;
+ reg |= USBDP_CMN26_PLL_AGMC_FROM_MAX_GM;
+ reg |= USBDP_CMN26_PLL_AFC_FROM_PRE_CODE;
+ reg |= USBDP_CMN26_PLL_AFC_MAN_BSEL_L_SET(0x3);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_CMN_R26);
+
+ reg = 0;
+ reg |= USBDP_CMN27_PLL_ANA_LC_VREF_BYPASS_EN;
+ reg |= USBDP_CMN27_PLL_ANA_LC_VCDO_CAP_OFFSET_SEL_SET(0x5);
+ reg |= USBDP_CMN27_PLL_ANA_LC_VCO_BUFF_EN;
+ reg |= USBDP_CMN27_PLL_ANA_LC_GM_COMP_VCI_SEL_SET(0x4);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_CMN_R27);
+
+ reg = 0;
+ reg |= USBDP_TRSV23_DATA_CLEAR_BY_SIGVAL;
+ reg |= USBDP_TRSV23_FBB_H_BW_DIFF_SET(0x5);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R23);
+
+ /* SSC Setting */
+ reg = readl(regs_base + EXYNOS_USBDP_COM_CMN_R24);
+ reg |= USBDP_CMN24_SSC_EN;
+ writel(reg, regs_base + EXYNOS_USBDP_COM_CMN_R24);
+
+ reg_2c = readl(regs_base + EXYNOS_USBDP_COM_CMN_R2C);
+ reg_2d = readl(regs_base + EXYNOS_USBDP_COM_CMN_R2D);
+ reg_dp_b3 = readl(regs_base + EXYNOS_USBDP_COM_DP_RB3);
+ if (info->used_phy_port == 0) {
+ reg_2c |= USBDP_CMN2C_MAN_USBDP_MODE_SET(0x3);
+ reg_2d |= USBDP_CMN2D_USB_TX1_SEL;
+ reg_2d &= ~USBDP_CMN2D_USB_TX3_SEL;
+ reg_dp_b3 &= ~USBDP_DPB3_CMN_DUMMY_CTRL_7;
+ reg_dp_b3 |= USBDP_DPB3_CMN_DUMMY_CTRL_6;
+ reg_dp_b3 |= USBDP_DPB3_CMN_DUMMY_CTRL_1;
+ reg_dp_b3 |= USBDP_DPB3_CMN_DUMMY_CTRL_0;
+ } else {
+ reg_2c &= ~USBDP_CMN2C_MAN_USBDP_MODE_MASK;
+ reg_2d &= ~USBDP_CMN2D_USB_TX1_SEL;
+ reg_2d |= USBDP_CMN2D_USB_TX3_SEL;
+ reg_dp_b3 |= USBDP_DPB3_CMN_DUMMY_CTRL_7;
+ reg_dp_b3 &= ~USBDP_DPB3_CMN_DUMMY_CTRL_6;
+ reg_dp_b3 &= ~USBDP_DPB3_CMN_DUMMY_CTRL_1;
+ reg_dp_b3 &= ~USBDP_DPB3_CMN_DUMMY_CTRL_0;
+ }
+ reg_2c |= USBDP_CMN2C_MAN_USBDP_MODE_EN;
+ reg_2d &= ~USBDP_CMN2D_LCPLL_SSCDIV_MASK;
+ reg_2d |= USBDP_CMN2D_LCPLL_SSCDIV_SET(0x1);
+ writel(reg_2c, regs_base + EXYNOS_USBDP_COM_CMN_R2C);
+ writel(reg_2d, regs_base + EXYNOS_USBDP_COM_CMN_R2D);
+ writel(reg_dp_b3, regs_base + EXYNOS_USBDP_COM_DP_RB3);
+
+ reg = 0;
+ reg |= USBDP_TRSV38_SFR_RX_LFPS_LPF_CTRL_SET(0x2);
+ reg |= USBDP_TRSV38_SFR_RX_LFPS_TH_CTRL_SET(0x2);
+ reg |= USBDP_TRSV38_RX_LFPS_DET_EN;
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R38);
+
+ /* RX EQ tuning */
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R01);
+ reg &= ~USBDP_TRSV01_RXAFE_CTLE_SEL_MASK;
+ reg |= USBDP_TRSV01_RXAFE_CTLE_SEL_SET(0x3);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R01);
+
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R03);
+ reg &= ~USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN1_MASK;
+ reg &= ~USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN2_MASK;
+ reg |= USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN1_SET(0x0);
+ reg |= USBDP_TRSV03_RXAFE_LEQ_RSEL_GEN2_SET(0x5);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R03);
+
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R0D);
+ reg |= USBDP_TRSV0D_MAN_DRVR_DE_EMP_LVL_MAN_EN;
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R0D);
+
+ /* Sys Valid Debouncd Digital Filter */
+ reg = 0;
+ reg |= USBDP_TRSV34_INT_SIGVAL_FILT_SEL;
+ reg |= USBDP_TRSV34_OUT_SIGVAL_FILT_SEL;
+ reg |= USBDP_TRSV34_SIGVAL_FILT_DLY_CODE_SET(0x3);
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R34);
+
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R27);
+ reg |= USBDP_TRSV27_MAN_RSTN_EN;
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R27);
+
+ /* Recevier Detection On */
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R24);
+ reg &= ~USBDP_TRSV24_MAN_TX_RCV_DET_EN;
+ writel(reg, regs_base + EXYNOS_USBDP_COM_TRSV_R24);
+
+ /* Set Tune Value */
+ phy_exynos_usbdp_tune(info);
+}
+
+int phy_exynos_usbdp_check_pll_lock(struct exynos_usbphy_info *info)
+{
+ void __iomem *regs_base = info->regs_base;
+ u32 reg;
+
+ reg = readl(regs_base + EXYNOS_USBDP_COM_CMN_R2F);
+ printk("CMN_2F(0x%p) : 0x%x\n",
+ regs_base + EXYNOS_USBDP_COM_CMN_R2F,
+ reg);
+ if (!(reg & USBDP_CMN2F_PLL_LOCK_DONE))
+ return -1;
+
+ reg = readl(regs_base + EXYNOS_USBDP_COM_TRSV_R4B);
+ printk("TRSV_4B(0x%p) : 0x%x\n",
+ regs_base + EXYNOS_USBDP_COM_TRSV_R4B,
+ reg);
+ if (!(reg & USBDP_TRSV4B_CDR_FLD_PLL_MODE_DONE))
+ return -1;
+
+ return 0;
+}
+
+void phy_exynos_usbdp_disable(struct exynos_usbphy_info *info)
+{
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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 PHY_EXYNOS_USBDP_H_
+#define PHY_EXYNOS_USBDP_H_
+
+
+extern void phy_exynos_usbdp_enable(struct exynos_usbphy_info *);
+extern void phy_exynos_usbdp_ilbk(struct exynos_usbphy_info *info);
+extern int phy_exynos_usbdp_check_pll_lock(struct exynos_usbphy_info *info);
+extern void phy_exynos_usbdp_disable(struct exynos_usbphy_info *);
+extern void phy_exynos_usbdp_tune_each(struct exynos_usbphy_info *, char *, int);
+extern void phy_exynos_usbdp_tune(struct exynos_usbphy_info *info);
+extern void exynos_usbdrd_request_phy_isol(void);
+extern int exynos_usbdrd_inform_dp_use(int use, int lane_cnt);
+
+#endif /* PHY_EXYNOS_USBDP_H_ */
--- /dev/null
+/*
+ * Samsung EXYNOS SoC series USB DRD PHY driver
+ *
+ * Phy provider for USB 3.0 DRD controller on Exynos SoC series
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd.
+ * Author: Vivek Gautam <gautam.vivek@samsung.com>
+ * Minho Lee <minho55.lee@samsung.com>
+ *
+ * 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/exynos5-pmu.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/samsung_usb.h>
+#include <linux/usb/otg.h>
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+#include <linux/exynos_otp.h>
+#endif
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
+
+#include "phy-exynos-usbdrd.h"
+#include "phy-exynos-debug.h"
+
+static void __iomem *usbdp_combo_phy_reg;
+static int phy_isol_delayed, dp_use_informed;
+static struct regmap *reg_pmu_delayed;
+static u32 pmu_offset_delayed, pmu_offset_dp_delayed;
+
+static int exynos_usbdrd_clk_prepare(struct exynos_usbdrd_phy *phy_drd)
+{
+ int i;
+ int ret;
+
+ for (i = 0; phy_drd->clocks[i] != NULL; i++) {
+ ret = clk_prepare(phy_drd->clocks[i]);
+ if (ret)
+ goto err;
+ }
+
+ if (phy_drd->use_phy_umux) {
+ for (i = 0; phy_drd->phy_clocks[i] != NULL; i++) {
+ ret = clk_prepare(phy_drd->phy_clocks[i]);
+ if (ret)
+ goto err1;
+ }
+ }
+ return 0;
+err:
+ for (i = i - 1; i >= 0; i--)
+ clk_unprepare(phy_drd->clocks[i]);
+ return ret;
+err1:
+ for (i = i - 1; i >= 0; i--)
+ clk_unprepare(phy_drd->phy_clocks[i]);
+ return ret;
+}
+
+static int exynos_usbdrd_clk_enable(struct exynos_usbdrd_phy *phy_drd,
+ bool umux)
+{
+ int i;
+ int ret;
+
+ if (!umux) {
+ for (i = 0; phy_drd->clocks[i] != NULL; i++) {
+ ret = clk_enable(phy_drd->clocks[i]);
+ if (ret)
+ goto err;
+ }
+ } else {
+ for (i = 0; phy_drd->phy_clocks[i] != NULL; i++) {
+ ret = clk_enable(phy_drd->phy_clocks[i]);
+ if (ret)
+ goto err1;
+ }
+ }
+ return 0;
+err:
+ for (i = i - 1; i >= 0; i--)
+ clk_disable(phy_drd->clocks[i]);
+ return ret;
+err1:
+ for (i = i - 1; i >= 0; i--)
+ clk_disable(phy_drd->phy_clocks[i]);
+ return ret;
+}
+
+static void exynos_usbdrd_clk_unprepare(struct exynos_usbdrd_phy *phy_drd)
+{
+ int i;
+
+ for (i = 0; phy_drd->clocks[i] != NULL; i++)
+ clk_unprepare(phy_drd->clocks[i]);
+ for (i = 0; phy_drd->phy_clocks[i] != NULL; i++)
+ clk_unprepare(phy_drd->phy_clocks[i]);
+}
+
+static void exynos_usbdrd_clk_disable(struct exynos_usbdrd_phy *phy_drd, bool umux)
+{
+ int i;
+
+ if (!umux) {
+ for (i = 0; phy_drd->clocks[i] != NULL; i++)
+ clk_disable(phy_drd->clocks[i]);
+ } else {
+ for (i = 0; phy_drd->phy_clocks[i] != NULL; i++)
+ clk_disable(phy_drd->phy_clocks[i]);
+ }
+}
+static int exynos_usbdrd_phyclk_get(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ const char **phyclk_ids;
+ const char **clk_ids;
+ const char *refclk_name;
+ struct clk *clk;
+ int phyclk_count;
+ int clk_count;
+ bool is_phyclk = false;
+ int clk_index = 0;
+ int i, j, ret;
+
+ phyclk_count = of_property_count_strings(dev->of_node, "phyclk_mux");
+ if (IS_ERR_VALUE((unsigned long)phyclk_count)) {
+ dev_err(dev, "invalid phyclk list in %s node\n",
+ dev->of_node->name);
+ return -EINVAL;
+ }
+
+ phyclk_ids = (const char **)devm_kmalloc(dev,
+ (phyclk_count+1) * sizeof(const char *),
+ GFP_KERNEL);
+ for (i = 0; i < phyclk_count; i++) {
+ ret = of_property_read_string_index(dev->of_node,
+ "phyclk_mux", i, &phyclk_ids[i]);
+ if (ret) {
+ dev_err(dev, "failed to read phyclk_mux name %d from %s node\n",
+ i, dev->of_node->name);
+ return ret;
+ }
+ }
+ phyclk_ids[phyclk_count] = NULL;
+
+ if (!strcmp("none", phyclk_ids[0])) {
+ dev_info(dev, "don't need user Mux for phyclk\n");
+ phy_drd->use_phy_umux = false;
+ phyclk_count = 0;
+
+ } else {
+ phy_drd->use_phy_umux = true;
+
+ phy_drd->phy_clocks = (struct clk **) devm_kmalloc(dev,
+ (phyclk_count+1) * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!phy_drd->phy_clocks) {
+ dev_err(dev, "failed to alloc : phy clocks\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; phyclk_ids[i] != NULL; i++) {
+ clk = devm_clk_get(dev, phyclk_ids[i]);
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "couldn't get %s clock\n", phyclk_ids[i]);
+ return -EINVAL;
+ }
+ phy_drd->phy_clocks[i] = clk;
+ }
+
+ phy_drd->phy_clocks[i] = NULL;
+ }
+
+ clk_count = of_property_count_strings(dev->of_node, "clock-names");
+ if (IS_ERR_VALUE((unsigned long)clk_count)) {
+ dev_err(dev, "invalid clk list in %s node", dev->of_node->name);
+ return -EINVAL;
+ }
+ clk_ids = (const char **)devm_kmalloc(dev,
+ (clk_count + 1) * sizeof(const char *),
+ GFP_KERNEL);
+ for (i = 0; i < clk_count; i++) {
+ ret = of_property_read_string_index(dev->of_node, "clock-names",
+ i, &clk_ids[i]);
+ if (ret) {
+ dev_err(dev, "failed to read clocks name %d from %s node\n",
+ i, dev->of_node->name);
+ return ret;
+ }
+ }
+ clk_ids[clk_count] = NULL;
+
+ phy_drd->clocks = (struct clk **) devm_kmalloc(dev,
+ (clk_count + 1) * sizeof(struct clk *), GFP_KERNEL);
+ if (!phy_drd->clocks) {
+ dev_err(dev, "failed to alloc for clocks\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; clk_ids[i] != NULL; i++) {
+ if (phyclk_count) {
+ for (j = 0; phyclk_ids[j] != NULL; j++) {
+ if (!strcmp(phyclk_ids[j], clk_ids[i])) {
+ is_phyclk = true;
+ phyclk_count--;
+ }
+ }
+ }
+ if (!is_phyclk) {
+ clk = devm_clk_get(dev, clk_ids[i]);
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "couldn't get %s clock\n", clk_ids[i]);
+ return -EINVAL;
+ }
+ phy_drd->clocks[clk_index] = clk;
+ clk_index++;
+ }
+ is_phyclk = false;
+ }
+ phy_drd->clocks[clk_index] = NULL;
+
+ ret = of_property_read_string_index(dev->of_node,
+ "phy_refclk", 0, &refclk_name);
+ if (ret) {
+ dev_err(dev, "failed to read ref_clocks name from %s node\n",
+ dev->of_node->name);
+ return ret;
+ }
+
+ if (!strcmp("none", refclk_name)) {
+ dev_err(dev, "phy reference clock shouldn't be omitted");
+ return -EINVAL;
+ }
+
+ for (i = 0; clk_ids[i] != NULL; i++) {
+ if (!strcmp(clk_ids[i], refclk_name)) {
+ phy_drd->ref_clk = devm_clk_get(dev, refclk_name);
+ break;
+ }
+ }
+
+ if (IS_ERR_OR_NULL(phy_drd->ref_clk)) {
+ dev_err(dev, "%s couldn't get ref_clk", __func__);
+ return -EINVAL;
+ }
+
+ devm_kfree(dev, phyclk_ids);
+ devm_kfree(dev, clk_ids);
+
+ return 0;
+
+}
+
+static int exynos_usbdrd_clk_get(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ int ret;
+
+ ret = exynos_usbdrd_phyclk_get(phy_drd);
+ if (ret < 0) {
+ dev_err(dev, "failed to get clock for DRD USBPHY");
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline
+struct exynos_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst)
+{
+ return container_of((inst), struct exynos_usbdrd_phy,
+ phys[(inst)->index]);
+}
+
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+void exynos_usbdrd_phy_get_otp_info(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct tune_bits *data;
+ u16 magic;
+ u8 type;
+ u8 index_count;
+ u8 i, j;
+
+ phy_drd->otp_index[0] = phy_drd->otp_index[1] = 0;
+
+ for (i = 0; i < OTP_SUPPORT_USBPHY_NUMBER; i++) {
+ magic = i ? OTP_MAGIC_USB2: OTP_MAGIC_USB3;
+
+ if (otp_tune_bits_parsed(magic, &type, &index_count, &data)) {
+ dev_err(phy_drd->dev, "%s failed to get usb%d otp\n",
+ __func__, i ? 2 : 3);
+ continue;
+ }
+ dev_info(phy_drd->dev, "usb[%d] otp index_count: %d\n",
+ i, index_count);
+
+ if (!index_count) {
+ phy_drd->otp_data[i] = NULL;
+ continue;
+ }
+
+ phy_drd->otp_data[i] = devm_kzalloc(phy_drd->dev,
+ sizeof(*data) * index_count, GFP_KERNEL);
+ if (!phy_drd->otp_data[i]) {
+ dev_err(phy_drd->dev, "%s failed to alloc for usb%d\n",
+ __func__, i ? 2 : 3);
+ continue;
+ }
+
+ phy_drd->otp_index[i] = index_count;
+ phy_drd->otp_type[i] = type ? 4 : 1;
+ dev_info(phy_drd->dev, "usb[%d] otp type: %d\n", i, type);
+
+ for (j = 0; j < index_count; j++) {
+ phy_drd->otp_data[i][j].index = data[j].index;
+ phy_drd->otp_data[i][j].value = data[j].value;
+ dev_dbg(phy_drd->dev,
+ "usb[%d][%d] otp_data index:%d, value:0x%08x\n",
+ i, j, phy_drd->otp_data[i][j].index,
+ phy_drd->otp_data[i][j].value);
+ }
+ }
+}
+#endif
+
+/*
+ * exynos_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static unsigned int exynos_rate_to_clk(struct exynos_usbdrd_phy *phy_drd)
+{
+ int ret;
+
+ ret = clk_prepare_enable(phy_drd->ref_clk);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s failed to enable ref_clk", __func__);
+ return 0;
+ }
+
+ /* EXYNOS_FSEL_MASK */
+ switch (clk_get_rate(phy_drd->ref_clk)) {
+ case 9600 * KHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_9MHZ6;
+ break;
+ case 10 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_10MHZ;
+ break;
+ case 12 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_12MHZ;
+ break;
+ case 19200 * KHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_19MHZ2;
+ break;
+ case 20 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_20MHZ;
+ break;
+ case 24 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_24MHZ;
+ break;
+ case 26 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_26MHZ;
+ break;
+ case 50 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_50MHZ;
+ break;
+ default:
+ phy_drd->extrefclk = 0;
+ clk_disable_unprepare(phy_drd->ref_clk);
+ return -EINVAL;
+ }
+
+ clk_disable_unprepare(phy_drd->ref_clk);
+
+ return 0;
+}
+
+static void exynos_usbdrd_pipe3_phy_isol(struct phy_usb_instance *inst,
+ unsigned int on, unsigned int mask)
+{
+ unsigned int val;
+
+ if (!inst->reg_pmu)
+ return;
+
+ val = on ? 0 : mask;
+
+ regmap_update_bits(inst->reg_pmu, inst->pmu_offset_dp,
+ mask, val);
+}
+
+static void exynos_usbdrd_utmi_phy_isol(struct phy_usb_instance *inst,
+ unsigned int on, unsigned int mask)
+{
+ unsigned int val;
+
+ if (!inst->reg_pmu)
+ return;
+
+ val = on ? 0 : mask;
+
+ regmap_update_bits(inst->reg_pmu, inst->pmu_offset,
+ mask, 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
+exynos_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst)
+{
+ static u32 reg;
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ /* PHYCLKRST setting isn't required in Combo PHY */
+ if (phy_drd->usbphy_info.version >= EXYNOS_USBPHY_VER_02_0_0)
+ return -EINVAL;
+
+ /* restore any previous reference clock settings */
+ reg = readl(phy_drd->reg_phy + EXYNOS_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 EXYNOS_FSEL_50MHZ:
+ reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
+ PHYCLKRST_SSC_REFCLKSEL(0x00));
+ break;
+ case EXYNOS_FSEL_24MHZ:
+ reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
+ PHYCLKRST_SSC_REFCLKSEL(0x88));
+ break;
+ case EXYNOS_FSEL_20MHZ:
+ reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
+ PHYCLKRST_SSC_REFCLKSEL(0x00));
+ break;
+ case EXYNOS_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
+exynos_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst)
+{
+ static u32 reg;
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ /* PHYCLKRST setting isn't required in Combo PHY */
+ if(phy_drd->usbphy_info.version >= EXYNOS_USBPHY_VER_02_0_0)
+ return EINVAL;
+
+ /* restore any previous reference clock settings */
+ reg = readl(phy_drd->reg_phy + EXYNOS_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;
+}
+
+/*
+ * Sets the default PHY tuning values for high-speed connection.
+ */
+static int exynos_usbdrd_fill_hstune(struct exynos_usbdrd_phy *phy_drd,
+ struct device_node *node)
+{
+ struct device *dev = phy_drd->dev;
+ struct exynos_usbphy_hs_tune *hs_tune = phy_drd->hs_value;
+ int ret;
+ u32 res[2];
+ u32 value;
+
+ ret = of_property_read_u32_array(node, "tx_vref", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_vref = res[0];
+ hs_tune[1].tx_vref = res[1];
+ } else {
+ dev_err(dev, "can't get tx_vref value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_pre_emp", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_pre_emp = res[0];
+ hs_tune[1].tx_pre_emp = res[1];
+ } else {
+ dev_err(dev, "can't get tx_pre_emp value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_pre_emp_puls", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_pre_emp_puls = res[0];
+ hs_tune[1].tx_pre_emp_puls = res[1];
+ } else {
+ dev_err(dev, "can't get tx_pre_emp_puls value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_res", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_res = res[0];
+ hs_tune[1].tx_res = res[1];
+ } else {
+ dev_err(dev, "can't get tx_res value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_rise", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_rise = res[0];
+ hs_tune[1].tx_rise = res[1];
+ } else {
+ dev_err(dev, "can't get tx_rise value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_hsxv", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_hsxv = res[0];
+ hs_tune[1].tx_hsxv = res[1];
+ } else {
+ dev_err(dev, "can't get tx_hsxv value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_fsls", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_fsls = res[0];
+ hs_tune[1].tx_fsls = res[1];
+ } else {
+ dev_err(dev, "can't get tx_fsls value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "rx_sqrx", res, 2);
+ if (ret == 0) {
+ hs_tune[0].rx_sqrx = res[0];
+ hs_tune[1].rx_sqrx = res[1];
+ } else {
+ dev_err(dev, "can't get tx_sqrx value, error = %d\n", ret);
+ return -EINVAL;
+}
+
+ ret = of_property_read_u32_array(node, "compdis", res, 2);
+ if (ret == 0) {
+ hs_tune[0].compdis = res[0];
+ hs_tune[1].compdis = res[1];
+ } else {
+ dev_err(dev, "can't get compdis value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "otg", res, 2);
+ if (ret == 0) {
+ hs_tune[0].otg = res[0];
+ hs_tune[1].otg = res[1];
+ } else {
+ dev_err(dev, "can't get otg_tune value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "enable_user_imp", res, 2);
+ if (ret == 0) {
+ if (res[0]) {
+ hs_tune[0].enable_user_imp = true;
+ hs_tune[1].enable_user_imp = true;
+ hs_tune[0].user_imp_value = res[1];
+ hs_tune[1].user_imp_value = res[1];
+ } else {
+ hs_tune[0].enable_user_imp = false;
+ hs_tune[1].enable_user_imp = false;
+ }
+ } else {
+ dev_err(dev, "can't get enable_user_imp value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, "is_phyclock", &value);
+ if (ret == 0) {
+ if ( value == 1) {
+ hs_tune[0].utmi_clk = USBPHY_UTMI_PHYCLOCK;
+ hs_tune[1].utmi_clk = USBPHY_UTMI_PHYCLOCK;
+ } else {
+ hs_tune[0].utmi_clk = USBPHY_UTMI_FREECLOCK;
+ hs_tune[1].utmi_clk = USBPHY_UTMI_FREECLOCK;
+ }
+ } else {
+ dev_err(dev, "can't get is_phyclock value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Sets the default PHY tuning values for super-speed connection.
+ */
+static int exynos_usbdrd_fill_sstune(struct exynos_usbdrd_phy *phy_drd,
+ struct device_node *node)
+{
+ struct device *dev = phy_drd->dev;
+ struct exynos_usbphy_ss_tune *ss_tune = phy_drd->ss_value;
+ u32 res[2];
+ int ret;
+
+ ret = of_property_read_u32_array(node, "tx_boost_level", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_boost_level = res[0];
+ ss_tune[1].tx_boost_level = res[1];
+ } else {
+ dev_err(dev, "can't get tx_boost_level value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_swing_level", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_swing_level = res[0];
+ ss_tune[1].tx_swing_level = res[1];
+ } else {
+ dev_err(dev, "can't get tx_swing_level value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_swing_full", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_swing_full = res[0];
+ ss_tune[1].tx_swing_full = res[1];
+ } else {
+ dev_err(dev, "can't get tx_swing_full value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_swing_low", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_swing_low = res[0];
+ ss_tune[1].tx_swing_low = res[1];
+ } else {
+ dev_err(dev, "can't get tx_swing_low value, error = %d\n", ret);
+ return -EINVAL;
+}
+
+ ret = of_property_read_u32_array(node, "tx_deemphasis_mode", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_deemphasis_mode = res[0];
+ ss_tune[1].tx_deemphasis_mode = res[1];
+ } else {
+ dev_err(dev, "can't get tx_deemphasis_mode value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_deemphasis_3p5db", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_deemphasis_3p5db = res[0];
+ ss_tune[1].tx_deemphasis_3p5db = res[1];
+ } else {
+ dev_err(dev, "can't get tx_deemphasis_3p5db value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_deemphasis_6db", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_deemphasis_6db = res[0];
+ ss_tune[1].tx_deemphasis_6db = res[1];
+ } else {
+ dev_err(dev, "can't get tx_deemphasis_6db value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "enable_ssc", res, 2);
+ if (ret == 0) {
+ ss_tune[0].enable_ssc = res[0];
+ ss_tune[1].enable_ssc = res[1];
+ } else {
+ dev_err(dev, "can't get enable_ssc value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "ssc_range", res, 2);
+ if (ret == 0) {
+ ss_tune[0].ssc_range = res[0];
+ ss_tune[1].ssc_range = res[1];
+ } else {
+ dev_err(dev, "can't get ssc_range value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "los_bias", res, 2);
+ if (ret == 0) {
+ ss_tune[0].los_bias = res[0];
+ ss_tune[1].los_bias = res[1];
+ } else {
+ dev_err(dev, "can't get los_bias value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "los_mask_val", res, 2);
+ if (ret == 0) {
+ ss_tune[0].los_mask_val = res[0];
+ ss_tune[1].los_mask_val = res[1];
+ } else {
+ dev_err(dev, "can't get los_mask_val value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "enable_fixed_rxeq_mode", res, 2);
+ if (ret == 0) {
+ ss_tune[0].enable_fixed_rxeq_mode = res[0];
+ ss_tune[1].enable_fixed_rxeq_mode = res[1];
+ } else {
+ dev_err(dev, "can't get enable_fixed_rxeq_mode value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "fix_rxeq_value", res, 2);
+ if (ret == 0) {
+ ss_tune[0].fix_rxeq_value = res[0];
+ ss_tune[1].fix_rxeq_value = res[1];
+ } else {
+ dev_err(dev, "can't get fix_rxeq_value value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "set_crport_level_en", res, 2);
+ if (ret == 0) {
+ ss_tune[0].set_crport_level_en = res[0];
+ ss_tune[1].set_crport_level_en = res[1];
+ } else {
+ dev_err(dev, "can't get set_crport_level_en value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "set_crport_mpll_charge_pump", res, 2);
+ if (ret == 0) {
+ ss_tune[0].set_crport_mpll_charge_pump = res[0];
+ ss_tune[1].set_crport_mpll_charge_pump = res[1];
+ } else {
+ dev_err(dev, "can't get set_crport_mpll_charge_pump value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int exynos_usbdrd_fill_hstune_param(struct exynos_usbdrd_phy *phy_drd,
+ struct device_node *node)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *child = NULL;
+ struct exynos_usb_tune_param *hs_tune_param;
+ size_t size = sizeof(struct exynos_usb_tune_param);
+ int ret;
+ u32 res[2];
+ u32 param_index = 0;
+ const char *name;
+
+ ret = of_property_read_u32_array(node, "hs_tune_cnt", &res[0], 1);
+
+ dev_info(dev, "%s hs tune cnt = %d\n", __func__, res[0]);
+
+ hs_tune_param = devm_kzalloc(dev, size*res[0], GFP_KERNEL);
+ if (!hs_tune_param)
+ return -ENOMEM;
+ phy_drd->usbphy_info.tune_param = hs_tune_param;
+
+ for_each_child_of_node(node, child) {
+ ret = of_property_read_string(child, "tune_name", &name);
+ if (ret == 0) {
+ memcpy(hs_tune_param[param_index].name, name, strlen(name));
+ } else {
+ dev_err(dev, "failed to read hs tune name from %s node\n", child->name);
+ return ret;
+ }
+
+ ret = of_property_read_u32_array(child, "tune_value", res, 2);
+ if (ret == 0) {
+ phy_drd->hs_tune_param_value[param_index][0] = res[0];
+ phy_drd->hs_tune_param_value[param_index][1] = res[1];
+ } else {
+ dev_err(dev, "failed to read hs tune value from %s node\n", child->name);
+ return -EINVAL;
+ }
+ param_index++;
+ }
+
+ hs_tune_param[param_index].value = EXYNOS_USB_TUNE_LAST;
+
+ return 0;
+}
+
+/*
+ * Sets the default PHY tuning values for super-speed connection.
+ */
+static int exynos_usbdrd_fill_sstune_param(struct exynos_usbdrd_phy *phy_drd,
+ struct device_node *node)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *child = NULL;
+ struct exynos_usb_tune_param *ss_tune_param;
+ size_t size = sizeof(struct exynos_usb_tune_param);
+ int ret;
+ u32 res[2];
+ u32 param_index = 0;
+ const char *name;
+
+ ret = of_property_read_u32_array(node, "ss_tune_cnt", &res[0], 1);
+
+ dev_info(dev, "%s ss tune cnt = %d\n", __func__, res[0]);
+
+ ss_tune_param = devm_kzalloc(dev, size*res[0], GFP_KERNEL);
+ if (!ss_tune_param)
+ return -ENOMEM;
+ phy_drd->usbphy_sub_info.tune_param = ss_tune_param;
+
+ for_each_child_of_node(node, child) {
+ ret = of_property_read_string(child, "tune_name", &name);
+ if (ret == 0) {
+ memcpy(ss_tune_param[param_index].name, name, strlen(name));
+ }
+ else {
+ dev_err(dev, "failed to read ss tune name from %s node\n", child->name);
+ return ret;
+ }
+
+ ret = of_property_read_u32_array(child, "tune_value", res, 2);
+ if (ret == 0) {
+ phy_drd->ss_tune_param_value[param_index][0] = res[0];
+ phy_drd->ss_tune_param_value[param_index][1] = res[1];
+ } else {
+ dev_err(dev, "failed to read ss tune value from %s node\n", child->name);
+ return -EINVAL;
+ }
+ param_index++;
+ }
+
+ ss_tune_param[param_index].value = EXYNOS_USB_TUNE_LAST;
+
+ return 0;
+}
+
+static int exynos_usbdrd_get_phy_refsel(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *node = dev->of_node;
+ int value, ret;
+ int check_flag = 0;
+
+ ret = of_property_read_u32(node, "phy_refsel_clockcore", &value);
+ if (ret == 0 && value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_CLKCORE;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_CLKCORE;
+ } else if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_clockcore, error = %d\n", ret);
+ return ret;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_ext_osc", &value);
+ if (ret == 0 && value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_EXT_OSC;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_EXT_OSC;
+ } else if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_ext_osc, error = %d\n", ret);
+ return ret;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_xtal", &value);
+ if (ret == 0 && value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_EXT_XTAL;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_EXT_XTAL;
+ } else if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_xtal, error = %d\n", ret);
+ return ret;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_diff_pad", &value);
+ if (ret == 0 && value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_PAD;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_PAD;
+ } else if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_diff_pad, error = %d\n", ret);
+ return ret;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_diff_internal", &value);
+ if (ret == 0 && value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_INTERNAL;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_INTERNAL;
+ } else if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_diff_internal, error = %d\n", ret);
+ return ret;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_diff_single", &value);
+ if (ret == 0 && value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_SINGLE;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_SINGLE;
+ } else if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_diff_single, error = %d\n", ret);
+ return ret;
+ } else {
+ check_flag++;
+ }
+
+ if (check_flag > 5) {
+ dev_err(dev, "USB refsel Must be choosed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int exynos_usbdrd_get_sub_phyinfo(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *tune_node;
+ int ret;
+ int value;
+
+ if (!of_property_read_u32(dev->of_node, "sub_phy_version", &value)) {
+ phy_drd->usbphy_sub_info.version = value;
+ } else {
+ dev_err(dev, "can't get sub_phy_version\n");
+ return -EINVAL;
+ }
+ phy_drd->usbphy_sub_info.refclk = phy_drd->extrefclk;
+ phy_drd->usbphy_sub_info.regs_base = phy_drd->reg_phy2;
+ /* Temporary WA, CAL code modification is needed */
+ phy_drd->usbphy_info.regs_base_2nd = phy_drd->reg_phy2;
+ phy_drd->usbphy_sub_info.regs_base_2nd = phy_drd->reg_phy3;
+ usbdp_combo_phy_reg = phy_drd->usbphy_sub_info.regs_base;
+
+ tune_node = of_parse_phandle(dev->of_node, "ss_tune_param", 0);
+ if (tune_node != NULL) {
+ ret = exynos_usbdrd_fill_sstune_param(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill super speed tuning param\n");
+ return -EINVAL;
+ }
+ } else
+ dev_info(dev, "don't need usbphy tuning param for high speed\n");
+
+ return 0;
+}
+
+static int exynos_usbdrd_get_phyinfo(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *tune_node;
+ int ret;
+ int value;
+
+ phy_drd->usbphy_info.hs_rewa = 1;
+
+ if (!of_property_read_u32(dev->of_node, "phy_version", &value)) {
+ phy_drd->usbphy_info.version = value;
+ } else {
+ dev_err(dev, "can't get phy_version\n");
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(dev->of_node, "use_io_for_ovc", &value)) {
+ phy_drd->usbphy_info.use_io_for_ovc = value ? true : false;
+ } else {
+ dev_err(dev, "can't get io_for_ovc\n");
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(dev->of_node, "common_block_disable", &value)) {
+ phy_drd->usbphy_info.common_block_disable = value ? true : false;
+ } else {
+ dev_err(dev, "can't get common_block_disable\n");
+ return -EINVAL;
+ }
+
+ phy_drd->usbphy_info.refclk = phy_drd->extrefclk;
+ phy_drd->usbphy_info.regs_base = phy_drd->reg_phy;
+
+ if (!of_property_read_u32(dev->of_node, "is_not_vbus_pad", &value)) {
+ phy_drd->usbphy_info.not_used_vbus_pad = value ? true : false;
+ } else {
+ dev_err(dev, "can't get vbus_pad\n");
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(dev->of_node, "used_phy_port", &value)) {
+ phy_drd->usbphy_info.used_phy_port = value ? true : false;
+ } else {
+ dev_err(dev, "can't get used_phy_port\n");
+ return -EINVAL;
+ }
+
+ ret = exynos_usbdrd_get_phy_refsel(phy_drd);
+ if (ret < 0)
+ dev_err(dev, "can't get phy refsel\n");
+
+ tune_node = of_parse_phandle(dev->of_node, "ss_tune_info", 0);
+ if (tune_node == NULL)
+ dev_info(dev, "don't need usbphy tuning value for super speed\n");
+
+ if (of_device_is_available(tune_node)) {
+ ret = exynos_usbdrd_fill_sstune(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill super speed tuning value\n");
+ return -EINVAL;
+ }
+ }
+
+ tune_node = of_parse_phandle(dev->of_node, "hs_tune_info", 0);
+ if (tune_node == NULL)
+ dev_info(dev, "don't need usbphy tuning value for high speed\n");
+
+ if (of_device_is_available(tune_node)) {
+ ret = exynos_usbdrd_fill_hstune(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill high speed tuning value\n");
+ return -EINVAL;
+ }
+ }
+
+ tune_node = of_parse_phandle(dev->of_node, "hs_tune_param", 0);
+ if (tune_node != NULL) {
+ ret = exynos_usbdrd_fill_hstune_param(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill high speed tuning param\n");
+ return -EINVAL;
+ }
+ } else
+ dev_info(dev, "don't need usbphy tuning param for high speed\n");
+
+ dev_info(phy_drd->dev, "usbphy info: version:0x%x, refclk:0x%x\n",
+ phy_drd->usbphy_info.version, phy_drd->usbphy_info.refclk);
+
+ return 0;
+}
+
+static int exynos_usbdrd_get_iptype(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ int ret, value;
+
+ ret = of_property_read_u32(dev->of_node, "ip_type", &value);
+ if (ret) {
+ dev_err(dev, "can't get ip type");
+ return ret;
+ }
+
+ switch (value) {
+ case TYPE_USB3DRD:
+ phy_drd->ip_type = TYPE_USB3DRD;
+ dev_info(dev, "It is TYPE USB3DRD");
+ break;
+ case TYPE_USB3HOST:
+ phy_drd->ip_type = TYPE_USB3HOST;
+ dev_info(dev, "It is TYPE USB3HOST");
+ break;
+ case TYPE_USB2DRD:
+ phy_drd->ip_type = TYPE_USB2DRD;
+ dev_info(dev, "It is TYPE USB2DRD");
+ break;
+ case TYPE_USB2HOST:
+ phy_drd->ip_type = TYPE_USB2HOST;
+ dev_info(dev, "It is TYPE USB2HOST");
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void exynos_usbdrd_pipe3_init(struct exynos_usbdrd_phy *phy_drd)
+{
+#if defined(USB_SS_ENABLED)
+ int value, ret;
+
+ if (gpio_is_valid(phy_drd->phy_port)) {
+ value = !gpio_get_value(phy_drd->phy_port);
+ phy_drd->usbphy_info.used_phy_port = phy_drd->usbphy_sub_info.used_phy_port = value;
+ dev_info(phy_drd->dev, "%s: phy port[%d]\n", __func__,
+ phy_drd->usbphy_info.used_phy_port);
+ } else {
+ dev_info(phy_drd->dev, "%s: phy port fail retry\n", __func__);
+ phy_drd->phy_port = of_get_named_gpio(phy_drd->dev->of_node,
+ "phy,gpio_phy_port", 0);
+ if (gpio_is_valid(phy_drd->phy_port)) {
+ dev_err(phy_drd->dev, "PHY CON Selection OK\n");
+
+ ret = gpio_request(phy_drd->phy_port, "PHY_CON");
+ if (ret)
+ dev_err(phy_drd->dev, "fail to request gpio %s:%d\n", "PHY_CON", ret);
+ else
+ gpio_direction_input(phy_drd->phy_port);
+
+ value = !gpio_get_value(phy_drd->phy_port);
+ phy_drd->usbphy_info.used_phy_port = phy_drd->usbphy_sub_info.used_phy_port = value;
+ dev_info(phy_drd->dev, "%s: phy port1[%d]\n", __func__,
+ phy_drd->usbphy_info.used_phy_port);
+ } else {
+ dev_err(phy_drd->dev, "non-DT: PHY CON Selection\n");
+ }
+ }
+
+ /* Fill USBDP Combo phy init */
+ phy_exynos_usb_v3p1_pma_ready(&phy_drd->usbphy_info);
+
+ phy_exynos_usbdp_enable(&phy_drd->usbphy_sub_info);
+
+ phy_exynos_usb_v3p1_pma_sw_rst_release(&phy_drd->usbphy_info);
+#endif
+}
+
+static void exynos_usbdrd_utmi_init(struct exynos_usbdrd_phy *phy_drd)
+{
+ int ret;
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+ struct tune_bits *otp_data;
+ u8 otp_type;
+ u8 otp_index;
+ u8 i;
+#endif
+ pr_info("%s: +++\n", __func__);
+
+ ret = exynos_usbdrd_clk_enable(phy_drd, false);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s: Failed to enable clk\n", __func__);
+ return;
+ }
+
+ phy_exynos_usb_v3p1_enable(&phy_drd->usbphy_info);
+
+ phy_exynos_usb_v3p1_pipe_ovrd(&phy_drd->usbphy_info);
+
+ if (phy_drd->use_phy_umux) {
+ /* USB User MUX enable */
+ ret = exynos_usbdrd_clk_enable(phy_drd, true);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s: Failed to enable clk\n", __func__);
+ return;
+ }
+ }
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+ if (phy_drd->ip_type < TYPE_USB2DRD) {
+ otp_type = phy_drd->otp_type[OTP_USB3PHY_INDEX];
+ otp_index = phy_drd->otp_index[OTP_USB3PHY_INDEX];
+ otp_data = phy_drd->otp_data[OTP_USB3PHY_INDEX];
+ } else {
+ otp_type = phy_drd->otp_type[OTP_USB2PHY_INDEX];
+ otp_index = phy_drd->otp_index[OTP_USB2PHY_INDEX];
+ otp_data = phy_drd->otp_data[OTP_USB2PHY_INDEX];
+ }
+
+ for (i = 0; i < otp_index; i++) {
+ samsung_exynos_cal_usb3phy_write_register(
+ &phy_drd->usbphy_info,
+ otp_data[i].index * otp_type,
+ otp_data[i].value);
+ }
+#endif
+
+ pr_info("%s: ---\n", __func__);
+}
+
+static int exynos_usbdrd_phy_init(struct phy *phy)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ /* UTMI or PIPE3 specific init */
+ inst->phy_cfg->phy_init(phy_drd);
+
+ return 0;
+}
+
+static void __exynos_usbdrd_phy_shutdown(struct exynos_usbdrd_phy *phy_drd)
+{
+ phy_exynos_usb_v3p1_disable(&phy_drd->usbphy_info);
+ phy_exynos_usbdp_disable(&phy_drd->usbphy_sub_info);
+}
+
+static void exynos_usbdrd_pipe3_exit(struct exynos_usbdrd_phy *phy_drd)
+{
+ /* pipe3 phy diable is exucuted in utmi_exit.
+ Later divide the exit of main and sub phy if necessary */
+ return;
+}
+
+static void exynos_usbdrd_utmi_exit(struct exynos_usbdrd_phy *phy_drd)
+{
+ if (phy_drd->use_phy_umux) {
+ /*USB User MUX disable */
+ exynos_usbdrd_clk_disable(phy_drd, true);
+ }
+ phy_exynos_usb_v3p1_disable(&phy_drd->usbphy_info);
+ phy_exynos_usbdp_disable(&phy_drd->usbphy_sub_info);
+
+ exynos_usbdrd_clk_disable(phy_drd, false);
+}
+
+static int exynos_usbdrd_phy_exit(struct phy *phy)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ /* UTMI or PIPE3 specific exit */
+ inst->phy_cfg->phy_exit(phy_drd);
+
+ return 0;
+}
+
+static void exynos_usbdrd_utmi_ilbk(struct exynos_usbdrd_phy *phy_drd)
+{
+ dev_info(phy_drd->dev, "%s\n", __func__);
+}
+
+static void exynos_usbdrd_pipe3_ilbk(struct exynos_usbdrd_phy *phy_drd)
+{
+ dev_info(phy_drd->dev, "%s\n", __func__);
+
+ phy_exynos_usbdp_ilbk(&phy_drd->usbphy_sub_info);
+}
+
+static int exynos_usbdrd_pipe3_vendor_set(struct exynos_usbdrd_phy *phy_drd,
+ int is_enable, int is_cancel)
+{
+ dev_info(phy_drd->dev, "%s \n",__func__);
+ return 0;
+}
+
+static int exynos_usbdrd_utmi_vendor_set(struct exynos_usbdrd_phy *phy_drd,
+ int is_enable, int is_cancel)
+{
+ int ret = 0;
+
+ dev_info(phy_drd->dev, "rewa irq : %d, enable: %d, cancel: %d\n",
+ phy_drd->is_irq_enabled, is_enable, is_cancel);
+ if (is_cancel) {
+ if (is_enable) {
+ if (phy_drd->is_irq_enabled == 1) {
+ dev_info(phy_drd->dev, "[%s] REWA CANCEL\n", __func__);
+ phy_exynos_usb3p1_rewa_cancel(&phy_drd->usbphy_info);
+
+ dev_info(phy_drd->dev, "REWA wakeup/conn IRQ disable\n");
+
+ disable_irq_nosync(phy_drd->irq_wakeup);
+ disable_irq_nosync(phy_drd->irq_conn);
+ phy_drd->is_irq_enabled = 0;
+ } else {
+ dev_info(phy_drd->dev, "Vendor set by interrupt, Do not REWA cancel\n");
+ }
+ }
+ } else {
+ if (is_enable) {
+ ret = phy_exynos_usb3p1_rewa_enable(&phy_drd->usbphy_info);
+ if (ret) {
+ dev_err(phy_drd->dev, "REWA ENABLE FAIL, ret : %d \n", ret);
+ return ret;
+ }
+ dev_info(phy_drd->dev, "REWA ENABLE Complete\n");
+
+ if (phy_drd->is_irq_enabled == 0) {
+ enable_irq(phy_drd->irq_wakeup);
+ enable_irq(phy_drd->irq_conn);
+ phy_drd->is_irq_enabled = 1;
+ } else {
+ dev_info(phy_drd->dev, "rewa irq already enabled\n");
+ }
+ } else {
+ dev_info(phy_drd->dev, "REWA Disconn & Wakeup IRQ DISABLE\n");
+ ret = phy_exynos_usb3p1_rewa_disable(&phy_drd->usbphy_info);
+ if (ret) {
+ dev_err(phy_drd->dev, "REWA DISABLE FAIL, ret : %d \n", ret);
+ return ret;
+ }
+ dev_info(phy_drd->dev, "REWA DISABLE Complete\n");
+ }
+ }
+ return ret;
+}
+
+static void exynos_usbdrd_pipe3_tune(struct exynos_usbdrd_phy *phy_drd,
+ int phy_state)
+{
+ struct exynos_usb_tune_param *ss_tune_param = phy_drd->usbphy_sub_info.tune_param;
+ int i;
+
+ dev_info(phy_drd->dev, "%s\n", __func__);
+
+ if (phy_state >= OTG_STATE_A_IDLE) {
+ /* for host mode */
+ for (i = 0; ss_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++) {
+ if (i == EXYNOS_DRD_MAX_TUNEPARAM_NUM)
+ break;
+ ss_tune_param[i].value = phy_drd->ss_tune_param_value[i][USBPHY_MODE_HOST];
+ }
+ } else {
+ /* for device mode */
+ for (i = 0; ss_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++) {
+ if (i == EXYNOS_DRD_MAX_TUNEPARAM_NUM)
+ break;
+ ss_tune_param[i].value = phy_drd->ss_tune_param_value[i][USBPHY_MODE_DEV];
+ }
+ }
+ phy_exynos_usbdp_tune(&phy_drd->usbphy_sub_info);
+}
+
+static void exynos_usbdrd_utmi_tune(struct exynos_usbdrd_phy *phy_drd,
+ int phy_state)
+{
+ struct exynos_usb_tune_param *hs_tune_param = phy_drd->usbphy_info.tune_param;
+ int i;
+
+ dev_info(phy_drd->dev, "%s\n", __func__);
+
+ if (phy_state >= OTG_STATE_A_IDLE) {
+ /* for host mode */
+ for (i = 0; hs_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++) {
+ if (i == EXYNOS_DRD_MAX_TUNEPARAM_NUM)
+ break;
+ hs_tune_param[i].value = phy_drd->hs_tune_param_value[i][USBPHY_MODE_HOST];
+ }
+ } else {
+ /* for device mode */
+ for (i = 0; hs_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++) {
+ if (i == EXYNOS_DRD_MAX_TUNEPARAM_NUM)
+ break;
+ hs_tune_param[i].value = phy_drd->hs_tune_param_value[i][USBPHY_MODE_DEV];
+ }
+ }
+ phy_exynos_usb_v3p1_tune(&phy_drd->usbphy_info);
+}
+
+static int exynos_usbdrd_phy_tune(struct phy *phy, int phy_state)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ inst->phy_cfg->phy_tune(phy_drd, phy_state);
+
+ return 0;
+}
+
+static void exynos_usbdrd_phy_conn(struct phy *phy, int is_conn)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ if (is_conn) {
+ dev_info(phy_drd->dev, "USB PHY Conn Set\n");
+ phy_drd->is_conn = 1;
+ } else {
+ dev_info(phy_drd->dev, "USB PHY Conn Clear\n");
+ phy_drd->is_conn = 0;
+ }
+
+ return;
+}
+
+static int exynos_usbdrd_dp_ilbk(struct phy *phy)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ inst->phy_cfg->phy_ilbk(phy_drd);
+
+ return 0;
+}
+
+static int exynos_usbdrd_phy_vendor_set(struct phy *phy, int is_enable,
+ int is_cancel)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+ int ret;
+
+ ret = inst->phy_cfg->phy_vendor_set(phy_drd, is_enable, is_cancel);
+
+ return ret;
+}
+
+static void exynos_usbdrd_pipe3_set(struct exynos_usbdrd_phy *phy_drd,
+ int option, void *info)
+{
+ /* Fill USBDP Combo phy set */
+ return;
+}
+
+static void exynos_usbdrd_utmi_set(struct exynos_usbdrd_phy *phy_drd,
+ int option, void *info)
+{
+ switch (option) {
+ case SET_DPPULLUP_ENABLE:
+ phy_exynos_usb_v3p1_enable_dp_pullup(
+ &phy_drd->usbphy_info);
+ break;
+ case SET_DPPULLUP_DISABLE:
+ phy_exynos_usb_v3p1_disable_dp_pullup(
+ &phy_drd->usbphy_info);
+ break;
+ case SET_DPDM_PULLDOWN:
+ phy_exynos_usb_v3p1_config_host_mode(
+ &phy_drd->usbphy_info);
+ default:
+ break;
+ }
+}
+
+static int exynos_usbdrd_phy_set(struct phy *phy, int option, void *info)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ inst->phy_cfg->phy_set(phy_drd, option, info);
+
+ return 0;
+}
+
+static int exynos_usbdrd_phy_power_on(struct phy *phy)
+{
+ int ret;
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n");
+
+ /* Enable VBUS supply */
+ if (phy_drd->vbus) {
+ ret = regulator_enable(phy_drd->vbus);
+ if (ret) {
+ dev_err(phy_drd->dev, "Failed to enable VBUS supply\n");
+ return ret;
+ }
+ }
+
+ inst->phy_cfg->phy_isol(inst, 0, inst->pmu_mask);
+
+ phy_isol_delayed = 0;
+ dp_use_informed = 0;
+
+ return 0;
+}
+
+static int exynos_usbdrd_phy_power_off(struct phy *phy)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n");
+
+ if (!dp_use_informed)
+ inst->phy_cfg->phy_isol(inst, 1, inst->pmu_mask);
+ else
+ phy_isol_delayed = 1;
+
+ /* Disable VBUS supply */
+ if (phy_drd->vbus)
+ regulator_disable(phy_drd->vbus);
+
+ return 0;
+}
+
+void exynos_usbdrd_request_phy_isol(void)
+{
+ pr_info("[%s] phy_isol_delayed = %d\n", __func__, phy_isol_delayed);
+
+ if (!reg_pmu_delayed || !pmu_offset_dp_delayed)
+ return;
+
+ if (phy_isol_delayed == 1) {
+ regmap_update_bits(reg_pmu_delayed, pmu_offset_delayed, 1, 0);
+ regmap_update_bits(reg_pmu_delayed,
+ pmu_offset_dp_delayed, 1, 0);
+ phy_isol_delayed = 0;
+ dp_use_informed = 0;
+ }
+}
+
+int exynos_usbdrd_inform_dp_use(int use, int lane_cnt)
+{
+ int ret = 0;
+
+ pr_info("[%s] dp use = %d, lane_cnt = %d\n", __func__, use, lane_cnt);
+
+ dp_use_informed = use;
+
+ if ((use == 1) && (lane_cnt == 4)) {
+ ret = xhci_portsc_set(0);
+ udelay(1);
+ }
+
+ return ret;
+}
+
+static struct phy *exynos_usbdrd_phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev);
+
+ if (WARN_ON(args->args[0] > EXYNOS_DRDPHYS_NUM))
+ return ERR_PTR(-ENODEV);
+
+ return phy_drd->phys[args->args[0]].phy;
+}
+
+#if defined(USB_L2_ENABLED)
+static irqreturn_t exynos_usbdrd_phy_wakeup_interrupt(int irq, void *_phydrd)
+{
+ struct exynos_usbdrd_phy *phy_drd = (struct exynos_usbdrd_phy *)_phydrd;
+ int ret;
+
+ ret = phy_exynos_usb3p1_rewa_req_sys_valid(&phy_drd->usbphy_info);
+ dev_info(phy_drd->dev, "[%s] rewa sys vaild set : %s \n",
+ __func__, (ret == 1) ? "Disable" : "Disconnect");
+
+ if (phy_drd->is_irq_enabled == 1) {
+ disable_irq_nosync(phy_drd->irq_wakeup);
+ disable_irq_nosync(phy_drd->irq_conn);
+ phy_drd->is_irq_enabled = 0;
+ } else {
+ dev_info(phy_drd->dev, "rewa irq already disabled\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t exynos_usbdrd_phy_conn_interrupt(int irq, void *_phydrd)
+{
+ struct exynos_usbdrd_phy *phy_drd = (struct exynos_usbdrd_phy *)_phydrd;
+ int ret;
+
+ ret = phy_exynos_usb3p1_rewa_req_sys_valid(&phy_drd->usbphy_info);
+ dev_info(phy_drd->dev, "[%s] rewa sys vaild set : %s \n",
+ __func__, (ret == 1) ? "Disable" : "Disconnect");
+
+ if (phy_drd->is_irq_enabled == 1) {
+ disable_irq_nosync(phy_drd->irq_wakeup);
+ disable_irq_nosync(phy_drd->irq_conn);
+ phy_drd->is_irq_enabled = 0;
+ } else {
+ dev_info(phy_drd->dev, "rewa irq already disabled\n");
+ }
+
+ return IRQ_HANDLED;
+}
+#endif
+
+static struct phy_ops exynos_usbdrd_phy_ops = {
+ .init = exynos_usbdrd_phy_init,
+ .exit = exynos_usbdrd_phy_exit,
+ .tune = exynos_usbdrd_phy_tune,
+ .set = exynos_usbdrd_phy_set,
+ .vendor_set = exynos_usbdrd_phy_vendor_set,
+ .conn = exynos_usbdrd_phy_conn,
+ .ilbk = exynos_usbdrd_dp_ilbk,
+ .power_on = exynos_usbdrd_phy_power_on,
+ .power_off = exynos_usbdrd_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static const struct exynos_usbdrd_phy_config phy_cfg_exynos[] = {
+ {
+ .id = EXYNOS_DRDPHY_UTMI,
+ .phy_isol = exynos_usbdrd_utmi_phy_isol,
+ .phy_init = exynos_usbdrd_utmi_init,
+ .phy_exit = exynos_usbdrd_utmi_exit,
+ .phy_tune = exynos_usbdrd_utmi_tune,
+ .phy_vendor_set = exynos_usbdrd_utmi_vendor_set,
+ .phy_ilbk = exynos_usbdrd_utmi_ilbk,
+ .phy_set = exynos_usbdrd_utmi_set,
+ .set_refclk = exynos_usbdrd_utmi_set_refclk,
+ },
+ {
+ .id = EXYNOS_DRDPHY_PIPE3,
+ .phy_isol = exynos_usbdrd_pipe3_phy_isol,
+ .phy_init = exynos_usbdrd_pipe3_init,
+ .phy_exit = exynos_usbdrd_pipe3_exit,
+ .phy_tune = exynos_usbdrd_pipe3_tune,
+ .phy_vendor_set = exynos_usbdrd_pipe3_vendor_set,
+ .phy_ilbk = exynos_usbdrd_pipe3_ilbk,
+ .phy_set = exynos_usbdrd_pipe3_set,
+ .set_refclk = exynos_usbdrd_pipe3_set_refclk,
+ },
+};
+
+static const struct exynos_usbdrd_phy_drvdata exynos_usbdrd_phy = {
+ .phy_cfg = phy_cfg_exynos,
+};
+
+static const struct of_device_id exynos_usbdrd_phy_of_match[] = {
+ {
+ .compatible = "samsung,exynos-usbdrd-phy",
+ .data = &exynos_usbdrd_phy
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match);
+
+void __iomem *phy_exynos_usbdp_get_address(void)
+{
+ return usbdp_combo_phy_reg;
+}
+
+static int exynos_usbdrd_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_usbdrd_phy *phy_drd;
+ struct phy_provider *phy_provider;
+ struct resource *res;
+ const struct of_device_id *match;
+ const struct exynos_usbdrd_phy_drvdata *drv_data;
+ struct regmap *reg_pmu;
+ u32 pmu_offset, pmu_offset_dp, pmu_mask;
+ int i, ret;
+
+ pr_info("%s: +++ %s %s\n", __func__, dev->init_name, pdev->name);
+ 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;
+
+ match = of_match_node(exynos_usbdrd_phy_of_match, pdev->dev.of_node);
+
+ drv_data = match->data;
+ phy_drd->drv_data = drv_data;
+
+#if defined(USB_L2_ENABLED)
+ phy_drd->irq_wakeup = platform_get_irq(pdev, 0);
+ irq_set_status_flags(phy_drd->irq_wakeup, IRQ_NOAUTOEN);
+ ret = devm_request_irq(dev, phy_drd->irq_wakeup, exynos_usbdrd_phy_wakeup_interrupt,
+ IRQF_SHARED, "phydrd-wakeup", phy_drd);
+ if (ret) {
+ dev_err(dev, "failed to request irq #%d --> %d\n",
+ phy_drd->irq_wakeup, ret);
+ return ret;
+ }
+ phy_drd->irq_conn = platform_get_irq(pdev, 1);
+ irq_set_status_flags(phy_drd->irq_conn, IRQ_NOAUTOEN);
+ ret = devm_request_irq(dev, phy_drd->irq_conn, exynos_usbdrd_phy_conn_interrupt,
+ IRQF_SHARED, "phydrd-conn", phy_drd);
+ if (ret) {
+ dev_err(dev, "failed to request irq #%d --> %d\n",
+ phy_drd->irq_conn, ret);
+ return ret;
+ }
+#endif
+
+ 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);
+
+ /* Both has_other_phy and has_combo_phy can't be enabled at the same time. It's alternative. */
+ if (!of_property_read_u32(dev->of_node, "has_other_phy", &ret)) {
+ if (ret) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ phy_drd->reg_phy2 = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy_drd->reg_phy2))
+ return PTR_ERR(phy_drd->reg_phy2);
+ } else {
+ dev_err(dev, "It has not the other phy\n");
+ }
+ }
+
+ ret = exynos_usbdrd_get_iptype(phy_drd);
+ if (ret) {
+ dev_err(dev, "%s: Failed to get ip_type\n", __func__);
+ return ret;
+ }
+
+ ret = exynos_usbdrd_clk_get(phy_drd);
+ if (ret) {
+ dev_err(dev, "%s: Failed to get clocks\n", __func__);
+ return ret;
+ }
+
+ ret = exynos_usbdrd_clk_prepare(phy_drd);
+ if (ret) {
+ dev_err(dev, "%s: Failed to prepare clocks\n", __func__);
+ return ret;
+ }
+
+ ret = exynos_rate_to_clk(phy_drd);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s: Not supported ref clock\n",
+ __func__);
+ goto err1;
+ }
+
+ 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");
+ goto err1;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "pmu_offset", &pmu_offset);
+ if (ret < 0) {
+ dev_err(dev, "couldn't read pmu_offset on %s node, error = %d\n",
+ dev->of_node->name, ret);
+ goto err1;
+ }
+ ret = of_property_read_u32(dev->of_node, "pmu_offset_dp", &pmu_offset_dp);
+ if (ret < 0) {
+ dev_err(dev, "couldn't read pmu_offset on %s node, error = %d\n",
+ dev->of_node->name, ret);
+ goto err1;
+ }
+ ret = of_property_read_u32(dev->of_node, "pmu_mask", &pmu_mask);
+ if (ret < 0) {
+ dev_err(dev, "couldn't read pmu_mask on %s node, error = %d\n",
+ dev->of_node->name, ret);
+ goto err1;
+ }
+ pmu_mask = (u32)BIT(pmu_mask);
+
+ dev_vdbg(dev, "Creating usbdrd_phy phy\n");
+ phy_drd->phy_port = of_get_named_gpio(dev->of_node,
+ "phy,gpio_phy_port", 0);
+ if (gpio_is_valid(phy_drd->phy_port)) {
+ dev_err(dev, "PHY CON Selection OK\n");
+
+ ret = gpio_request(phy_drd->phy_port, "PHY_CON");
+ if (ret)
+ dev_err(dev, "fail to request gpio %s:%d\n", "PHY_CON", ret);
+ else
+ gpio_direction_input(phy_drd->phy_port);
+ }
+ else
+ dev_err(dev, "non-DT: PHY CON Selection\n");
+
+ ret = exynos_usbdrd_get_phyinfo(phy_drd);
+ if (ret)
+ goto err1;
+
+ if (!of_property_read_u32(dev->of_node, "has_combo_phy", &ret)) {
+ if (ret) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ phy_drd->reg_phy2 = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy_drd->reg_phy2))
+ return PTR_ERR(phy_drd->reg_phy2);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ phy_drd->reg_phy3 = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy_drd->reg_phy3))
+ return PTR_ERR(phy_drd->reg_phy3);
+
+ exynos_usbdrd_get_sub_phyinfo(phy_drd);
+ } else {
+ dev_err(dev, "It has not combo phy\n");
+ }
+ }
+
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+ exynos_usbdrd_phy_get_otp_info(phy_drd);
+#endif
+
+ for (i = 0; i < EXYNOS_DRDPHYS_NUM; i++) {
+ struct phy *phy = devm_phy_create(dev, NULL,
+ &exynos_usbdrd_phy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "Failed to create usbdrd_phy phy\n");
+ goto err1;
+ }
+
+ phy_drd->phys[i].phy = phy;
+ phy_drd->phys[i].index = i;
+ phy_drd->phys[i].reg_pmu = reg_pmu_delayed = reg_pmu;
+ phy_drd->phys[i].pmu_offset = pmu_offset_delayed = pmu_offset;
+ phy_drd->phys[i].pmu_offset_dp =
+ pmu_offset_dp_delayed = pmu_offset_dp;
+ phy_drd->phys[i].pmu_mask = pmu_mask;
+ phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i];
+ phy_set_drvdata(phy, &phy_drd->phys[i]);
+ }
+#if IS_ENABLED(CONFIG_PHY_EXYNOS_DEBUGFS)
+ ret = exynos_usbdrd_debugfs_init(phy_drd);
+ if (ret) {
+ dev_err(dev, "Failed to initialize debugfs\n");
+ goto err1;
+ }
+#endif
+
+#if IS_ENABLED(CONFIG_PHY_EXYNOS_DP_DEBUGFS)
+ ret = exynos_usbdrd_dp_debugfs_init(phy_drd);
+ if (ret) {
+ dev_err(dev, "Failed to initialize dp debugfs\n");
+ goto err1;
+ }
+#endif
+
+ phy_provider = devm_of_phy_provider_register(dev,
+ exynos_usbdrd_phy_xlate);
+ if (IS_ERR(phy_provider)) {
+ dev_err(phy_drd->dev, "Failed to register phy provider\n");
+ goto err1;
+ }
+
+ phy_drd->is_irq_enabled = 0;
+
+ pr_info("%s: ---\n", __func__);
+ return 0;
+err1:
+ exynos_usbdrd_clk_unprepare(phy_drd);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int exynos_usbdrd_phy_resume(struct device *dev)
+{
+ int ret;
+ struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev);
+
+ /*
+ * There is issue, when USB3.0 PHY is in active state
+ * after resume. This leads to increased power consumption
+ * if no USB drivers use the PHY.
+ *
+ * The following code shutdowns the PHY, so it is in defined
+ * state (OFF) after resume. If any USB driver already got
+ * the PHY at this time, we do nothing and just exit.
+ */
+
+ dev_info(dev, "%s\n", __func__);
+
+ if (!phy_drd->is_conn) {
+ dev_info(dev, "USB wasn't connected\n");
+ ret = exynos_usbdrd_clk_enable(phy_drd, false);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s: Failed to enable clk\n", __func__);
+ return ret;
+ }
+
+ __exynos_usbdrd_phy_shutdown(phy_drd);
+
+ exynos_usbdrd_clk_disable(phy_drd, false);
+ } else {
+ dev_info(dev, "USB was connected\n");
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops exynos_usbdrd_phy_dev_pm_ops = {
+ .resume = exynos_usbdrd_phy_resume,
+};
+
+#define EXYNOS_USBDRD_PHY_PM_OPS &(exynos_usbdrd_phy_dev_pm_ops)
+#else
+#define EXYNOS_USBDRD_PHY_PM_OPS NULL
+#endif
+
+static struct platform_driver phy_exynos_usbdrd = {
+ .probe = exynos_usbdrd_phy_probe,
+ .driver = {
+ .of_match_table = exynos_usbdrd_phy_of_match,
+ .name = "phy_exynos_usbdrd",
+ .pm = EXYNOS_USBDRD_PHY_PM_OPS,
+ }
+};
+
+module_platform_driver(phy_exynos_usbdrd);
+MODULE_DESCRIPTION("Samsung EXYNOS SoCs USB DRD controller PHY driver");
+MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:phy_exynos_usbdrd");
--- /dev/null
+/*
+ * Copyright (C) 2015 Samsung 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PHY_EXYNOS_USBDRD_H__
+#define __PHY_EXYNOS_USBDRD_H__
+
+#include "phy-samsung-usb-cal.h"
+#include "phy-exynos-usb3p1.h"
+#include "phy-exynos-usbdp.h"
+
+#define EXYNOS_USBPHY_VER_02_0_0 0x0200 /* Lhotse - USBDP Combo PHY */
+
+/* 9810 PMU register offset */
+#define EXYNOS_USBDP_PHY_CONTROL (0x704)
+#define EXYNOS_USB2_PHY_CONTROL (0x72C)
+/* PMU register offset for USB */
+#define EXYNOS_USBDEV_PHY_CONTROL (0x704)
+#define EXYNOS_USBDRD_ENABLE BIT(0)
+#define EXYNOS_USBHOST_ENABLE BIT(1)
+/* enables TCXO_USB. 1:enable TCXO */
+#define ENABLE_TCXO_BUF_MASK (0x10000)
+
+/* Exynos USB PHY registers */
+#define EXYNOS_FSEL_9MHZ6 0x0
+#define EXYNOS_FSEL_10MHZ 0x1
+#define EXYNOS_FSEL_12MHZ 0x2
+#define EXYNOS_FSEL_19MHZ2 0x3
+#define EXYNOS_FSEL_20MHZ 0x4
+#define EXYNOS_FSEL_24MHZ 0x5
+#define EXYNOS_FSEL_26MHZ 0x82
+#define EXYNOS_FSEL_50MHZ 0x7
+
+/* EXYNOS: USB DRD PHY registers */
+#define EXYNOS_DRD_LINKSYSTEM 0x04
+
+#define LINKSYSTEM_FLADJ_MASK (0x3f << 1)
+#define LINKSYSTEM_FLADJ(_x) ((_x) << 1)
+
+#define EXYNOS_DRD_PHYUTMI 0x08
+
+#define EXYNOS_DRD_PHYPIPE 0x0c
+
+#define PHYPIPE_PHY_CLOCK_SEL (0x1 << 4)
+
+#define EXYNOS_DRD_PHYCLKRST 0x10
+
+#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_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_REFCLKSEL_MASK (0x03 << 2)
+#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2)
+#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2)
+
+#define EXYNOS_DRD_PHYREG0 0x14
+#define EXYNOS_DRD_PHYREG1 0x18
+
+#define EXYNOS_DRD_PHYPARAM0 0x1c
+
+#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26)
+#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26)
+
+#define EXYNOS_DRD_PHYPARAM1 0x20
+
+#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0)
+#define PHYPARAM1_PCS_TXDEEMPH (0x1c)
+
+#define EXYNOS_DRD_PHYTERM 0x24
+
+#define EXYNOS_DRD_PHYTEST 0x28
+
+#define EXYNOS_DRD_PHYADP 0x2c
+
+#define EXYNOS_DRD_PHYUTMICLKSEL 0x30
+
+#define PHYUTMICLKSEL_UTMI_CLKSEL BIT(2)
+
+#define EXYNOS_DRD_PHYRESUME 0x34
+#define EXYNOS_DRD_LINKPORT 0x44
+
+#define KHZ 1000
+#define MHZ (KHZ * KHZ)
+
+#define EXYNOS_DRD_MAX_TUNEPARAM_NUM 32
+
+enum exynos_usbdrd_phy_id {
+ EXYNOS_DRDPHY_UTMI,
+ EXYNOS_DRDPHY_PIPE3,
+ EXYNOS_DRDPHYS_NUM,
+};
+
+struct phy_usb_instance;
+struct exynos_usbdrd_phy;
+
+struct exynos_usbdrd_phy_config {
+ u32 id;
+ void (*phy_isol)(struct phy_usb_instance *inst, u32 on, unsigned int);
+ void (*phy_init)(struct exynos_usbdrd_phy *phy_drd);
+ void (*phy_exit)(struct exynos_usbdrd_phy *phy_drd);
+ void (*phy_tune)(struct exynos_usbdrd_phy *phy_drd, int);
+ int (*phy_vendor_set)(struct exynos_usbdrd_phy *phy_drd, int, int);
+ void (*phy_ilbk)(struct exynos_usbdrd_phy *phy_drd);
+ void (*phy_set)(struct exynos_usbdrd_phy *phy_drd, int, void *);
+ unsigned int (*set_refclk)(struct phy_usb_instance *inst);
+};
+
+struct exynos_usbdrd_phy_drvdata {
+ const struct exynos_usbdrd_phy_config *phy_cfg;
+};
+
+/**
+ * struct exynos_usbdrd_phy - driver data for USB DRD PHY
+ * @dev: pointer to device instance of this platform device
+ * @reg_phy: usb phy controller register memory base
+ * @clk: phy clock for register access
+ * @drv_data: pointer to SoC level driver data structure
+ * @phys[]: array for 'EXYNOS_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
+ * @usbphy_info; Phy main control info
+ * @usbphy_sub_info; USB3.0 phy control info
+ */
+struct exynos_usbdrd_phy {
+ struct device *dev;
+ void __iomem *reg_phy;
+ void __iomem *reg_phy2;
+ void __iomem *reg_phy3;
+ struct clk **clocks;
+ struct clk **phy_clocks;
+ const struct exynos_usbdrd_phy_drvdata *drv_data;
+ struct phy_usb_instance {
+ struct phy *phy;
+ u32 index;
+ struct regmap *reg_pmu;
+ u32 pmu_offset;
+ u32 pmu_offset_dp;
+ u32 pmu_mask;
+ const struct exynos_usbdrd_phy_config *phy_cfg;
+ } phys[EXYNOS_DRDPHYS_NUM];
+ u32 extrefclk;
+ bool use_phy_umux;
+ struct clk *ref_clk;
+ struct regulator *vbus;
+ struct exynos_usbphy_info usbphy_info;
+ struct exynos_usbphy_info usbphy_sub_info;
+ struct exynos_usbphy_ss_tune ss_value[2];
+ struct exynos_usbphy_hs_tune hs_value[2];
+ int hs_tune_param_value[EXYNOS_DRD_MAX_TUNEPARAM_NUM][2];
+ int ss_tune_param_value[EXYNOS_DRD_MAX_TUNEPARAM_NUM][2];
+
+ u32 ip_type;
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+#define OTP_SUPPORT_USBPHY_NUMBER 2
+#define OTP_USB3PHY_INDEX 0
+#define OTP_USB2PHY_INDEX 1
+ u8 otp_type[OTP_SUPPORT_USBPHY_NUMBER];
+ u8 otp_index[OTP_SUPPORT_USBPHY_NUMBER];
+ struct tune_bits *otp_data[OTP_SUPPORT_USBPHY_NUMBER];
+#endif
+ int irq_wakeup;
+ int irq_conn;
+ int is_conn;
+ int is_irq_enabled;
+ u32 phy_port;
+};
+
+void __iomem *phy_exynos_usbdp_get_address(void);
+extern int xhci_portsc_set(int on);
+
+#endif /* __PHY_EXYNOS_USBDRD_H__ */
--- /dev/null
+/*
+ * Samsung EXYNOS SoC series USB DRD PHY driver
+ *
+ * Phy provider for USB 3.0 DRD controller on Exynos SoC series
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd.
+ * Author: Vivek Gautam <gautam.vivek@samsung.com>
+ * Minho Lee <minho55.lee@samsung.com>
+ *
+ * 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/exynos5-pmu.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/samsung_usb.h>
+#include <linux/usb/otg.h>
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+#include <linux/exynos_otp.h>
+#endif
+#ifdef CONFIG_OF
+#include <linux/of_gpio.h>
+#endif
+
+#include "phy-exynos-usbdrd.h"
+#include "phy-exynos-debug.h"
+/*
+extern int sm5713_get_usb_connect(void);
+*/
+
+static void exynos_usbdrd_check_connection(struct exynos_usbdrd_phy *phy_drd)
+{
+#if 0
+ int usb_side;
+
+ usb_side = sm5713_get_usb_connect();
+ dev_info(phy_drd->dev, "USB is plugged in %d side...\n", usb_side);
+
+ if (usb_side == 1) /* front */
+ phy_drd->usbphy_info.used_phy_port = 0;
+ else if (usb_side == 0)
+ phy_drd->usbphy_info.used_phy_port = 1;
+#endif
+}
+
+static int exynos_usbdrd_clk_prepare(struct exynos_usbdrd_phy *phy_drd)
+{
+ int i;
+ int ret;
+
+ for (i = 0; phy_drd->clocks[i] != NULL; i++) {
+ ret = clk_prepare(phy_drd->clocks[i]);
+ if (ret)
+ goto err;
+ }
+
+ if (phy_drd->use_phy_umux) {
+ for (i = 0; phy_drd->phy_clocks[i] != NULL; i++) {
+ ret = clk_prepare(phy_drd->phy_clocks[i]);
+ if (ret)
+ goto err1;
+ }
+ }
+ return 0;
+
+err1:
+ for (i = i - 1; i >= 0; i--)
+ clk_unprepare(phy_drd->phy_clocks[i]);
+err:
+ for (i = i - 1; i >= 0; i--)
+ clk_unprepare(phy_drd->clocks[i]);
+ return ret;
+}
+
+static int exynos_usbdrd_clk_enable(struct exynos_usbdrd_phy *phy_drd,
+ bool umux)
+{
+ int i;
+ int ret;
+
+#ifdef CONFIG_SOC_EXYNOS7885
+ clk_set_rate(phy_drd->ref_clk, 50 * 1000000);
+#endif
+
+ if (!phy_drd->use_phy_umux) {
+ for (i = 0; phy_drd->clocks[i] != NULL; i++) {
+ ret = clk_enable(phy_drd->clocks[i]);
+ if (ret)
+ goto err;
+ }
+ } else {
+ for (i = 0; phy_drd->phy_clocks[i] != NULL; i++) {
+ ret = clk_enable(phy_drd->phy_clocks[i]);
+ if (ret)
+ goto err1;
+ }
+ }
+ return 0;
+
+err1:
+ for (i = i - 1; i >= 0; i--)
+ clk_disable(phy_drd->phy_clocks[i]);
+ return ret;
+err:
+ for (i = i - 1; i >= 0; i--)
+ clk_disable(phy_drd->clocks[i]);
+ return ret;
+}
+
+static void exynos_usbdrd_clk_unprepare(struct exynos_usbdrd_phy *phy_drd)
+{
+ int i;
+
+ for (i = 0; phy_drd->clocks[i] != NULL; i++)
+ clk_unprepare(phy_drd->clocks[i]);
+ for (i = 0; phy_drd->phy_clocks[i] != NULL; i++)
+ clk_unprepare(phy_drd->phy_clocks[i]);
+}
+
+static void exynos_usbdrd_clk_disable(struct exynos_usbdrd_phy *phy_drd, bool umux)
+{
+ int i;
+
+ if (!umux) {
+ for (i = 0; phy_drd->clocks[i] != NULL; i++)
+ clk_disable(phy_drd->clocks[i]);
+ } else {
+ for (i = 0; phy_drd->phy_clocks[i] != NULL; i++)
+ clk_disable(phy_drd->phy_clocks[i]);
+ }
+}
+static int exynos_usbdrd_phyclk_get(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ const char **phyclk_ids;
+ const char **clk_ids;
+ const char *refclk_name;
+ struct clk *clk;
+ int phyclk_count;
+ int clk_count;
+ bool is_phyclk = false;
+ int clk_index = 0;
+ int i, j, ret;
+
+ phyclk_count = of_property_count_strings(dev->of_node, "phyclk_mux");
+ if (IS_ERR_VALUE((unsigned long)phyclk_count)) {
+ dev_err(dev, "invalid phyclk list in %s node\n",
+ dev->of_node->name);
+ return -EINVAL;
+ }
+
+ phyclk_ids = (const char **)devm_kmalloc(dev,
+ (phyclk_count+1) * sizeof(const char *),
+ GFP_KERNEL);
+ for (i = 0; i < phyclk_count; i++) {
+ ret = of_property_read_string_index(dev->of_node,
+ "phyclk_mux", i, &phyclk_ids[i]);
+ if (ret) {
+ dev_err(dev, "failed to read phyclk_mux name %d from %s node\n",
+ i, dev->of_node->name);
+ return ret;
+ }
+ }
+ phyclk_ids[phyclk_count] = NULL;
+
+ if (!strcmp("none", phyclk_ids[0])) {
+ dev_info(dev, "don't need user Mux for phyclk\n");
+ phy_drd->use_phy_umux = false;
+ phyclk_count = 0;
+
+ } else {
+ phy_drd->use_phy_umux = true;
+
+ phy_drd->phy_clocks = (struct clk **) devm_kmalloc(dev,
+ (phyclk_count+1) * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!phy_drd->phy_clocks) {
+ dev_err(dev, "failed to alloc : phy clocks\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; phyclk_ids[i] != NULL; i++) {
+ clk = devm_clk_get(dev, phyclk_ids[i]);
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "couldn't get %s clock\n", phyclk_ids[i]);
+ return -EINVAL;
+ }
+ phy_drd->phy_clocks[i] = clk;
+ }
+
+ phy_drd->phy_clocks[i] = NULL;
+ }
+
+ clk_count = of_property_count_strings(dev->of_node, "clock-names");
+ if (IS_ERR_VALUE((unsigned long)clk_count)) {
+ dev_err(dev, "invalid clk list in %s node", dev->of_node->name);
+ return -EINVAL;
+ }
+ clk_ids = (const char **)devm_kmalloc(dev,
+ (clk_count + 1) * sizeof(const char *),
+ GFP_KERNEL);
+ for (i = 0; i < clk_count; i++) {
+ ret = of_property_read_string_index(dev->of_node, "clock-names",
+ i, &clk_ids[i]);
+ if (ret) {
+ dev_err(dev, "failed to read clocks name %d from %s node\n",
+ i, dev->of_node->name);
+ return ret;
+ }
+ }
+ clk_ids[clk_count] = NULL;
+
+ phy_drd->clocks = (struct clk **) devm_kmalloc(dev,
+ (clk_count + 1) * sizeof(struct clk *), GFP_KERNEL);
+ if (!phy_drd->clocks) {
+ dev_err(dev, "failed to alloc for clocks\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; clk_ids[i] != NULL; i++) {
+ if (phyclk_count) {
+ for (j = 0; phyclk_ids[j] != NULL; j++) {
+ if (!strcmp(phyclk_ids[j], clk_ids[i])) {
+ is_phyclk = true;
+ phyclk_count--;
+ }
+ }
+ }
+ if (!is_phyclk) {
+ clk = devm_clk_get(dev, clk_ids[i]);
+ if (IS_ERR_OR_NULL(clk)) {
+ dev_err(dev, "couldn't get %s clock\n", clk_ids[i]);
+ return -EINVAL;
+ }
+ phy_drd->clocks[clk_index] = clk;
+ clk_index++;
+ }
+ is_phyclk = false;
+ }
+ phy_drd->clocks[clk_index] = NULL;
+
+ ret = of_property_read_string_index(dev->of_node,
+ "phy_refclk", 0, &refclk_name);
+ if (ret) {
+ dev_err(dev, "failed to read ref_clocks name from %s node\n",
+ dev->of_node->name);
+ return ret;
+ }
+
+ if (!strcmp("none", refclk_name)) {
+ dev_err(dev, "phy reference clock shouldn't be omitted");
+ return -EINVAL;
+ }
+
+ for (i = 0; clk_ids[i] != NULL; i++) {
+ if (!strcmp(clk_ids[i], refclk_name)) {
+ phy_drd->ref_clk = devm_clk_get(dev, refclk_name);
+ break;
+ }
+ }
+
+ if (IS_ERR_OR_NULL(phy_drd->ref_clk)) {
+ dev_err(dev, "%s couldn't get ref_clk", __func__);
+ return -EINVAL;
+ }
+
+ devm_kfree(dev, phyclk_ids);
+ devm_kfree(dev, clk_ids);
+
+ return 0;
+
+}
+
+static int exynos_usbdrd_clk_get(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ int ret;
+
+ ret = exynos_usbdrd_phyclk_get(phy_drd);
+ if (ret < 0) {
+ dev_err(dev, "failed to get clock for DRD USBPHY");
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline
+struct exynos_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst)
+{
+ return container_of((inst), struct exynos_usbdrd_phy,
+ phys[(inst)->index]);
+}
+
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+void exynos_usbdrd_phy_get_otp_info(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct tune_bits *data;
+ u16 magic;
+ u8 type;
+ u8 index_count;
+ u8 i, j;
+
+ phy_drd->otp_index[0] = phy_drd->otp_index[1] = 0;
+
+ for (i = 0; i < OTP_SUPPORT_USBPHY_NUMBER; i++) {
+ magic = i ? OTP_MAGIC_USB2 : OTP_MAGIC_USB3;
+
+ if (otp_tune_bits_parsed(magic, &type, &index_count, &data)) {
+ dev_err(phy_drd->dev, "%s failed to get usb%d otp\n",
+ __func__, i ? 2 : 3);
+ continue;
+ }
+ dev_info(phy_drd->dev, "usb[%d] otp index_count: %d\n",
+ i, index_count);
+
+ if (!index_count) {
+ phy_drd->otp_data[i] = NULL;
+ continue;
+ }
+
+ phy_drd->otp_data[i] = devm_kzalloc(phy_drd->dev,
+ sizeof(*data) * index_count, GFP_KERNEL);
+ if (!phy_drd->otp_data[i])
+ continue;
+
+ phy_drd->otp_index[i] = index_count;
+ phy_drd->otp_type[i] = type ? 4 : 1;
+ dev_info(phy_drd->dev, "usb[%d] otp type: %d\n", i, type);
+
+ for (j = 0; j < index_count; j++) {
+ phy_drd->otp_data[i][j].index = data[j].index;
+ phy_drd->otp_data[i][j].value = data[j].value;
+ dev_dbg(phy_drd->dev,
+ "usb[%d][%d] otp_data index:%d, value:0x%08x\n",
+ i, j, phy_drd->otp_data[i][j].index,
+ phy_drd->otp_data[i][j].value);
+ }
+ }
+}
+#endif
+
+/*
+ * exynos_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static unsigned int exynos_rate_to_clk(struct exynos_usbdrd_phy *phy_drd)
+{
+ int ret;
+
+#ifdef CONFIG_SOC_EXYNOS7885
+ clk_set_rate(phy_drd->ref_clk, 50 * 1000000);
+#endif
+
+ ret = clk_prepare_enable(phy_drd->ref_clk);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s failed to enable ref_clk", __func__);
+ return 0;
+ }
+
+ /* EXYNOS_FSEL_MASK */
+ switch (clk_get_rate(phy_drd->ref_clk)) {
+ case 9600 * KHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_9MHZ6;
+ break;
+ case 10 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_10MHZ;
+ break;
+ case 12 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_12MHZ;
+ break;
+ case 19200 * KHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_19MHZ2;
+ break;
+ case 20 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_20MHZ;
+ break;
+ case 24 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_24MHZ;
+ break;
+ case 26 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_26MHZ;
+ break;
+ case 50 * MHZ:
+ phy_drd->extrefclk = EXYNOS_FSEL_50MHZ;
+ break;
+ default:
+ phy_drd->extrefclk = 0;
+ clk_disable_unprepare(phy_drd->ref_clk);
+ return -EINVAL;
+ }
+
+ clk_disable_unprepare(phy_drd->ref_clk);
+
+ return 0;
+}
+
+
+static void exynos_usbdrd_usb_txco_enable(struct phy_usb_instance *inst, int on)
+{
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+ void __iomem *base;
+ u32 reg;
+
+ base = ioremap(0x11860000, 0x100000);
+ reg = readl(base + EXYNOS_USBDEV_PHY_CONTROL);
+
+ dev_info(phy_drd->dev, "[%s] ++USB DEVCTRL reg 0x%x \n",
+ __func__, reg);
+
+ if (!on) {
+ reg |= ENABLE_TCXO_BUF_MASK;
+ } else {
+ reg &= ~ENABLE_TCXO_BUF_MASK;
+ }
+ writel(reg, base + EXYNOS_USBDEV_PHY_CONTROL);
+
+ reg = readl(base + EXYNOS_USBDEV_PHY_CONTROL);
+ dev_info(phy_drd->dev, "[%s] --USB DEVCTRL reg 0x%x \n",
+ __func__, reg);
+}
+
+static void exynos_usbdrd_pipe3_phy_isol(struct phy_usb_instance *inst,
+ unsigned int on, unsigned int mask)
+{
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+ unsigned int val;
+
+ if (!inst->reg_pmu)
+ return;
+
+ val = on ? 0 : mask;
+
+ dev_info(phy_drd->dev, "[%s] val : 0x%x / mask : 0x%x \n",
+ __func__, val, mask);
+ regmap_update_bits(inst->reg_pmu, inst->pmu_offset,
+ mask, val);
+
+ /* Enable TCXO_USB */
+ val = on ? 0 : ENABLE_TCXO_BUF_MASK;
+ regmap_update_bits(inst->reg_pmu, inst->pmu_offset,
+ ENABLE_TCXO_BUF_MASK, val);
+
+ /* exynos_usbdrd_usb_txco_enable(inst, on); */
+}
+
+static void exynos_usbdrd_utmi_phy_isol(struct phy_usb_instance *inst,
+ unsigned int on, unsigned int mask)
+{
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+ unsigned int val;
+
+ if (!inst->reg_pmu)
+ return;
+
+ val = on ? 0 : mask;
+
+ dev_info(phy_drd->dev, "[%s] val : 0x%x / mask : 0x%x \n",
+ __func__, val, mask);
+ regmap_update_bits(inst->reg_pmu, inst->pmu_offset,
+ mask, val);
+
+ exynos_usbdrd_usb_txco_enable(inst, on);
+}
+
+/*
+ * 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
+exynos_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst)
+{
+ return 0;
+}
+
+/*
+ * 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
+exynos_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst)
+{
+ static u32 reg;
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+ /* PHYCLKRST setting isn't required in Combo PHY */
+ if (phy_drd->usbphy_info.version >= EXYNOS_USBPHY_VER_02_0_0)
+ return -EINVAL;
+
+ /* restore any previous reference clock settings */
+ reg = readl(phy_drd->reg_phy + EXYNOS_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;
+}
+
+#ifdef OLD_FASHIONED_PHY_TUNE
+/*
+ * Sets the default PHY tuning values for high-speed connection.
+ */
+static int exynos_usbdrd_fill_hstune(struct exynos_usbdrd_phy *phy_drd,
+ struct device_node *node)
+{
+ struct device *dev = phy_drd->dev;
+ struct exynos_usbphy_hs_tune *hs_tune = phy_drd->hs_value;
+ int ret;
+ u32 res[2];
+ u32 value;
+
+ ret = of_property_read_u32_array(node, "tx_vref", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_vref = res[0];
+ hs_tune[1].tx_vref = res[1];
+ } else {
+ dev_err(dev, "can't get tx_vref value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_pre_emp", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_pre_emp = res[0];
+ hs_tune[1].tx_pre_emp = res[1];
+ } else {
+ dev_err(dev, "can't get tx_pre_emp value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_pre_emp_puls", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_pre_emp_puls = res[0];
+ hs_tune[1].tx_pre_emp_puls = res[1];
+ } else {
+ dev_err(dev, "can't get tx_pre_emp_puls value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_res", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_res = res[0];
+ hs_tune[1].tx_res = res[1];
+ } else {
+ dev_err(dev, "can't get tx_res value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_rise", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_rise = res[0];
+ hs_tune[1].tx_rise = res[1];
+ } else {
+ dev_err(dev, "can't get tx_rise value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_hsxv", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_hsxv = res[0];
+ hs_tune[1].tx_hsxv = res[1];
+ } else {
+ dev_err(dev, "can't get tx_hsxv value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_fsls", res, 2);
+ if (ret == 0) {
+ hs_tune[0].tx_fsls = res[0];
+ hs_tune[1].tx_fsls = res[1];
+ } else {
+ dev_err(dev, "can't get tx_fsls value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "rx_sqrx", res, 2);
+ if (ret == 0) {
+ hs_tune[0].rx_sqrx = res[0];
+ hs_tune[1].rx_sqrx = res[1];
+ } else {
+ dev_err(dev, "can't get tx_sqrx value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "compdis", res, 2);
+ if (ret == 0) {
+ hs_tune[0].compdis = res[0];
+ hs_tune[1].compdis = res[1];
+ } else {
+ dev_err(dev, "can't get compdis value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "otg", res, 2);
+ if (ret == 0) {
+ hs_tune[0].otg = res[0];
+ hs_tune[1].otg = res[1];
+ } else {
+ dev_err(dev, "can't get otg_tune value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "enable_user_imp", res, 2);
+ if (ret == 0) {
+ if (res[0]) {
+ hs_tune[0].enable_user_imp = true;
+ hs_tune[1].enable_user_imp = true;
+ hs_tune[0].user_imp_value = res[1];
+ hs_tune[1].user_imp_value = res[1];
+ } else {
+ hs_tune[0].enable_user_imp = false;
+ hs_tune[1].enable_user_imp = false;
+ }
+ } else {
+ dev_err(dev, "can't get enable_user_imp value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(node, "is_phyclock", &value);
+ if (ret == 0) {
+ if (value == 1) {
+ hs_tune[0].utmi_clk = USBPHY_UTMI_PHYCLOCK;
+ hs_tune[1].utmi_clk = USBPHY_UTMI_PHYCLOCK;
+ } else {
+ hs_tune[0].utmi_clk = USBPHY_UTMI_FREECLOCK;
+ hs_tune[1].utmi_clk = USBPHY_UTMI_FREECLOCK;
+ }
+ } else {
+ dev_err(dev, "can't get is_phyclock value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Sets the default PHY tuning values for super-speed connection.
+ */
+static int exynos_usbdrd_fill_sstune(struct exynos_usbdrd_phy *phy_drd,
+ struct device_node *node)
+{
+ struct device *dev = phy_drd->dev;
+ struct exynos_usbphy_ss_tune *ss_tune = phy_drd->ss_value;
+ u32 res[2];
+ int ret;
+
+ ret = of_property_read_u32_array(node, "tx_boost_level", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_boost_level = res[0];
+ ss_tune[1].tx_boost_level = res[1];
+ } else {
+ dev_err(dev, "can't get tx_boost_level value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_swing_level", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_swing_level = res[0];
+ ss_tune[1].tx_swing_level = res[1];
+ } else {
+ dev_err(dev, "can't get tx_swing_level value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_swing_full", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_swing_full = res[0];
+ ss_tune[1].tx_swing_full = res[1];
+ } else {
+ dev_err(dev, "can't get tx_swing_full value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_swing_low", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_swing_low = res[0];
+ ss_tune[1].tx_swing_low = res[1];
+ } else {
+ dev_err(dev, "can't get tx_swing_low value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_deemphasis_mode", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_deemphasis_mode = res[0];
+ ss_tune[1].tx_deemphasis_mode = res[1];
+ } else {
+ dev_err(dev, "can't get tx_deemphasis_mode value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_deemphasis_3p5db", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_deemphasis_3p5db = res[0];
+ ss_tune[1].tx_deemphasis_3p5db = res[1];
+ } else {
+ dev_err(dev, "can't get tx_deemphasis_3p5db value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "tx_deemphasis_6db", res, 2);
+ if (ret == 0) {
+ ss_tune[0].tx_deemphasis_6db = res[0];
+ ss_tune[1].tx_deemphasis_6db = res[1];
+ } else {
+ dev_err(dev, "can't get tx_deemphasis_6db value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "enable_ssc", res, 2);
+ if (ret == 0) {
+ ss_tune[0].enable_ssc = res[0];
+ ss_tune[1].enable_ssc = res[1];
+ } else {
+ dev_err(dev, "can't get enable_ssc value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "ssc_range", res, 2);
+ if (ret == 0) {
+ ss_tune[0].ssc_range = res[0];
+ ss_tune[1].ssc_range = res[1];
+ } else {
+ dev_err(dev, "can't get ssc_range value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "los_bias", res, 2);
+ if (ret == 0) {
+ ss_tune[0].los_bias = res[0];
+ ss_tune[1].los_bias = res[1];
+ } else {
+ dev_err(dev, "can't get los_bias value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "los_mask_val", res, 2);
+ if (ret == 0) {
+ ss_tune[0].los_mask_val = res[0];
+ ss_tune[1].los_mask_val = res[1];
+ } else {
+ dev_err(dev, "can't get los_mask_val value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "enable_fixed_rxeq_mode", res, 2);
+ if (ret == 0) {
+ ss_tune[0].enable_fixed_rxeq_mode = res[0];
+ ss_tune[1].enable_fixed_rxeq_mode = res[1];
+ } else {
+ dev_err(dev, "can't get enable_fixed_rxeq_mode value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "fix_rxeq_value", res, 2);
+ if (ret == 0) {
+ ss_tune[0].fix_rxeq_value = res[0];
+ ss_tune[1].fix_rxeq_value = res[1];
+ } else {
+ dev_err(dev, "can't get fix_rxeq_value value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "set_crport_level_en", res, 2);
+ if (ret == 0) {
+ ss_tune[0].set_crport_level_en = res[0];
+ ss_tune[1].set_crport_level_en = res[1];
+ } else {
+ dev_err(dev, "can't get set_crport_level_en value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32_array(node, "set_crport_mpll_charge_pump", res, 2);
+ if (ret == 0) {
+ ss_tune[0].set_crport_mpll_charge_pump = res[0];
+ ss_tune[1].set_crport_mpll_charge_pump = res[1];
+ } else {
+ dev_err(dev, "can't get set_crport_mpll_charge_pump value, error = %d\n", ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#endif
+
+static int exynos_usbdrd_fill_hstune_param(struct exynos_usbdrd_phy *phy_drd,
+ struct device_node *node)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *child = NULL;
+ struct exynos_usb_tune_param *hs_tune_param;
+ size_t size = sizeof(struct exynos_usb_tune_param);
+ int ret;
+ u32 res[2];
+ u32 param_index = 0;
+ const char *name;
+
+ ret = of_property_read_u32_array(node, "hs_tune_cnt", &res[0], 1);
+
+ dev_info(dev, "%s hs tune cnt = %d\n", __func__, res[0]);
+
+ hs_tune_param = devm_kzalloc(dev, size*res[0], GFP_KERNEL);
+ phy_drd->usbphy_info.tune_param = hs_tune_param;
+
+ for_each_child_of_node(node, child) {
+ ret = of_property_read_string(child, "tune_name", &name);
+ if (ret == 0) {
+ memcpy(hs_tune_param[param_index].name, name, strlen(name));
+ } else {
+ dev_err(dev, "failed to read hs tune name from %s node\n", child->name);
+ return ret;
+ }
+
+ ret = of_property_read_u32_array(child, "tune_value", res, 2);
+ if (ret == 0) {
+ hs_tune_param[param_index].value = res[0];
+ } else {
+ dev_err(dev, "failed to read hs tune value from %s node\n", child->name);
+ return -EINVAL;
+ }
+ param_index++;
+ }
+
+ hs_tune_param[param_index].value = EXYNOS_USB_TUNE_LAST;
+
+ return 0;
+}
+
+/*
+ * Sets the default PHY tuning values for super-speed connection.
+ */
+static int exynos_usbdrd_fill_sstune_param(struct exynos_usbdrd_phy *phy_drd,
+ struct device_node *node)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *child = NULL;
+ struct exynos_usb_tune_param *ss_tune_param;
+ size_t size = sizeof(struct exynos_usb_tune_param);
+ int ret;
+ u32 res[2];
+ u32 param_index = 0;
+ const char *name;
+
+ ret = of_property_read_u32_array(node, "ss_tune_cnt", &res[0], 1);
+
+ dev_info(dev, "%s ss tune cnt = %d\n", __func__, res[0]);
+
+ ss_tune_param = devm_kzalloc(dev, size*res[0], GFP_KERNEL);
+ phy_drd->usbphy_sub_info.tune_param = ss_tune_param;
+ for_each_child_of_node(node, child) {
+ ret = of_property_read_string(child, "tune_name", &name);
+ if (ret == 0)
+ memcpy(ss_tune_param[param_index].name, name, strlen(name));
+ else {
+ dev_err(dev, "failed to read ss tune name from %s node\n", child->name);
+ return ret;
+ }
+
+ ret = of_property_read_u32_array(child, "tune_value", res, 2);
+ if (ret == 0) {
+ ss_tune_param[param_index].value = res[0];
+ } else {
+ dev_err(dev, "failed to read ss tune value from %s node\n", child->name);
+ return -EINVAL;
+ }
+ param_index++;
+ }
+
+ ss_tune_param[param_index].value = EXYNOS_USB_TUNE_LAST;
+
+ return 0;
+}
+
+static int exynos_usbdrd_get_phy_refsel(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *node = dev->of_node;
+ int value, ret;
+ int check_flag = 0;
+
+ ret = of_property_read_u32(node, "phy_refsel_clockcore", &value);
+ if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_clockcore, error = %d\n", ret);
+ return ret;
+ }
+
+ if (value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_CLKCORE;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_CLKCORE;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_ext_osc", &value);
+ if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_ext_osc, error = %d\n", ret);
+ return ret;
+ }
+
+ if (value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_EXT_OSC;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_EXT_OSC;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_xtal", &value);
+ if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_xtal, error = %d\n", ret);
+ return ret;
+ }
+
+ if (value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_EXT_XTAL;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_EXT_XTAL;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_diff_pad", &value);
+ if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_diff_pad, error = %d\n", ret);
+ return ret;
+ }
+
+ if (value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_PAD;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_PAD;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_diff_internal", &value);
+ if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_diff_internal, error = %d\n", ret);
+ return ret;
+ }
+
+ if (value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_INTERNAL;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_INTERNAL;
+ } else {
+ check_flag++;
+ }
+
+ ret = of_property_read_u32(node, "phy_refsel_diff_single", &value);
+ if (ret < 0) {
+ dev_err(dev, "can't get phy_refsel_diff_single, error = %d\n", ret);
+ return ret;
+ }
+
+ if (value == 1) {
+ phy_drd->usbphy_info.refsel = USBPHY_REFSEL_DIFF_SINGLE;
+ phy_drd->usbphy_sub_info.refsel = USBPHY_REFSEL_DIFF_SINGLE;
+ } else {
+ check_flag++;
+ }
+
+ if (check_flag > 5) {
+ dev_err(dev, "USB refsel Must be choosed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int exynos_usbdrd_get_sub_phyinfo(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *tune_node;
+ int ret;
+ int value;
+
+ if (!of_property_read_u32(dev->of_node, "sub_phy_version", &value)) {
+ phy_drd->usbphy_sub_info.version = value;
+ } else {
+ dev_err(dev, "can't get sub_phy_version\n");
+ return -EINVAL;
+ }
+ phy_drd->usbphy_sub_info.refclk = phy_drd->extrefclk;
+ phy_drd->usbphy_sub_info.regs_base = phy_drd->reg_phy2;
+ phy_drd->usbphy_sub_info.regs_base_2nd = phy_drd->reg_phy3;
+
+ /*
+ * use PHY of samsung
+ */
+ tune_node = of_parse_phandle(dev->of_node, "ss_tune_param", 0);
+ if (tune_node != NULL) {
+ ret = exynos_usbdrd_fill_sstune_param(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill super speed tuning param\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int exynos_usbdrd_get_phyinfo(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ struct device_node *tune_node;
+ int ret;
+ int value;
+
+ if (!of_property_read_u32(dev->of_node, "phy_version", &value)) {
+ phy_drd->usbphy_info.version = value;
+ } else {
+ dev_err(dev, "can't get phy_version\n");
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(dev->of_node, "use_io_for_ovc", &value)) {
+ phy_drd->usbphy_info.use_io_for_ovc = value ? true : false;
+ } else {
+ dev_err(dev, "can't get io_for_ovc\n");
+ return -EINVAL;
+ }
+
+ if (!of_property_read_u32(dev->of_node, "common_block_disable", &value)) {
+ phy_drd->usbphy_info.common_block_disable = value ? true : false;
+ } else {
+ dev_err(dev, "can't get common_block_disable\n");
+ return -EINVAL;
+ }
+
+ phy_drd->usbphy_info.refclk = phy_drd->extrefclk;
+ phy_drd->usbphy_info.regs_base = phy_drd->reg_phy;
+
+ if (!of_property_read_u32(dev->of_node, "is_not_vbus_pad", &value)) {
+ phy_drd->usbphy_info.not_used_vbus_pad = value ? true : false;
+ } else {
+ dev_err(dev, "can't get vbus_pad\n");
+ return -EINVAL;
+ }
+ if (!of_property_read_u32(dev->of_node, "used_phy_port", &value)) {
+ phy_drd->usbphy_info.used_phy_port = value ? true : false;
+ } else {
+ dev_err(dev, "can't get used_phy_port\n");
+ return -EINVAL;
+ }
+
+ ret = exynos_usbdrd_get_phy_refsel(phy_drd);
+ if (ret < 0)
+ dev_err(dev, "can't get phy refsel\n");
+
+#ifdef OLD_FASHIONED_PHY_TUNE
+ /*
+ * use PHY of synopsys
+ */
+ tune_node = of_parse_phandle(dev->of_node, "ss_tune_info", 0);
+ if (tune_node == NULL)
+ dev_info(dev, "don't need usbphy tuning info for super speed\n");
+
+ if (of_device_is_available(tune_node)) {
+ ret = exynos_usbdrd_fill_sstune(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill super speed tuning info\n");
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * use PHY of synopsys
+ */
+ tune_node = of_parse_phandle(dev->of_node, "hs_tune_info", 0);
+ if (tune_node == NULL)
+ dev_info(dev, "don't need usbphy tuning info for high speed\n");
+
+ if (of_device_is_available(tune_node)) {
+ ret = exynos_usbdrd_fill_hstune(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill high speed tuning info\n");
+ return -EINVAL;
+ }
+ }
+#endif
+
+ /*
+ * use PHY of synopsys
+ */
+ tune_node = of_parse_phandle(dev->of_node, "ss_tune_param", 0);
+ if (tune_node != NULL) {
+ ret = exynos_usbdrd_fill_sstune_param(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill super speed tuning param\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_info(dev, "don't need usbphy tuning param for super speed\n");
+ }
+
+ /*
+ * use PHY of samsung
+ */
+ tune_node = of_parse_phandle(dev->of_node, "hs_tune_param", 0);
+ if (tune_node != NULL) {
+ ret = exynos_usbdrd_fill_hstune_param(phy_drd, tune_node);
+ if (ret < 0) {
+ dev_err(dev, "can't fill high speed tuning param\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_info(dev, "don't need usbphy tuning param for high speed\n");
+ }
+
+ dev_info(phy_drd->dev, "usbphy info: version:0x%x, refclk:0x%x\n",
+ phy_drd->usbphy_info.version, phy_drd->usbphy_info.refclk);
+
+ return 0;
+}
+
+static int exynos_usbdrd_get_iptype(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct device *dev = phy_drd->dev;
+ int ret, value;
+
+ ret = of_property_read_u32(dev->of_node, "ip_type", &value);
+ if (ret) {
+ dev_err(dev, "can't get ip type");
+ return ret;
+ }
+
+ switch (value) {
+ case TYPE_USB3DRD:
+ phy_drd->ip_type = TYPE_USB3DRD;
+ dev_info(dev, "It is TYPE USB3DRD");
+ break;
+ case TYPE_USB3HOST:
+ phy_drd->ip_type = TYPE_USB3HOST;
+ dev_info(dev, "It is TYPE USB3HOST");
+ break;
+ case TYPE_USB2DRD:
+ phy_drd->ip_type = TYPE_USB2DRD;
+ dev_info(dev, "It is TYPE USB2DRD");
+ break;
+ case TYPE_USB2HOST:
+ phy_drd->ip_type = TYPE_USB2HOST;
+ dev_info(dev, "It is TYPE USB2HOST");
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void exynos_usbdrd_pipe3_init(struct exynos_usbdrd_phy *phy_drd)
+{
+ exynos_usbdrd_check_connection(phy_drd);
+ phy_exynos_usb_v3p1_enable(&phy_drd->usbphy_info);
+}
+
+static void exynos_usbdrd_utmi_init(struct exynos_usbdrd_phy *phy_drd)
+{
+ int ret;
+ struct phy_usb_instance *inst = &phy_drd->phys[0];
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+ struct tune_bits *otp_data;
+ u8 otp_type;
+ u8 otp_index;
+ u8 i;
+#endif
+ pr_info("%s: +++\n", __func__);
+
+ if (gpio_is_valid(phy_drd->phy_port)) {
+ phy_drd->usbphy_info.used_phy_port = !gpio_get_value(phy_drd->phy_port);
+ dev_info(phy_drd->dev, "%s: phy port[%d]\n", __func__,
+ phy_drd->usbphy_info.used_phy_port);
+ }
+
+ inst->phy_cfg->phy_isol(inst, 0, inst->pmu_mask);
+
+ ret = exynos_usbdrd_clk_enable(phy_drd, false);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s: Failed to enable clk\n", __func__);
+ return;
+ }
+
+ phy_exynos_usb_v3p1_enable(&phy_drd->usbphy_info);
+ /*
+ * The below function is used to block USB3.0 PHY. If you don't want to
+ * use USB3.0 PHY, add this function and comment phy_exynos_usbv3p1_pipe
+ * _ready().
+ *
+ * phy_exynos_usb_v3p1_pipe_ovrd(&phy_drd->usbphy_info);
+ */
+ phy_exynos_usb_v3p1_pipe_ready(&phy_drd->usbphy_info);
+ if (phy_drd->use_phy_umux) {
+ /* USB User MUX enable */
+ ret = exynos_usbdrd_clk_enable(phy_drd, true);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s: Failed to enable clk\n", __func__);
+ return;
+ }
+ }
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+ if (phy_drd->ip_type < TYPE_USB2DRD) {
+ otp_type = phy_drd->otp_type[OTP_USB3PHY_INDEX];
+ otp_index = phy_drd->otp_index[OTP_USB3PHY_INDEX];
+ otp_data = phy_drd->otp_data[OTP_USB3PHY_INDEX];
+ } else {
+ otp_type = phy_drd->otp_type[OTP_USB2PHY_INDEX];
+ otp_index = phy_drd->otp_index[OTP_USB2PHY_INDEX];
+ otp_data = phy_drd->otp_data[OTP_USB2PHY_INDEX];
+ }
+
+ for (i = 0; i < otp_index; i++) {
+ samsung_exynos_cal_usb3phy_write_register(
+ &phy_drd->usbphy_info,
+ otp_data[i].index * otp_type,
+ otp_data[i].value);
+ }
+#endif
+
+ pr_info("%s: ---\n", __func__);
+}
+
+static int exynos_usbdrd_phy_init(struct phy *phy)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ /* UTMI or PIPE3 specific init */
+ inst->phy_cfg->phy_init(phy_drd);
+
+ return 0;
+}
+
+static void exynos_usbdrd_pipe3_exit(struct exynos_usbdrd_phy *phy_drd)
+{
+ pr_info("%s : Do nothing...\n", __func__);
+}
+
+static void exynos_usbdrd_utmi_exit(struct exynos_usbdrd_phy *phy_drd)
+{
+ struct phy_usb_instance *inst = &phy_drd->phys[0];
+
+ if (phy_drd->use_phy_umux) {
+ /*USB User MUX disable */
+ exynos_usbdrd_clk_disable(phy_drd, true);
+ }
+ phy_exynos_usb_v3p1_disable(&phy_drd->usbphy_info);
+
+ exynos_usbdrd_clk_disable(phy_drd, false);
+
+ inst->phy_cfg->phy_isol(inst, 1, inst->pmu_mask);
+}
+
+static int exynos_usbdrd_phy_exit(struct phy *phy)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ /* UTMI or PIPE3 specific exit */
+ inst->phy_cfg->phy_exit(phy_drd);
+
+ return 0;
+}
+
+static void exynos_usbdrd_pipe3_tune(struct exynos_usbdrd_phy *phy_drd,
+ int phy_state)
+{
+ struct exynos_usb_tune_param *ss_tune_param =
+ phy_drd->usbphy_info.ss_tune_param;
+ int i = 0;
+
+ exynos_usbdrd_check_connection(phy_drd);
+
+ dev_info(phy_drd->dev, "%s %s %d\n", __func__, ss_tune_param[0].name,
+ ss_tune_param[0].value);
+
+ for (i = 0; ss_tune_param[i].value != EXYNOS_USB_TUNE_LAST; i++)
+ phy_exynos_usb_v3p1_tune_each(&phy_drd->usbphy_info,
+ ss_tune_param[i].name, ss_tune_param[i].value);
+}
+
+static void exynos_usbdrd_utmi_tune(struct exynos_usbdrd_phy *phy_drd,
+ int phy_state)
+{
+ struct exynos_usb_tune_param *hs_tune_param = phy_drd->usbphy_info.tune_param;
+
+ dev_info(phy_drd->dev, "%s %s %d\n", __func__, hs_tune_param[0].name,
+ hs_tune_param[0].value);
+
+ phy_exynos_usb_v3p1_tune(&phy_drd->usbphy_info);
+
+ /* USB3P1 CAL code doesn't provide late_enable api */
+ /* samsung_exynos_cal_usb3phy_late_enable(&phy_drd->usbphy_info); */
+}
+
+static int exynos_usbdrd_phy_tune(struct phy *phy, int phy_state)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ inst->phy_cfg->phy_tune(phy_drd, phy_state);
+
+ return 0;
+}
+
+static void exynos_usbdrd_pipe3_set(struct exynos_usbdrd_phy *phy_drd,
+ int option, void *info)
+{
+}
+
+static void exynos_usbdrd_utmi_set(struct exynos_usbdrd_phy *phy_drd,
+ int option, void *info)
+{
+ switch (option) {
+ case SET_DPPULLUP_ENABLE:
+#if 0
+ phy_exynos_usb_v3p1_enable_dp_pullup(
+ &phy_drd->usbphy_info);
+#endif
+ break;
+ case SET_DPPULLUP_DISABLE:
+#if 0
+ phy_exynos_usb_v3p1_disable_dp_pullup(
+ &phy_drd->usbphy_info);
+#endif
+ break;
+ case SET_DPDM_PULLDOWN:
+ phy_exynos_usb_v3p1_config_host_mode(
+ &phy_drd->usbphy_info);
+ default:
+ break;
+ }
+}
+
+static int exynos_usbdrd_phy_set(struct phy *phy, int option, void *info)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ inst->phy_cfg->phy_set(phy_drd, option, info);
+
+ return 0;
+}
+
+static int exynos_usbdrd_phy_power_on(struct phy *phy)
+{
+ int ret;
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n");
+
+ /* Enable VBUS supply */
+ if (phy_drd->vbus) {
+ ret = regulator_enable(phy_drd->vbus);
+ if (ret) {
+ dev_err(phy_drd->dev, "Failed to enable VBUS supply\n");
+ return ret;
+ }
+ }
+
+ inst->phy_cfg->phy_isol(inst, 0, inst->pmu_mask);
+
+ return 0;
+}
+
+static int exynos_usbdrd_phy_power_off(struct phy *phy)
+{
+ struct phy_usb_instance *inst = phy_get_drvdata(phy);
+ struct exynos_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+ dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n");
+
+ /* Disable VBUS supply */
+ if (phy_drd->vbus)
+ regulator_disable(phy_drd->vbus);
+
+ inst->phy_cfg->phy_isol(inst, 1, inst->pmu_mask);
+
+ return 0;
+}
+
+static struct phy *exynos_usbdrd_phy_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct exynos_usbdrd_phy *phy_drd = dev_get_drvdata(dev);
+
+ if (WARN_ON(args->args[0] > EXYNOS_DRDPHYS_NUM))
+ return ERR_PTR(-ENODEV);
+
+ return phy_drd->phys[args->args[0]].phy;
+}
+
+static struct phy_ops exynos_usbdrd_phy_ops = {
+ .init = exynos_usbdrd_phy_init,
+ .exit = exynos_usbdrd_phy_exit,
+ .tune = exynos_usbdrd_phy_tune,
+ .set = exynos_usbdrd_phy_set,
+ .power_on = exynos_usbdrd_phy_power_on,
+ .power_off = exynos_usbdrd_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static const struct exynos_usbdrd_phy_config phy_cfg_exynos[] = {
+ {
+ .id = EXYNOS_DRDPHY_UTMI,
+ .phy_isol = exynos_usbdrd_utmi_phy_isol,
+ .phy_init = exynos_usbdrd_utmi_init,
+ .phy_exit = exynos_usbdrd_utmi_exit,
+ .phy_tune = exynos_usbdrd_utmi_tune,
+ .phy_set = exynos_usbdrd_utmi_set,
+ .set_refclk = exynos_usbdrd_utmi_set_refclk,
+ },
+ {
+ .id = EXYNOS_DRDPHY_PIPE3,
+ .phy_isol = exynos_usbdrd_pipe3_phy_isol,
+ .phy_init = exynos_usbdrd_pipe3_init,
+ .phy_exit = exynos_usbdrd_pipe3_exit,
+ .phy_tune = exynos_usbdrd_pipe3_tune,
+ .phy_set = exynos_usbdrd_pipe3_set,
+ .set_refclk = exynos_usbdrd_pipe3_set_refclk,
+ },
+};
+
+static const struct exynos_usbdrd_phy_drvdata exynos_usbdrd_phy = {
+ .phy_cfg = phy_cfg_exynos,
+};
+
+static const struct of_device_id exynos_usbdrd_phy_of_match[] = {
+ {
+ .compatible = "samsung,exynos-usbdrd-phy",
+ .data = &exynos_usbdrd_phy
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match);
+
+static int exynos_usbdrd_phy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_usbdrd_phy *phy_drd;
+ struct phy_provider *phy_provider;
+ struct resource *res;
+ const struct of_device_id *match;
+ const struct exynos_usbdrd_phy_drvdata *drv_data;
+ struct phy_usb_instance *inst;
+ struct regmap *reg_pmu;
+ u32 pmu_offset, pmu_mask;
+ int i, ret;
+
+ pr_info("%s: +++ %s\n", __func__, pdev->name);
+ 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;
+
+ match = of_match_node(exynos_usbdrd_phy_of_match, pdev->dev.of_node);
+
+ drv_data = match->data;
+ phy_drd->drv_data = drv_data;
+
+ 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);
+
+ ret = exynos_usbdrd_get_iptype(phy_drd);
+ if (ret) {
+ dev_err(dev, "%s: Failed to get ip_type\n", __func__);
+ return ret;
+ }
+
+ ret = exynos_usbdrd_clk_get(phy_drd);
+ if (ret) {
+ dev_err(dev, "%s: Failed to get clocks\n", __func__);
+ return ret;
+ }
+
+ ret = exynos_usbdrd_clk_prepare(phy_drd);
+ if (ret) {
+ dev_err(dev, "%s: Failed to prepare clocks\n", __func__);
+ 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");
+ goto err;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "pmu_offset", &pmu_offset);
+ if (ret < 0) {
+ dev_err(dev, "couldn't read pmu_offset on %s node, error = %d\n",
+ dev->of_node->name, ret);
+ goto err;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "pmu_mask", &pmu_mask);
+ if (ret < 0) {
+ dev_err(dev, "couldn't read pmu_mask on %s node, error = %d\n",
+ dev->of_node->name, ret);
+ goto err;
+ }
+
+ dev_vdbg(dev, "Creating usbdrd_phy phy\n");
+ phy_drd->phy_port = of_get_named_gpio(dev->of_node,
+ "phy,gpio_phy_port", 0);
+ if (gpio_is_valid(phy_drd->phy_port)) {
+ dev_err(dev, "PHY CON Selection OK\n");
+
+ ret = gpio_request(phy_drd->phy_port, "PHY_CON");
+ if (ret)
+ dev_err(dev, "fail to request gpio %s:%d\n", "PHY_CON", ret);
+ else
+ gpio_direction_input(phy_drd->phy_port);
+ } else {
+ dev_err(dev, "non-DT: PHY CON Selection\n");
+ }
+
+ if (!of_property_read_u32(dev->of_node, "has_combo_phy", &ret)) {
+ if (ret) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ phy_drd->reg_phy2 = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy_drd->reg_phy2))
+ return PTR_ERR(phy_drd->reg_phy2);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ phy_drd->reg_phy3 = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy_drd->reg_phy3))
+ return PTR_ERR(phy_drd->reg_phy3);
+
+ exynos_usbdrd_get_sub_phyinfo(phy_drd);
+ } else {
+ dev_err(dev, "It has not the other phy\n");
+ }
+ }
+#if IS_ENABLED(CONFIG_EXYNOS_OTP)
+ exynos_usbdrd_phy_get_otp_info(phy_drd);
+#endif
+
+ for (i = 0; i < EXYNOS_DRDPHYS_NUM; i++) {
+ struct phy *phy = devm_phy_create(dev, NULL,
+ &exynos_usbdrd_phy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "Failed to create usbdrd_phy phy\n");
+ goto err;
+ }
+
+ 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].pmu_mask = pmu_mask;
+ phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i];
+ phy_set_drvdata(phy, &phy_drd->phys[i]);
+ }
+#if IS_ENABLED(CONFIG_PHY_EXYNOS_DEBUGFS)
+ ret = exynos_usbdrd_debugfs_init(phy_drd);
+ if (ret) {
+ dev_err(dev, "Failed to initialize debugfs\n");
+ goto err;
+ }
+#endif
+
+ inst = &phy_drd->phys[0];
+ inst->phy_cfg->phy_isol(inst, 0, inst->pmu_mask);
+ ret = exynos_rate_to_clk(phy_drd);
+ if (ret) {
+ dev_err(phy_drd->dev, "%s: Not supported ref clock\n",
+ __func__);
+ goto err;
+ }
+ inst->phy_cfg->phy_isol(inst, 1, inst->pmu_mask);
+
+ ret = exynos_usbdrd_get_phyinfo(phy_drd);
+ if (ret)
+ goto err;
+
+ /*
+ * Both has_other_phy and has_combo_phy can't be enabled at the same time.
+ * It's alternative.
+ */
+ if (!of_property_read_u32(dev->of_node, "has_other_phy", &ret)) {
+ if (ret) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ phy_drd->reg_phy2 = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy_drd->reg_phy2))
+ return PTR_ERR(phy_drd->reg_phy2);
+
+ phy_drd->usbphy_info.regs_base_2nd = phy_drd->reg_phy2;
+ phy_drd->usbphy_info.ss_tune_param =
+ phy_drd->usbphy_sub_info.tune_param;
+ } else {
+ dev_err(dev, "It has not the other phy\n");
+ }
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev,
+ exynos_usbdrd_phy_xlate);
+ if (IS_ERR(phy_provider)) {
+ dev_err(phy_drd->dev, "Failed to register phy provider\n");
+ goto err;
+ }
+
+ pr_info("%s: ---\n", __func__);
+ return 0;
+
+err:
+ exynos_usbdrd_clk_unprepare(phy_drd);
+
+ return ret;
+}
+
+#define EXYNOS_USBDRD_PHY_PM_OPS NULL
+
+static struct platform_driver phy_exynos_usbdrd = {
+ .probe = exynos_usbdrd_phy_probe,
+ .driver = {
+ .of_match_table = exynos_usbdrd_phy_of_match,
+ .name = "phy_exynos_usbdrd",
+ .pm = EXYNOS_USBDRD_PHY_PM_OPS,
+ }
+};
+
+module_platform_driver(phy_exynos_usbdrd);
+MODULE_DESCRIPTION("Samsung EXYNOS SoCs USB DRD controller PHY driver");
+MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:phy_exynos_usbdrd");
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ *
+ * USBPHY configuration definitions for Samsung USB PHY CAL
+ *
+ * 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.
+ *
+ * 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 __PHY_SAMSUNG_USB_FW_CAL_H__
+#define __PHY_SAMSUNG_USB_FW_CAL_H__
+
+#define EXYNOS_USBCON_VER_01_0_0 0x0100 /* Istor */
+#define EXYNOS_USBCON_VER_01_0_1 0x0101 /* JF 3.0 */
+#define EXYNOS_USBCON_VER_01_1_1 0x0111 /* KC */
+#define EXYNOS_USBCON_VER_01_MAX 0x01FF
+
+#define EXYNOS_USBCON_VER_02_0_0 0x0200 /* Insel-D, Island */
+#define EXYNOS_USBCON_VER_02_0_1 0x0201 /* JF EVT0 2.0 Host */
+#define EXYNOS_USBCON_VER_02_1_0 0x0210
+#define EXYNOS_USBCON_VER_02_1_1 0x0211 /* JF EVT1 2.0 Host */
+#define EXYNOS_USBCON_VER_02_1_2 0x0212 /* Katmai EVT0 */
+#define EXYNOS_USBCON_VER_02_MAX 0x02FF
+
+#define EXYNOS_USBCON_VER_03_0_0 0x0300 /* Lhotse, Lassen HS, Ramen HS */
+#define EXYNOS_USBCON_VER_03_0_1 0x0301 /* MK */
+#define EXYNOS_USBCON_VER_03_MAX 0x03FF
+
+#define EXYNOS_USBCON_VER_04_0_0 0x0400 /* Lhotse - USB/DP */
+#define EXYNOS_USBCON_VER_04_MAX 0x04FF
+
+/* Sub phy control - not include System/Link control */
+#define EXYNOS_USBCON_VER_05_0_0 0x0500 /* High Speed Only */
+#define EXYNOS_USBCON_VER_05_1_0 0x0510 /* Super Speed */
+#define EXYNOS_USBCON_VER_05_3_0 0x0530 /* Super Speed Dual PHY */
+#define EXYNOS_USBCON_VER_05_MAX 0x05FF
+
+#define EXYNOS_USBCON_VER_F2_0_0 0xF200
+#define EXYNOS_USBCON_VER_F2_MAX 0xF2FF
+
+#define EXYNOS_USBCON_VER_MAJOR_VER_MASK 0xFF00
+#define EXYNOS_USBCON_VER_SS_CAP 0x0010
+
+#define EXYNOS_USBCON_VER_MINOR(_x) ((_x) & 0xf)
+#define EXYNOS_USBCON_VER_MID(_x) ((_x) & 0xf0)
+#define EXYNOS_USBCON_VER_MAJOR(_x) ((_x) & 0xff00)
+
+enum exynos_usbphy_mode {
+ USBPHY_MODE_DEV = 0,
+ USBPHY_MODE_HOST = 1,
+
+ /* usb phy for uart bypass mode */
+ USBPHY_MODE_BYPASS = 0x10,
+};
+
+enum exynos_usbphy_refclk {
+ USBPHY_REFCLK_DIFF_100MHZ = 0x80 | 0x27,
+ USBPHY_REFCLK_DIFF_52MHZ = 0x80 | 0x02 | 0x40,
+ USBPHY_REFCLK_DIFF_26MHZ = 0x80 | 0x02,
+ USBPHY_REFCLK_DIFF_24MHZ = 0x80 | 0x2a,
+ USBPHY_REFCLK_DIFF_20MHZ = 0x80 | 0x31,
+ USBPHY_REFCLK_DIFF_19_2MHZ = 0x80 | 0x38,
+
+ USBPHY_REFCLK_EXT_50MHZ = 0x07,
+ USBPHY_REFCLK_EXT_26MHZ = 0x06,
+ USBPHY_REFCLK_EXT_24MHZ = 0x05,
+ USBPHY_REFCLK_EXT_20MHZ = 0x04,
+ USBPHY_REFCLK_EXT_12MHZ = 0x02,
+};
+
+enum exynos_usbphy_refsel {
+ USBPHY_REFSEL_CLKCORE = 0x2,
+ USBPHY_REFSEL_EXT_OSC = 0x1,
+ USBPHY_REFSEL_EXT_XTAL = 0x0,
+
+ USBPHY_REFSEL_DIFF_PAD = 0x6,
+ USBPHY_REFSEL_DIFF_INTERNAL = 0x4,
+ USBPHY_REFSEL_DIFF_SINGLE = 0x3,
+};
+
+enum exynos_usbphy_utmi {
+ USBPHY_UTMI_FREECLOCK, USBPHY_UTMI_PHYCLOCK,
+};
+
+enum exynos_usbphy_tune_para {
+ USBPHY_TUNE_HS_COMPDIS = 0x0,
+ USBPHY_TUNE_HS_OTG = 0x1,
+ USBPHY_TUNE_HS_SQRX = 0x2,
+ USBPHY_TUNE_HS_TXFSLS = 0x3,
+ USBPHY_TUNE_HS_TXHSXV = 0x4,
+ USBPHY_TUNE_HS_TXPREEMP = 0x5,
+ USBPHY_TUNE_HS_TXPREEMP_PLUS = 0x6,
+ USBPHY_TUNE_HS_TXRES = 0x7,
+ USBPHY_TUNE_HS_TXRISE = 0x8,
+ USBPHY_TUNE_HS_TXVREF = 0x9,
+
+ USBPHY_TUNE_SS_TX_BOOST = 0x0 | 0x10000,
+ USBPHY_TUNE_SS_TX_SWING = 0x1 | 0x10000,
+ USBPHY_TUNE_SS_TX_DEEMPHASIS = 0x2 | 0x10000,
+ USBPHY_TUNE_SS_LOS_BIAS = 0x3 | 0x10000,
+ USBPHY_TUNE_SS_LOS_MASK_VAL = 0x4 | 0x10000,
+ USBPHY_TUNE_SS_FIX_EQ = 0x5 | 0x10000,
+ USBPHY_TUNE_SS_RX_EQ = 0x6 | 0x10000,
+
+ USBPHY_TUNE_COMBO = 0x20000,
+ USBPHY_TUNE_COMBO_TX_AMP = USBPHY_TUNE_COMBO | 0x0,
+ USBPHY_TUNE_COMBO_TX_EMPHASIS = USBPHY_TUNE_COMBO | 0x1,
+ USBPHY_TUNE_COMBO_TX_IDRV = USBPHY_TUNE_COMBO | 0x2,
+ USBPHY_TUNE_COMBO_TX_ACCDRV = USBPHY_TUNE_COMBO | 0x3,
+};
+
+enum exynos_usb_bc {
+ BC_NO_CHARGER,
+ BC_SDP,
+ BC_DCP,
+ BC_CDP,
+ BC_ACA_DOCK,
+ BC_ACA_A,
+ BC_ACA_B,
+ BC_ACA_C,
+};
+
+struct exynos_usb_tune_param {
+ char name[30];
+ int value;
+};
+
+#define EXYNOS_USB_TUNE_LAST 0x4C415354
+
+/* HS PHY tune parameter */
+struct exynos_usbphy_hs_tune {
+ u8 tx_vref;
+ u8 tx_pre_emp;
+ u8 tx_pre_emp_puls;
+ u8 tx_res;
+ u8 tx_rise;
+ u8 tx_hsxv;
+ u8 tx_fsls;
+ u8 rx_sqrx;
+ u8 compdis;
+ u8 otg;
+ bool enable_user_imp;
+ u8 user_imp_value;
+ enum exynos_usbphy_utmi utmi_clk;
+};
+
+/* SS PHY tune parameter */
+struct exynos_usbphy_ss_tune {
+ /* TX Swing Level*/
+ u8 tx_boost_level;
+ u8 tx_swing_level;
+ u8 tx_swing_full;
+ u8 tx_swing_low;
+ /* TX De-Emphasis */
+ u8 tx_deemphasis_mode;
+ u8 tx_deemphasis_3p5db;
+ u8 tx_deemphasis_6db;
+ /* SSC Operation*/
+ u8 enable_ssc;
+ u8 ssc_range;
+ /* Loss-of-Signal detector threshold level */
+ u8 los_bias;
+ /* Loss-of-Signal mask width */
+ u16 los_mask_val;
+ /* RX equalizer mode */
+ u8 enable_fixed_rxeq_mode;
+ u8 fix_rxeq_value;
+ /* Decrease TX Impedance */
+ u8 decrease_ss_tx_imp;
+
+ u8 set_crport_level_en;
+ u8 set_crport_mpll_charge_pump;
+ /* RX LFPS(decode) mode */
+ u8 rx_decode_mode;
+};
+
+/**
+ * struct exynos_usbphy_info : USBPHY information to share USBPHY CAL code
+ * @version: PHY controller version
+ * 0x0100 - for EXYNOS_USB3 : EXYNOS7420, EXYNOS7890
+ * 0x0101 - EXYNOS8890
+ * 0x0111 - EXYNOS8895
+ * 0x0200 - for EXYNOS_USB2 : EXYNOS7580, EXYNOS3475
+ * 0x0210 - EXYNOS8890_EVT1
+ * 0xF200 - for EXT : EXYNOS7420_HSIC
+ * @refclk: reference clock frequency for USBPHY
+ * @refsrc: reference clock source path for USBPHY
+ * @use_io_for_ovc: use over-current notification io for USBLINK
+ * @regs_base: base address of PHY control register *
+ */
+
+struct exynos_usbphy_info {
+ /* Device Information */
+ struct device *dev;
+
+ u32 version;
+ enum exynos_usbphy_refclk refclk;
+ enum exynos_usbphy_refsel refsel;
+
+ bool use_io_for_ovc;
+ bool common_block_disable;
+ bool not_used_vbus_pad;
+
+ void __iomem *regs_base;
+
+ /* HS PHY tune parameter */
+ struct exynos_usbphy_hs_tune *hs_tune;
+
+ /* SS PHY tune parameter */
+ struct exynos_usbphy_ss_tune *ss_tune;
+
+ /* Tune Parma list - Synopsys USB3 PHY */
+ struct exynos_usb_tune_param *ss_tune_param;
+
+ /* Tune Parma list */
+ struct exynos_usb_tune_param *tune_param;
+
+ /* multiple phy */
+ int hw_version;
+ void __iomem *regs_base_2nd;
+ int used_phy_port;
+
+ /* Alternative PHY REF_CLK source */
+ bool alt_ref_clk;
+
+ /* Remote Wake-up Advisor */
+ unsigned hs_rewa :1;
+
+ /* Dual PHY */
+ bool dual_phy;
+};
+
+
+#endif /* __PHY_SAMSUNG_USB_FW_CAL_H__ */
--- /dev/null
+#ifndef __PHY_SAMSUNG_COMBO_H__
+#define __PHY_SAMSUNG_COMBO_H__
+
+#define EXYNOS_USBCON_CTRLVER (0x0)
+#define CTRLVER_CTRL_VER_MASK (0xFFFFFFFF)
+
+#define EXYNOS_USBCON_LINKCTRL (0x4)
+#define LINKCTRL_FORCE_RXELECIDLE (0x1 << 18)
+#define LINKCTRL_FORCE_PHYSTATUS (0x1 << 17)
+#define LINKCTRL_FORCE_PIPE_EN (0x1 << 16)
+#define LINKCTRL_DIS_BUSPEND_QACT (0x1 << 13)
+#define LINKCTRL_DIS_LINKGATE_QACT (0x1 << 12)
+#define LINKCTRL_DIS_ID0_QACT (0x1 << 11)
+#define LINKCTRL_DIS_VBUSVALID_QACT (0x1 << 10)
+#define LINKCTRL_DIS_BVALID_QACT (0x1 << 9)
+#define LINKCTRL_FORCE_QACT (0x1 << 8)
+#define LINKCTRL_BUS_FILTER_BYPASS_MASK (0xF << 4)
+#define LINKCTRL_HOST_SYSTEM_ERR (0x1 << 2)
+#define LINKCTRL_LINK_PME (0x1 << 1)
+#define LINKCTRL_PME_GENERATION (0x1 << 0)
+
+#define EXYNOS_USBCON_PHYCON_LINKPORT (0x08)
+#define PHYCON_LINKPORT_HOST_NUM_U3_MASK (0xF << 16)
+#define PHYCON_LINKPORT_HOST_NUM_U2_MASK (0xF << 12)
+#define PHYCON_LINKPORT_HOST_U3_PORT_DISABLE (0x1 << 9)
+#define PHYCON_LINKPORT_HOST_U2_PORT_DISABLE (0x1 << 8)
+#define PHYCON_LINKPORT_HOST_PORT_POWER_CON_PRESENT (0x1 << 6)
+#define PHYCON_LINKPORT_HUB_PORT_OVERCURRENT_U3 (0x1 << 5)
+#define PHYCON_LINKPORT_HUB_PORT_OVERCURRENT_U2 (0x1 << 4)
+#define PHYCON_LINKPORT_HUB_PORT_OVERCURRENT_SET_U3 (0x1 << 3)
+#define PHYCON_LINKPORT_HUB_PORT_OVERCURRENT_SET_U2 (0x1 << 2)
+#define PHYCON_LINKPORT_HUB_PERM_ATTACH_U3 (0x1 << 1)
+#define PHYCON_LINKPORT_HUB_PERM_ATTACH_U2 (0x1 << 0)
+
+#define EXYNOS_USBCON_LINK_DEBUG_L (0xC)
+#define LINK_DEBUG_L_DEBUG_L_MASK (0xFFFFFFFF)
+
+#define EXYNOS_USBCON_LINK_DEBUG_H (0x10)
+#define LINK_DEBUG_H_DEBUG_H_MASK (0xFFFFFFFF)
+
+#define EXYNOS_USBCON_LTSTATE_HIS (0x14)
+#define LTSTATE_HIS_LINKTRN_DONE (0x1 << 31)
+#define LTSTATE_HIS_LTSTATE_HIS4_MASK (0xF << 16)
+#define LTSTATE_HIS_LTSTATE_HIS3_MASK (0xF << 12)
+#define LTSTATE_HIS_LTSTATE_HIS2_MASK (0xF << 8)
+#define LTSTATE_HIS_LTSTATE_HIS1_MASK (0xF << 4)
+#define LTSTATE_HIS_LTSTATE_HIS0_MASK (0xF << 0)
+
+#define EXYNOS_USBCON_CLKRSTCTRL (0x20)
+#define CLKRSTCTRL_USBAUDIO_CLK_GATE_EN (0x1 << 9)
+#define CLKRSTCTRL_USBAUDIO_CLK_SEL (0x1 << 8)
+#define CLKRSTCTRL_LINK_PCLK_SEL (0x1 << 7)
+#define CLKRSTCTRL_REFCLKSEL (0x1 << 4)
+#define CLKRSTCTRL_PHY_SW_RST (0x1 << 3)
+#define CLKRSTCTRL_PHY_RESET_SEL (0x1 << 2)
+#define CLKRSTCTRL_PORTRESET (0x1 << 1)
+#define CLKRSTCTRL_LINK_SW_RST (0x1 << 0)
+
+#define EXYNOS_USBCON_PWRCTL (0x24)
+#define PWRCTL_FORCE_POWERDOWN (0x1 << 2)
+
+#define EXYNOS_USBCON_SSPPLLCTL (0x30)
+#define SSPPLLCTL_FSEL_MASK (0x3F << 0)
+
+#define EXYNOS_USBCON_SECPMACTL (0x48)
+#define SECPMACTL_PMA_PLL_REF_CLK_SEL_MASK (0x3 << 10)
+#define SECPMACTL_PMA_REF_FREQ_SEL_MASK (0x3 << 8)
+#define SECPMACTL_PMA_LOW_PWR (0x1 << 4)
+#define SECPMACTL_PMA_TRSV_SW_RST (0x1 << 3)
+#define SECPMACTL_PMA_CMN_SW_RST (0x1 << 2)
+#define SECPMACTL_PMA_INIT_SW_RST (0x1 << 1)
+#define SECPMACTL_PMA_APB_SW_RST (0x1 << 0)
+
+#define EXYNOS_USBCON_UTMICTRL (0x50)
+#define UTMICTRL_OPMODE_EN (0x1 << 8)
+#define UTMICTRL_FORCE_OPMODE_MASK (0x3 << 6)
+#define UTMICTRL_FORCE_VBUSVALID (0x1 << 5)
+#define UTMICTRL_FORCE_BVALID (0x1 << 4)
+#define UTMICTRL_FORCE_DPPULLDOWN (0x1 << 3)
+#define UTMICTRL_FORCE_DMPULLDOWN (0x1 << 2)
+#define UTMICTRL_FORCE_SUSPEND (0x1 << 1)
+#define UTMICTRL_FORCE_SLEEP (0x1 << 0)
+
+#define EXYNOS_USBCON_HSPCTRL (0x54)
+#define HSPCTRL_AUTORSM_ENB (0x1 << 29)
+#define HSPCTRL_RETENABLE_EN (0x1 << 28)
+#define HSPCTRL_FSLS_SPEED_SEL (0x1 << 25)
+#define HSPCTRL_FSV_OUT_EN (0x1 << 24)
+#define HSPCTRL_HS_XCVR_EXT_CTL (0x1 << 22)
+#define HSPCTRL_HS_RXDAT (0x1 << 21)
+#define HSPCTRL_HS_SQUELCH (0x1 << 20)
+#define HSPCTRL_FSVMINUS (0x1 << 17)
+#define HSPCTRL_FSVPLUS (0x1 << 16)
+#define HSPCTRL_VBUSVLDEXTSEL (0x1 << 13)
+#define HSPCTRL_VBUSVLDEXT (0x1 << 12)
+#define HSPCTRL_EN_UTMISUSPEND (0x1 << 9)
+#define HSPCTRL_COMMONONN (0x1 << 8)
+#define HSPCTRL_VATESTENB (0x1 << 6)
+#define HSPCTRL_CHGDET (0x1 << 5)
+#define HSPCTRL_VDATSRCENB (0x1 << 4)
+#define HSPCTRL_VDATDETENB (0x1 << 3)
+#define HSPCTRL_CHRGSEL (0x1 << 2)
+#define HSPCTRL_ACAENB (0x1 << 1)
+#define HSPCTRL_DEDENB (0x1 << 0)
+
+#define EXYNOS_USBCON_HSPPARACON (0x58)
+#define HSPPARACON_TXVREFTUNE_MASK (0xF << 28)
+#define HSPPARACON_TXRISETUNE_MASK (0x3 << 24)
+#define HSPPARACON_TXRESTUNE_MASK (0x3 << 21)
+#define HSPPARACON_TXPREEMPPULSETUNE (0x1 << 20)
+#define HSPPARACON_TXPREEMPAMPTUNE_MASK (0x3 << 18)
+#define HSPPARACON_TXHSXVTUNE_MASK (0x3 << 16)
+#define HSPPARACON_TXFSLSTUNE_MASK (0xF << 12)
+#define HSPPARACON_SQRXTUNE_MASK (0x7 << 8)
+#define HSPPARACON_OTGTUNE_MASK (0x7 << 4)
+#define HSPPARACON_COMPDISTUNE_MASK (0x7 << 0)
+
+#define EXYNOS_USBCON_HSPTEST (0x5C)
+#define HSPTEST_SIDDQ_PORT0 (0x1 << 24)
+#define HSPTEST_LINESTATE_PORT0_MASK (0x3 << 20)
+#define HSPTEST_TESTDATAOUT_PORT0_MASK (0xF << 16)
+#define HSPTEST_TESTCLK_PORT0 (0x1 << 13)
+#define HSPTEST_TESTDATAOUTSEL_PORT0 (0x1 << 12)
+#define HSPTEST_TESTADDR_PORT0_MASK (0xF << 8)
+#define HSPTEST_TESTDATAIN_PORT0_MASK (0xFF << 0)
+
+#define EXYNOS_USBCON_HSPPLLTUNE (0x60)
+#define HSPPLLTUNE_PLLBTUNE (0x1 << 8)
+#define HSPPLLTUNE_PLLITUNE_MASK (0x3 << 4)
+#define HSPPLLTUNE_PLLPTUNE_MASK (0xF << 0)
+
+#define EXYNOS_USBCON_REWA_CTRL (0x100)
+#define REWA_CTRL_HSREWA_EN (0x1 << 0)
+
+#define EXYNOS_USBCON_HSREWA_INTR (0x104)
+#define HSREWA_INTR_WAKEUP_MASK_MASK (0x1 << 12)
+#define HSREWA_INTR_TIMEOUT_INTR_MASK_MASK (0x1 << 8)
+#define HSREWA_INTR_EVENT_INT_MASK_MASK (0x1 << 4)
+#define HSREWA_INTR_WAKEUP_INTR_MASK_MASK (0x1 << 0)
+
+#define EXYNOS_USBCON_HSREWA_CTRL (0x108)
+#define HSREWA_CTRL_DIG_BYPASS_CON_EN (0x1 << 28)
+#define HSREWA_CTRL_DPDM_MONITOR_SEL (0x1 << 24)
+#define HSREWA_CTRL_HS_LINK_READY (0x1 << 20)
+#define HSREWA_CTRL_HS_SYS_VALID (0x1 << 16)
+#define HSREWA_CTRL_HS_REWA_ERROR (0x1 << 4)
+#define HSREWA_CTRL_HS_REWA_DONE (0x1 << 0)
+
+#define EXYNOS_USBCON_HSREWA_REFTO (0x10C)
+#define HSREWA_REFTO_HOST_K_TIMEOUT_MASK (0xFFFFFFFF << 0)
+
+#define EXYNOS_USBCON_HSREWA_HSTK (0x110)
+#define HSREWA_HSTK_HOST_K_DELAY_MASK (0xFFFFFFFF << 0)
+
+#define EXYNOS_USBCON_HSREWA_CNT (0x114)
+#define HSREWA_CNT_WAKEUP_CNT_MASK (0xFFFFFFFF << 0)
+
+#define EXYNOS_USBCON_HSREWA_INT1_EVNT (0x118)
+#define HSREWA_INT1_EVNT_ERR_SUS (0x1 << 18)
+#define HSREWA_INT1_EVNT_ERR_DEV_K (0x1 << 17)
+#define HSREWA_INT1_EVNT_DISCON (0x1 << 16)
+#define HSREWA_INT1_EVNT_BYPASS_DIS (0x1 << 2)
+#define HSREWA_INT1_EVNT_RET_DIS (0x1 << 1)
+#define HSREWA_INT1_EVNT_RET_EN (0x1 << 0)
+
+#define EXYNOS_USBCON_HSREWA_INT1_EVNT_MSK (0x11C)
+#define HSREWA_INT1_EVNT_MSK_ERR_SUS_MASK (0x1 << 18)
+#define HSREWA_INT1_EVNT_MSK_ERR_DEV_K_MASK (0x1 << 17)
+#define HSREWA_INT1_EVNT_MSK_DISCON_MASK (0x1 << 16)
+#define HSREWA_INT1_EVNT_MSK_BYPASS_DIS_MASK (0x1 << 2)
+#define HSREWA_INT1_EVNT_MSK_RET_DIS_MASK (0x1 << 1)
+#define HSREWA_INT1_EVNT_MSK_RET_EN_MASK (0x1 << 0)
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Sung-Hyun Na <sunghyun.na@samsung.com>
+ * Author: Minho Lee <minho55.lee@samsung.com>
+ *
+ * Chip Abstraction Layer for USB PHY
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#ifdef __KERNEL__
+
+#ifndef __EXCITE__
+#include <linux/delay.h>
+#include <linux/io.h>
+#endif
+
+#include <linux/platform_device.h>
+
+#else
+
+#include "types.h"
+#include "customfunctions.h"
+#include "mct.h"
+
+#endif
+
+#include "phy-samsung-usb-cal.h"
+#include "phy-samsung-usb3-cal.h"
+
+#define USB_SS_TX_TUNE_PCS
+
+//#define USB_MUX_UTMI_ENABLE
+
+enum exynos_usbcon_cr {
+ USBCON_CR_ADDR = 0,
+ USBCON_CR_DATA = 1,
+ USBCON_CR_READ = 18,
+ USBCON_CR_WRITE = 19,
+};
+
+static u16 samsung_exynos_cal_cr_access(struct exynos_usbphy_info *usbphy_info,
+ enum exynos_usbcon_cr cr_bit, u16 data)
+{
+ void __iomem *base;
+ u32 phyreg0;
+ u32 phyreg1;
+ u32 loop;
+ u32 loop_cnt;
+
+ if (usbphy_info->used_phy_port != -1) {
+ if (usbphy_info->used_phy_port == 0)
+ base = usbphy_info->regs_base;
+ else
+ base = usbphy_info->regs_base_2nd;
+
+ } else
+ base = usbphy_info->regs_base;
+
+ /* Clear CR port register */
+ phyreg0 = readl(base + EXYNOS_USBCON_PHYREG0);
+ phyreg0 &= ~(0xfffff);
+ writel(phyreg0, base + EXYNOS_USBCON_PHYREG0);
+
+ /* Set Data for cr port */
+ phyreg0 &= ~PHYREG0_CR_DATA_MASK;
+ phyreg0 |= PHYREG0_CR_DATA_IN(data);
+ writel(phyreg0, base + EXYNOS_USBCON_PHYREG0);
+
+ if (cr_bit == USBCON_CR_ADDR)
+ loop = 1;
+ else
+ loop = 2;
+
+ for (loop_cnt = 0; loop_cnt < loop; loop_cnt++) {
+ u32 trigger_bit = 0;
+ u32 handshake_cnt = 2;
+ /* Trigger cr port */
+ if (cr_bit == USBCON_CR_ADDR)
+ trigger_bit = PHYREG0_CR_CR_CAP_ADDR;
+ else {
+ if (loop_cnt == 0)
+ trigger_bit = PHYREG0_CR_CR_CAP_DATA;
+ else {
+ if (cr_bit == USBCON_CR_READ)
+ trigger_bit = PHYREG0_CR_READ;
+ else
+ trigger_bit = PHYREG0_CR_WRITE;
+ }
+ }
+ /* Handshake Procedure */
+ do {
+ u32 usec = 100;
+ if (handshake_cnt == 2)
+ phyreg0 |= trigger_bit;
+ else
+ phyreg0 &= ~trigger_bit;
+ writel(phyreg0, base + EXYNOS_USBCON_PHYREG0);
+
+ /* Handshake */
+ do {
+ phyreg1 = readl(base + EXYNOS_USBCON_PHYREG1);
+ if ((handshake_cnt == 2)
+ && (phyreg1 & PHYREG1_CR_ACK))
+ break;
+ else if ((handshake_cnt == 1)
+ && !(phyreg1 & PHYREG1_CR_ACK))
+ break;
+
+ udelay(1);
+ } while (usec-- > 0);
+
+ if (!usec)
+ pr_err("CRPORT handshake timeout1 (0x%08x)\n",
+ phyreg0);
+
+ udelay(5);
+ handshake_cnt--;
+ } while (handshake_cnt != 0);
+ udelay(50);
+ }
+ return (u16) ((phyreg1 & PHYREG1_CR_DATA_OUT_MASK) >> 1);
+}
+
+void samsung_exynos_cal_cr_write(struct exynos_usbphy_info *usbphy_info,
+ u16 addr, u16 data)
+{
+ samsung_exynos_cal_cr_access(usbphy_info, USBCON_CR_ADDR, addr);
+ samsung_exynos_cal_cr_access(usbphy_info, USBCON_CR_WRITE, data);
+}
+
+u16 samsung_exynos_cal_cr_read(struct exynos_usbphy_info *usbphy_info, u16 addr)
+{
+ samsung_exynos_cal_cr_access(usbphy_info, USBCON_CR_ADDR, addr);
+ return samsung_exynos_cal_cr_access(usbphy_info, USBCON_CR_READ, 0);
+}
+
+void samsung_exynos_cal_usb3phy_tune_fix_rxeq(
+ struct exynos_usbphy_info *usbphy_info)
+{
+ u16 reg;
+ struct exynos_usbphy_ss_tune *tune = usbphy_info->ss_tune;
+
+ if (!tune)
+ return;
+
+ reg = samsung_exynos_cal_cr_read(usbphy_info, 0x1006);
+ reg &= ~(1 << 6);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1006, reg);
+
+ udelay(10);
+
+ reg |= (1 << 7);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1006, reg);
+
+ udelay(10);
+
+ reg &= ~(0x7 << 0x8);
+ reg |= (tune->fix_rxeq_value << 8);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1006, reg);
+
+ udelay(10);
+
+ reg |= (1 << 11);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1006, reg);
+
+ printk("Reg RX_OVRD_IN_HI : 0x%x\n",
+ samsung_exynos_cal_cr_read(usbphy_info, 0x1006));
+
+ printk("Reg RX_CDR_CDR_FSM_DEBUG : 0x%x\n",
+ samsung_exynos_cal_cr_read(usbphy_info, 0x101c));
+}
+
+static void set_ss_tx_impedance(struct exynos_usbphy_info *usbphy_info)
+{
+ u16 rtune_debug, tx_ovrd_in_hi;
+ u8 tx_imp;
+
+ /* obtain calibration code for 45Ohm impedance */
+ rtune_debug = samsung_exynos_cal_cr_read(usbphy_info, 0x3);
+ /* set SUP.DIG.RTUNE_DEBUG.TYPE = 2 */
+ rtune_debug &= ~(0x3 << 3);
+ rtune_debug |= (0x2 << 3);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x3, rtune_debug);
+
+ /* read SUP.DIG.RTUNE_STAT (0x0004[9:0]) */
+ tx_imp = samsung_exynos_cal_cr_read(usbphy_info, 0x4);
+ /* current_tx_cal_code[9:0] = SUP.DIG.RTUNE_STAT (0x0004[9:0]) */
+ tx_imp += 8;
+ /* tx_cal_code[9:0] = current_tx_cal_code[9:0] + 8(decimal)
+ * NOTE, max value is 63;
+ * i.e. if tx_cal_code[9:0] > 63, tx_cal_code[9:0]==63; */
+ if (tx_imp > 63)
+ tx_imp = 63;
+
+ /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET_OVRD = 1 */
+ tx_ovrd_in_hi = samsung_exynos_cal_cr_read(usbphy_info, 0x1001);
+ tx_ovrd_in_hi |= (1 << 7);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1001, tx_ovrd_in_hi);
+
+ /* SUP.DIG.RTUNE_DEBUG.MAN_TUNE = 0 */
+ rtune_debug &= ~(1 << 1);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x3, rtune_debug);
+
+ /* set SUP.DIG.RTUNE_DEBUG.VALUE = tx_cal_code[9:0] */
+ rtune_debug &= ~(0x1ff << 5);
+ rtune_debug |= (tx_imp << 5);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x3, rtune_debug);
+
+ /* set SUP.DIG.RTUNE_DEBUG.SET_VAL = 1 */
+ rtune_debug |= (1 << 2);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x3, rtune_debug);
+
+ /* set SUP.DIG.RTUNE_DEBUG.SET_VAL = 0 */
+ rtune_debug &= ~(1 << 2);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x3, rtune_debug);
+
+ /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET = 1 */
+ tx_ovrd_in_hi |= (1 << 6);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1001, tx_ovrd_in_hi);
+
+ /* set LANEX_DIG_TX_OVRD_IN_HI.TX_RESET = 0 */
+ tx_ovrd_in_hi &= ~(1 << 6);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1001, tx_ovrd_in_hi);
+}
+
+void samsung_exynos_cal_usb3phy_tune_adaptive_eq(
+ struct exynos_usbphy_info *usbphy_info, u8 eq_fix)
+{
+ u16 reg;
+
+ reg = samsung_exynos_cal_cr_read(usbphy_info, 0x1006);
+ if (eq_fix) {
+ reg |= (1 << 6);
+ reg &= ~(1 << 7);
+ } else {
+ reg &= ~(1 << 6);
+ reg |= (1 << 7);
+ }
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1006, reg);
+}
+
+void samsung_exynos_cal_usb3phy_tune_chg_rxeq(
+ struct exynos_usbphy_info *usbphy_info, u8 eq_val)
+{
+ u16 reg;
+
+ reg = samsung_exynos_cal_cr_read(usbphy_info, 0x1006);
+ reg &= ~(0x7 << 0x8);
+ reg |= ((eq_val & 0x7) << 8);
+ reg |= (1 << 11);
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1006, reg);
+}
+
+static void exynos_cal_ss_enable(struct exynos_usbphy_info *info,
+ u32 *arg_phyclkrst, u8 port_num)
+{
+ u32 phyclkrst = *arg_phyclkrst;
+ u32 phypipe;
+ u32 phyparam0;
+ u32 phyreg0;
+ void *reg_base;
+
+ if (port_num == 0)
+ reg_base = info->regs_base;
+ else
+ reg_base = info->regs_base_2nd;
+
+ if (info->version < EXYNOS_USBCON_VER_01_0_1)
+ phyclkrst |= PHYCLKRST_COMMONONN;
+
+ /* Disable Common block control by link */
+ phyclkrst &= ~PHYCLKRST_EN_UTMISUSPEND;
+
+ /* Digital Supply Mode : normal operating mode */
+ phyclkrst |= PHYCLKRST_RETENABLEN;
+
+ /* ref. clock enable for ss function */
+ phyclkrst |= PHYCLKRST_REF_SSP_EN;
+
+ phyparam0 = readl(info->regs_base + EXYNOS_USBCON_PHYPARAM0);
+ if (info->refsel == USBPHY_REFSEL_DIFF_PAD)
+ phyparam0 |= PHYPARAM0_REF_USE_PAD;
+ else
+ phyparam0 &= ~PHYPARAM0_REF_USE_PAD;
+ writel(phyparam0, reg_base + EXYNOS_USBCON_PHYPARAM0);
+
+ if (info->version >= EXYNOS_USBCON_VER_01_0_1)
+ phyreg0 = readl(reg_base + EXYNOS_USBCON_PHYREG0);
+
+ switch (info->refclk) {
+ case USBPHY_REFCLK_DIFF_100MHZ:
+ phyclkrst &= ~PHYCLKRST_MPLL_MULTIPLIER_MASK;
+ phyclkrst |= PHYCLKRST_MPLL_MULTIPLIER(0x00);
+ phyclkrst &= ~PHYCLKRST_REF_CLKDIV2;
+ if (info->version == EXYNOS_USBCON_VER_01_0_1) {
+ phyreg0 &= ~PHYREG0_SSC_REFCLKSEL_MASK;
+ phyreg0 |= PHYREG0_SSC_REFCLKSEL(0x00);
+ } else {
+ phyclkrst &= ~PHYCLKRST_SSC_REFCLKSEL_MASK;
+ phyclkrst |= PHYCLKRST_SSC_REFCLKSEL(0x00);
+ }
+ break;
+ case USBPHY_REFCLK_DIFF_26MHZ:
+ case USBPHY_REFCLK_DIFF_52MHZ:
+ phyclkrst &= ~PHYCLKRST_MPLL_MULTIPLIER_MASK;
+ phyclkrst |= PHYCLKRST_MPLL_MULTIPLIER(0x60);
+ if (info->refclk & 0x40)
+ phyclkrst |= PHYCLKRST_REF_CLKDIV2;
+ else
+ phyclkrst &= ~PHYCLKRST_REF_CLKDIV2;
+ if (info->version == EXYNOS_USBCON_VER_01_0_1) {
+ phyreg0 &= ~PHYREG0_SSC_REFCLKSEL_MASK;
+ phyreg0 |= PHYREG0_SSC_REFCLKSEL(0x108);
+ } else {
+ phyclkrst &= ~PHYCLKRST_SSC_REFCLKSEL_MASK;
+ phyclkrst |= PHYCLKRST_SSC_REFCLKSEL(0x108);
+ }
+ break;
+ case USBPHY_REFCLK_DIFF_24MHZ:
+ case USBPHY_REFCLK_EXT_24MHZ:
+ phyclkrst &= ~PHYCLKRST_MPLL_MULTIPLIER_MASK;
+ phyclkrst |= PHYCLKRST_MPLL_MULTIPLIER(0x00);
+ phyclkrst &= ~PHYCLKRST_REF_CLKDIV2;
+ if (info->version != EXYNOS_USBCON_VER_01_0_1) {
+ phyclkrst &= ~PHYCLKRST_SSC_REFCLKSEL_MASK;
+ phyclkrst |= PHYCLKRST_SSC_REFCLKSEL(0x88);
+ } else {
+ phyreg0 &= ~PHYREG0_SSC_REFCLKSEL_MASK;
+ phyreg0 |= PHYREG0_SSC_REFCLKSEL(0x88);
+ }
+ break;
+ case USBPHY_REFCLK_DIFF_20MHZ:
+ case USBPHY_REFCLK_DIFF_19_2MHZ:
+ phyclkrst &= ~PHYCLKRST_MPLL_MULTIPLIER_MASK;
+ phyclkrst &= ~PHYCLKRST_REF_CLKDIV2;
+ if (info->version == EXYNOS_USBCON_VER_01_0_1)
+ phyreg0 &= ~PHYREG0_SSC_REFCLKSEL_MASK;
+ else
+ phyclkrst &= ~PHYCLKRST_SSC_REFCLKSEL_MASK;
+ break;
+ default:
+ break;
+ }
+ /* SSC Enable */
+ if (info->ss_tune) {
+ u32 range = info->ss_tune->ssc_range;
+
+ if (info->ss_tune->enable_ssc)
+ phyclkrst |= PHYCLKRST_SSC_EN;
+ else
+ phyclkrst &= ~PHYCLKRST_SSC_EN;
+
+ if (info->version != EXYNOS_USBCON_VER_01_0_1) {
+ phyclkrst &= ~PHYCLKRST_SSC_RANGE_MASK;
+ phyclkrst |= PHYCLKRST_SSC_RANGE(range);
+ } else {
+ phyreg0 &= ~PHYREG0_SSC_RANGE_MASK;
+ phyreg0 |= PHYREG0_SSC_RAMGE(range);
+ }
+ } else {
+ /* Default Value : SSC Enable */
+ phyclkrst |= PHYCLKRST_SSC_EN;
+ if (info->version != EXYNOS_USBCON_VER_01_0_1) {
+ phyclkrst &= ~PHYCLKRST_SSC_RANGE_MASK;
+ phyclkrst |= PHYCLKRST_SSC_RANGE(0x0);
+ } else {
+ phyreg0 &= ~PHYREG0_SSC_RANGE_MASK;
+ phyreg0 |= PHYREG0_SSC_RAMGE(0x0);
+ }
+ }
+
+ /* Select UTMI CLOCK 0 : PHY CLOCK, 1 : FREE CLOCK */
+ phypipe = readl(reg_base + EXYNOS_USBCON_PHYPIPE);
+ phypipe |= PHY_CLOCK_SEL;
+ writel(phypipe, reg_base + EXYNOS_USBCON_PHYPIPE);
+
+ if (info->version >= EXYNOS_USBCON_VER_01_0_1)
+ writel(phyreg0, reg_base + EXYNOS_USBCON_PHYREG0);
+
+ *arg_phyclkrst = phyclkrst;
+}
+
+static void exynos_cal_ss_power_down(struct exynos_usbphy_info *info, bool en)
+{
+ u32 phytest;
+
+ phytest = readl(info->regs_base + EXYNOS_USBCON_PHYTEST);
+ if (en == 1) {
+ phytest &= ~PHYTEST_POWERDOWN_HSP;
+ phytest &= ~PHYTEST_POWERDOWN_SSP;
+ writel(phytest, info->regs_base + EXYNOS_USBCON_PHYTEST);
+ if (info->used_phy_port == 1) {
+ void *reg_base = info->regs_base_2nd;
+
+ phytest |= PHYTEST_POWERDOWN_SSP;
+ writel(phytest, info->regs_base + EXYNOS_USBCON_PHYTEST);
+
+ phytest = readl(reg_base + EXYNOS_USBCON_PHYTEST);
+ phytest &= ~PHYTEST_POWERDOWN_HSP;
+ phytest &= ~PHYTEST_POWERDOWN_SSP;
+ writel(phytest, reg_base + EXYNOS_USBCON_PHYTEST);
+ }
+ } else {
+ phytest |= PHYTEST_POWERDOWN_HSP;
+ phytest |= PHYTEST_POWERDOWN_SSP;
+ writel(phytest, info->regs_base + EXYNOS_USBCON_PHYTEST);
+ if (info->used_phy_port == 1) {
+ void *reg_base = info->regs_base_2nd;
+
+ phytest = readl(reg_base + EXYNOS_USBCON_PHYTEST);
+ phytest |= PHYTEST_POWERDOWN_HSP;
+ phytest |= PHYTEST_POWERDOWN_SSP;
+ writel(phytest, reg_base + EXYNOS_USBCON_PHYTEST);
+ }
+ }
+}
+
+static void exynos_cal_hs_enable(struct exynos_usbphy_info *info,
+ u32 *arg_phyclkrst)
+{
+ u32 phyclkrst = *arg_phyclkrst;
+
+ u32 hsphyplltune = readl(info->regs_base + EXYNOS_USBCON_HSPHYPLLTUNE);
+
+ if ((info->version & 0xf0) >= 0x10) {
+ /* Disable Common block control by link */
+ phyclkrst |= PHYCLKRST_EN_UTMISUSPEND;
+ phyclkrst |= PHYCLKRST_COMMONONN;
+ } else {
+ phyclkrst |= PHYCLKRST_EN_UTMISUSPEND;
+ phyclkrst |= PHYCLKRST_COMMONONN;
+ }
+
+ /* Change PHY PLL Tune value */
+ if (info->refclk == USBPHY_REFCLK_EXT_24MHZ)
+ hsphyplltune |= HSPHYPLLTUNE_PLL_B_TUNE;
+ else
+ hsphyplltune &= ~HSPHYPLLTUNE_PLL_B_TUNE;
+ hsphyplltune |= HSPHYPLLTUNE_PLL_P_TUNE(0xe);
+ writel(hsphyplltune, info->regs_base + EXYNOS_USBCON_HSPHYPLLTUNE);
+
+ *arg_phyclkrst = phyclkrst;
+}
+
+static void exynos_cal_hs_power_down(struct exynos_usbphy_info *info, bool en)
+{
+ u32 hsphyctrl;
+
+ hsphyctrl = readl(info->regs_base + EXYNOS_USBCON_HSPHYCTRL);
+ if (en) {
+ hsphyctrl &= ~HSPHYCTRL_SIDDQ;
+ hsphyctrl &= ~HSPHYCTRL_PHYSWRST;
+ hsphyctrl &= ~HSPHYCTRL_PHYSWRSTALL;
+ } else {
+ hsphyctrl |= HSPHYCTRL_SIDDQ;
+ }
+ writel(hsphyctrl, info->regs_base + EXYNOS_USBCON_HSPHYCTRL);
+}
+
+static void exynos_cal_usbphy_q_ch(void *regs_base, u8 enable)
+{
+ u32 phy_resume;
+
+ if (enable) {
+ /* WA for Q-channel: disable all q-act from usb */
+ phy_resume = readl(regs_base + EXYNOS_USBCON_PHYRESUME);
+ phy_resume |= PHYRESUME_DIS_ID0_QACT;
+ phy_resume |= PHYRESUME_DIS_VBUSVALID_QACT;
+ phy_resume |= PHYRESUME_DIS_BVALID_QACT;
+ phy_resume |= PHYRESUME_DIS_LINKGATE_QACT;
+ //phy_resume |= PHYRESUME_DIS_BUSPEND_QACT;
+ phy_resume &= ~PHYRESUME_FORCE_QACT;
+ udelay(500);
+ writel(phy_resume, regs_base + EXYNOS_USBCON_PHYRESUME);
+ udelay(500);
+ phy_resume = readl(regs_base + EXYNOS_USBCON_PHYRESUME);
+ phy_resume |= PHYRESUME_FORCE_QACT;
+ udelay(500);
+ writel(phy_resume, regs_base + EXYNOS_USBCON_PHYRESUME);
+ } else {
+ phy_resume = readl(regs_base + EXYNOS_USBCON_PHYRESUME);
+ phy_resume &= ~PHYRESUME_FORCE_QACT;
+ phy_resume |= PHYRESUME_DIS_ID0_QACT;
+ phy_resume |= PHYRESUME_DIS_VBUSVALID_QACT;
+ phy_resume |= PHYRESUME_DIS_BVALID_QACT;
+ phy_resume |= PHYRESUME_DIS_LINKGATE_QACT;
+ //phy_resume |= PHYRESUME_DIS_BUSPEND_QACT;
+ writel(phy_resume, regs_base + EXYNOS_USBCON_PHYRESUME);
+ }
+}
+
+void samsung_exynos_cal_usb3phy_enable(struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 version = usbphy_info->version;
+ enum exynos_usbphy_refclk refclkfreq = usbphy_info->refclk;
+ u32 phyutmi;
+ u32 phyclkrst;
+ u32 linkport;
+
+ /* check phycon version */
+ if (!usbphy_info->hw_version) {
+ u32 phyversion = readl(regs_base);
+
+ if (!phyversion) {
+ usbphy_info->hw_version = -1;
+ usbphy_info->used_phy_port = -1;
+ } else {
+ usbphy_info->hw_version = phyversion;
+ usbphy_info->regs_base += 4;
+ regs_base = usbphy_info->regs_base;
+
+ if (usbphy_info->regs_base_2nd)
+ usbphy_info->regs_base_2nd += 4;
+ }
+ }
+
+ /* Set force q-channel */
+ if ((version & 0xf) >= 0x01)
+ exynos_cal_usbphy_q_ch(regs_base, 1);
+
+ /* Select PHY MUX */
+ if (usbphy_info->used_phy_port != -1) {
+ u32 physel;
+
+ physel = readl(regs_base + EXYNOS_USBCON_PHYSELECTION);
+ if (usbphy_info->used_phy_port == 0) {
+ physel &= ~PHYSEL_PIPE;
+ physel &= ~PHYSEL_PIPE_CLK;
+#if defined(USB_MUX_UTMI_ENABLE)
+ /* UE_TASK: for utmi 2nd port test : will be removed */
+ physel &= ~PHYSEL_UTMI_CLK;
+ physel &= ~PHYSEL_UTMI;
+ physel &= ~PHYSEL_SIDEBAND;
+#endif
+ } else {
+ physel |= PHYSEL_PIPE;
+ physel |= PHYSEL_PIPE_CLK;
+#if defined(USB_MUX_UTMI_ENABLE)
+ /* UE_TASK: for utmi 2nd port test : will be removed */
+ physel |= PHYSEL_UTMI_CLK;
+ physel |= PHYSEL_UTMI;
+ physel |= PHYSEL_SIDEBAND;
+#endif
+ }
+ writel(physel, regs_base + EXYNOS_USBCON_PHYSELECTION);
+ }
+
+ /* set phy clock & control HS phy */
+ phyclkrst = readl(regs_base + EXYNOS_USBCON_PHYCLKRST);
+
+ /* assert port_reset */
+ phyclkrst |= PHYCLKRST_PORTRESET;
+
+ /* Select Reference clock source path */
+ phyclkrst &= ~PHYCLKRST_REFCLKSEL_MASK;
+ phyclkrst |= PHYCLKRST_REFCLKSEL(usbphy_info->refsel);
+
+ /* Select ref clk */
+ phyclkrst &= ~PHYCLKRST_FSEL_MASK;
+ phyclkrst |= PHYCLKRST_FSEL(refclkfreq & 0x3f);
+
+ /* Enable Common Block in suspend/sleep mode */
+ phyclkrst &= ~PHYCLKRST_COMMONONN;
+
+ /* Additional control for 3.0 PHY */
+ if ((EXYNOS_USBCON_VER_01_0_0 <= version)
+ && (version <= EXYNOS_USBCON_VER_01_MAX)) {
+ exynos_cal_ss_enable(usbphy_info, &phyclkrst, 0);
+ /* select used phy port */
+ if (usbphy_info->used_phy_port == 1) {
+ void *reg_2nd = usbphy_info->regs_base_2nd;
+
+ exynos_cal_ss_enable(usbphy_info, &phyclkrst, 1);
+
+ writel(phyclkrst,
+ reg_2nd + EXYNOS_USBCON_PHYCLKRST);
+ udelay(10);
+
+ phyclkrst &= ~PHYCLKRST_PORTRESET;
+ writel(phyclkrst,
+ reg_2nd + EXYNOS_USBCON_PHYCLKRST);
+
+ phyclkrst |= PHYCLKRST_PORTRESET;
+ }
+ } else if ((EXYNOS_USBCON_VER_02_0_0 <= version)
+ && (version <= EXYNOS_USBCON_VER_02_MAX))
+ exynos_cal_hs_enable(usbphy_info, &phyclkrst);
+
+ writel(phyclkrst, regs_base + EXYNOS_USBCON_PHYCLKRST);
+ udelay(10);
+
+ phyclkrst &= ~PHYCLKRST_PORTRESET;
+ writel(phyclkrst, regs_base + EXYNOS_USBCON_PHYCLKRST);
+
+ if ((EXYNOS_USBCON_VER_01_0_0 <= version)
+ && (version <= EXYNOS_USBCON_VER_01_MAX)) {
+ exynos_cal_ss_power_down(usbphy_info, 1);
+ } else if (version >= EXYNOS_USBCON_VER_02_0_0
+ && version <= EXYNOS_USBCON_VER_02_MAX) {
+ exynos_cal_hs_power_down(usbphy_info, 1);
+ }
+ udelay(500);
+
+ /* release force_sleep & force_suspend */
+ phyutmi = readl(regs_base + EXYNOS_USBCON_PHYUTMI);
+ phyutmi &= ~PHYUTMI_FORCESLEEP;
+ phyutmi &= ~PHYUTMI_FORCESUSPEND;
+
+ /* DP/DM Pull Down Control */
+ phyutmi &= ~PHYUTMI_DMPULLDOWN;
+ phyutmi &= ~PHYUTMI_DPPULLDOWN;
+
+ /* Set VBUSVALID signal if VBUS pad is not used */
+ if (usbphy_info->not_used_vbus_pad) {
+ u32 linksystem;
+
+ linksystem = readl(regs_base + EXYNOS_USBCON_LINKSYSTEM);
+ linksystem |= LINKSYSTEM_FORCE_BVALID;
+ linksystem |= LINKSYSTEM_FORCE_VBUSVALID;
+ writel(linksystem, regs_base + EXYNOS_USBCON_LINKSYSTEM);
+#if defined(USB_MUX_UTMI_ENABLE)
+ /* UE_TASK: for utmi 2nd port test : will be removed */
+ if (usbphy_info->regs_base_2nd && usbphy_info->used_phy_port == 1) {
+ linksystem = readl(usbphy_info->regs_base_2nd + EXYNOS_USBCON_LINKSYSTEM);
+ linksystem |= LINKSYSTEM_FORCE_BVALID;
+ linksystem |= LINKSYSTEM_FORCE_VBUSVALID;
+ writel(linksystem, usbphy_info->regs_base_2nd + EXYNOS_USBCON_LINKSYSTEM);
+ }
+#endif
+ phyutmi |= PHYUTMI_VBUSVLDEXTSEL;
+ phyutmi |= PHYUTMI_VBUSVLDEXT;
+ }
+
+ /* disable OTG block and VBUS valid comparator */
+ phyutmi &= ~PHYUTMI_DRVVBUS;
+ phyutmi |= PHYUTMI_OTGDISABLE;
+ writel(phyutmi, regs_base + EXYNOS_USBCON_PHYUTMI);
+
+#if defined(USB_MUX_UTMI_ENABLE)
+ /* UE_TASK: for utmi 2nd port test : will be removed */
+ if (usbphy_info->regs_base_2nd && usbphy_info->used_phy_port == 1)
+ writel(phyutmi, usbphy_info->regs_base_2nd + EXYNOS_USBCON_PHYUTMI);
+#endif
+
+ /* OVC io usage */
+ linkport = readl(regs_base + EXYNOS_USBCON_LINKPORT);
+ if (usbphy_info->use_io_for_ovc) {
+ linkport &= ~LINKPORT_HOST_PORT_OVCR_U3_SEL;
+ linkport &= ~LINKPORT_HOST_PORT_OVCR_U2_SEL;
+ } else {
+ linkport |= LINKPORT_HOST_PORT_OVCR_U3_SEL;
+ linkport |= LINKPORT_HOST_PORT_OVCR_U2_SEL;
+ }
+ writel(linkport, regs_base + EXYNOS_USBCON_LINKPORT);
+
+ if ((EXYNOS_USBCON_VER_02_0_0 <= version)
+ && (version <= EXYNOS_USBCON_VER_02_MAX)) {
+
+ u32 hsphyctrl;
+
+ hsphyctrl = readl(regs_base + EXYNOS_USBCON_HSPHYCTRL);
+ hsphyctrl |= HSPHYCTRL_PHYSWRST;
+ writel(hsphyctrl, regs_base + EXYNOS_USBCON_HSPHYCTRL);
+ udelay(20);
+ hsphyctrl = readl(regs_base + EXYNOS_USBCON_HSPHYCTRL);
+ hsphyctrl &= ~HSPHYCTRL_PHYSWRST;
+ writel(hsphyctrl, regs_base + EXYNOS_USBCON_HSPHYCTRL);
+ }
+}
+
+void samsung_exynos_cal_usb3phy_late_enable(
+ struct exynos_usbphy_info *usbphy_info)
+{
+ u32 version = usbphy_info->version;
+ struct exynos_usbphy_ss_tune *tune = usbphy_info->ss_tune;
+
+ if (EXYNOS_USBCON_VER_01_0_0 <= version
+ && version <= EXYNOS_USBCON_VER_01_MAX) {
+ /* Set RXDET_MEAS_TIME[11:4] each reference clock */
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1010, 0x80);
+ printk("Check CR Port write 0x1010: 0x%x\n",
+ samsung_exynos_cal_cr_read(usbphy_info, 0x1010));
+ if (tune) {
+#if defined(USB_SS_TX_TUNE_PCS)
+ samsung_exynos_cal_cr_write(usbphy_info,
+ 0x1002, 0x4000 |
+ (tune->tx_deemphasis_3p5db << 7) |
+ tune->tx_swing_full);
+#else
+ samsung_exynos_cal_cr_write(usbphy_info, 0x1002, 0x0);
+#endif
+ /* Set RX_SCOPE_LFPS_EN */
+ if (tune->rx_decode_mode) {
+ samsung_exynos_cal_cr_write(usbphy_info,
+ 0x1026, 0x1);
+ }
+ if (tune->set_crport_level_en) {
+ /* Enable override los_bias, los_level and
+ * tx_vboost_lvl, Set los_bias to 0x5 and
+ * los_level to 0x9 */
+ samsung_exynos_cal_cr_write(usbphy_info,
+ 0x15, 0xA409);
+ /* Set TX_VBOOST_LEVLE to default Value (0x4) */
+ samsung_exynos_cal_cr_write(usbphy_info,
+ 0x12, 0x8000);
+ }
+ /* to set the charge pump proportional current */
+ if (tune->set_crport_mpll_charge_pump) {
+ samsung_exynos_cal_cr_write(usbphy_info,
+ 0x30, 0xC0);
+ }
+ if (tune->enable_fixed_rxeq_mode) {
+ samsung_exynos_cal_usb3phy_tune_fix_rxeq(
+ usbphy_info);
+ }
+ set_ss_tx_impedance(usbphy_info);
+ }
+ }
+}
+
+void samsung_exynos_cal_usb3phy_disable(struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 version = usbphy_info->version;
+ u32 phyutmi;
+ u32 phyclkrst;
+
+ phyclkrst = readl(regs_base + EXYNOS_USBCON_PHYCLKRST);
+ phyclkrst |= PHYCLKRST_EN_UTMISUSPEND;
+ phyclkrst |= PHYCLKRST_COMMONONN;
+ phyclkrst |= PHYCLKRST_RETENABLEN;
+ phyclkrst &= ~PHYCLKRST_REF_SSP_EN;
+ phyclkrst &= ~PHYCLKRST_SSC_EN;
+ /* Select Reference clock source path */
+ phyclkrst &= ~PHYCLKRST_REFCLKSEL_MASK;
+ phyclkrst |= PHYCLKRST_REFCLKSEL(usbphy_info->refsel);
+
+ /* Select ref clk */
+ phyclkrst &= ~PHYCLKRST_FSEL_MASK;
+ phyclkrst |= PHYCLKRST_FSEL(usbphy_info->refclk & 0x3f);
+ writel(phyclkrst, regs_base + EXYNOS_USBCON_PHYCLKRST);
+
+ phyutmi = readl(regs_base + EXYNOS_USBCON_PHYUTMI);
+ phyutmi &= ~PHYUTMI_IDPULLUP;
+ phyutmi &= ~PHYUTMI_DRVVBUS;
+ phyutmi |= PHYUTMI_FORCESUSPEND;
+ phyutmi |= PHYUTMI_FORCESLEEP;
+ if (usbphy_info->not_used_vbus_pad) {
+ phyutmi &= ~PHYUTMI_VBUSVLDEXTSEL;
+ phyutmi &= ~PHYUTMI_VBUSVLDEXT;
+ }
+ writel(phyutmi, regs_base + EXYNOS_USBCON_PHYUTMI);
+
+ if ((EXYNOS_USBCON_VER_01_0_0 <= version)
+ && (version <= EXYNOS_USBCON_VER_01_MAX)) {
+ exynos_cal_ss_power_down(usbphy_info, 0);
+ } else if (version >= EXYNOS_USBCON_VER_02_0_0
+ && version <= EXYNOS_USBCON_VER_02_MAX) {
+ exynos_cal_hs_power_down(usbphy_info, 0);
+ }
+
+ /* Clear VBUSVALID signal if VBUS pad is not used */
+ if (usbphy_info->not_used_vbus_pad) {
+ u32 linksystem;
+
+ linksystem = readl(regs_base + EXYNOS_USBCON_LINKSYSTEM);
+ linksystem &= ~LINKSYSTEM_FORCE_BVALID;
+ linksystem &= ~LINKSYSTEM_FORCE_VBUSVALID;
+ writel(linksystem, regs_base + EXYNOS_USBCON_LINKSYSTEM);
+ }
+
+ /* Set force q-channel */
+ if ((version & 0xf) >= 0x01)
+ exynos_cal_usbphy_q_ch(regs_base, 0);
+}
+
+void samsung_exynos_cal_usb3phy_config_host_mode(
+ struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 phyutmi;
+
+ phyutmi = readl(regs_base + EXYNOS_USBCON_PHYUTMI);
+ phyutmi |= PHYUTMI_DMPULLDOWN;
+ phyutmi |= PHYUTMI_DPPULLDOWN;
+ phyutmi &= ~PHYUTMI_VBUSVLDEXTSEL;
+ phyutmi &= ~PHYUTMI_VBUSVLDEXT;
+ writel(phyutmi, regs_base + EXYNOS_USBCON_PHYUTMI);
+}
+
+void samsung_exynos_cal_usb3phy_enable_dp_pullup(
+ struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 phyutmi;
+
+ phyutmi = readl(regs_base + EXYNOS_USBCON_PHYUTMI);
+ phyutmi |= PHYUTMI_VBUSVLDEXT;
+ writel(phyutmi, regs_base + EXYNOS_USBCON_PHYUTMI);
+}
+
+void samsung_exynos_cal_usb3phy_disable_dp_pullup(
+ struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 phyutmi;
+
+ phyutmi = readl(regs_base + EXYNOS_USBCON_PHYUTMI);
+ phyutmi &= ~PHYUTMI_VBUSVLDEXT;
+ writel(phyutmi, regs_base + EXYNOS_USBCON_PHYUTMI);
+}
+
+void samsung_exynos_cal_usb3phy_tune_each(
+ struct exynos_usbphy_info *usbphy_info,
+ enum exynos_usbphy_tune_para para,
+ int val)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 reg;
+
+#if defined(USB_MUX_UTMI_ENABLE)
+ /* UE_TASK: for utmi 2nd port test : will be removed */
+ if (usbphy_info->regs_base_2nd && usbphy_info->used_phy_port == 1)
+ regs_base = usbphy_info->regs_base_2nd;
+#endif
+
+ if (para < 0x10000) {
+ struct exynos_usbphy_hs_tune *tune = usbphy_info->hs_tune;
+
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM0);
+ switch ((int) para) {
+ case USBPHY_TUNE_HS_COMPDIS:
+ reg &= ~PHYPARAM0_COMPDISTUNE_MASK;
+ reg |= PHYPARAM0_COMPDISTUNE(val);
+ if (tune)
+ tune->compdis = val;
+ break;
+ case USBPHY_TUNE_HS_OTG:
+ reg &= ~PHYPARAM0_OTGTUNE_MASK;
+ reg |= PHYPARAM0_OTGTUNE(val);
+ if (tune)
+ tune->otg = val;
+ break;
+ case USBPHY_TUNE_HS_SQRX:
+ reg &= ~PHYPARAM0_SQRXTUNE_MASK;
+ reg |= PHYPARAM0_SQRXTUNE(val);
+ if (tune)
+ tune->rx_sqrx = val;
+ break;
+ case USBPHY_TUNE_HS_TXFSLS:
+ reg &= ~PHYPARAM0_TXFSLSTUNE_MASK;
+ reg |= PHYPARAM0_TXFSLSTUNE(val);
+ if (tune)
+ tune->tx_fsls = val;
+ break;
+ case USBPHY_TUNE_HS_TXHSXV:
+ reg &= ~PHYPARAM0_TXHSXVTUNE_MASK;
+ reg |= PHYPARAM0_TXHSXVTUNE(val);
+ if (tune)
+ tune->tx_hsxv = val;
+ break;
+ case USBPHY_TUNE_HS_TXPREEMP:
+ reg &= ~PHYPARAM0_TXPREEMPAMPTUNE_MASK;
+ reg |= PHYPARAM0_TXPREEMPAMPTUNE(val);
+ if (tune)
+ tune->tx_pre_emp = val;
+ break;
+ case USBPHY_TUNE_HS_TXPREEMP_PLUS:
+ if (val)
+ reg |= PHYPARAM0_TXPREEMPPULSETUNE;
+ else
+ reg &= ~PHYPARAM0_TXPREEMPPULSETUNE;
+ if (tune)
+ tune->tx_pre_emp_plus = val & 0x1;
+ break;
+ case USBPHY_TUNE_HS_TXRES:
+ reg &= ~PHYPARAM0_TXRESTUNE_MASK;
+ reg |= PHYPARAM0_TXRESTUNE(val);
+ if (tune)
+ tune->tx_res = val;
+ break;
+ case USBPHY_TUNE_HS_TXRISE:
+ reg &= ~PHYPARAM0_TXRISETUNE_MASK;
+ reg |= PHYPARAM0_TXRISETUNE(val);
+ if (tune)
+ tune->tx_rise = val;
+ break;
+ case USBPHY_TUNE_HS_TXVREF:
+ reg &= ~PHYPARAM0_TXVREFTUNE_MASK;
+ reg |= PHYPARAM0_TXVREFTUNE(val);
+ if (tune)
+ tune->tx_vref = val;
+ break;
+ }
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM0);
+ } else {
+ struct exynos_usbphy_ss_tune *tune = usbphy_info->ss_tune;
+
+ if (usbphy_info->used_phy_port != -1) {
+ if (usbphy_info->used_phy_port == 0)
+ regs_base = usbphy_info->regs_base;
+ else
+ regs_base = usbphy_info->regs_base_2nd;
+ }
+
+ switch ((int) para) {
+ case USBPHY_TUNE_SS_FIX_EQ:
+ samsung_exynos_cal_usb3phy_tune_adaptive_eq(usbphy_info,
+ val);
+ if (tune)
+ tune->enable_fixed_rxeq_mode = val & 0x1;
+ break;
+ case USBPHY_TUNE_SS_RX_EQ:
+ samsung_exynos_cal_usb3phy_tune_chg_rxeq(usbphy_info,
+ val);
+ if (tune)
+ tune->fix_rxeq_value = val & 0xf;
+ break;
+ case USBPHY_TUNE_SS_TX_BOOST:
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM2);
+ reg &= ~PHYPARAM2_TX_VBOOST_LVL_MASK;
+ reg |= PHYPARAM2_TX_VBOOST_LVL(val);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM2);
+ if (tune)
+ tune->tx_boost_level = val;
+ break;
+ case USBPHY_TUNE_SS_TX_SWING:
+#if !defined(USB_SS_TX_TUNE_PCS)
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM1);
+ reg &= ~PHYPARAM1_PCS_TXSWING_FULL_MASK;
+ reg |= PHYPARAM1_PCS_TXSWING_FULL(val);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM1);
+#else
+ reg = samsung_exynos_cal_cr_read(usbphy_info, 0x1002);
+ reg &= ~0x7f;
+ reg |= val;
+ samsung_exynos_cal_cr_write(usbphy_info,
+ 0x1002, 0x4000 | reg);
+#endif
+ if (tune)
+ tune->tx_swing_full = val;
+ break;
+ case USBPHY_TUNE_SS_TX_DEEMPHASIS:
+#if !defined(USB_SS_TX_TUNE_PCS)
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM1);
+ reg &= ~PHYPARAM1_PCS_TXDEEMPH_3P5DB_MASK;
+ reg |= PHYPARAM1_PCS_TXDEEMPH_3P5DB(val);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM1);
+#else
+ reg = samsung_exynos_cal_cr_read(usbphy_info, 0x1002);
+ reg &= ~(0x3f << 7);
+ reg |= (val & 0x3f) << 7;
+ samsung_exynos_cal_cr_write(usbphy_info,
+ 0x1002, 0x4000 | reg);
+#endif
+ if (tune)
+ tune->tx_deemphasis_3p5db = val;
+ break;
+ }
+
+ }
+}
+
+void samsung_exynos_cal_usb3phy_hs_tune_extract(
+ struct exynos_usbphy_info *usbphy_info)
+{
+ struct exynos_usbphy_hs_tune *hs_tune;
+ u32 reg;
+
+ hs_tune = usbphy_info->hs_tune;
+
+ if (!hs_tune)
+ return;
+
+ reg = readl(usbphy_info->regs_base + EXYNOS_USBCON_PHYPARAM0);
+
+ hs_tune->tx_vref = PHYPARAM0_TXVREFTUNE_EXT(reg);
+ hs_tune->tx_rise = PHYPARAM0_TXRISETUNE_EXT(reg);
+ hs_tune->tx_res = PHYPARAM0_TXRESTUNE_EXT(reg);
+ hs_tune->tx_pre_emp_plus = PHYPARAM0_TXPREEMPPULSETUNE_EXT(reg);
+ hs_tune->tx_pre_emp = PHYPARAM0_TXPREEMPAMPTUNE_EXT(reg);
+ hs_tune->tx_hsxv = PHYPARAM0_TXHSXVTUNE_EXT(reg);
+ hs_tune->tx_fsls = PHYPARAM0_TXFSLSTUNE_EXT(reg);
+ hs_tune->rx_sqrx = PHYPARAM0_SQRXTUNE_EXT(reg);
+ hs_tune->otg = PHYPARAM0_OTGTUNE_EXT(reg);
+ hs_tune->compdis = PHYPARAM0_COMPDISTUNE_EXT(reg);
+}
+
+void samsung_exynos_cal_usb3phy_tune_dev(struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 reg;
+
+#if defined(USB_MUX_UTMI_ENABLE)
+ /* UE_TASK: for utmi 2nd port test : will be removed */
+ if (usbphy_info->regs_base_2nd && usbphy_info->used_phy_port == 1)
+ regs_base = usbphy_info->regs_base_2nd;
+#endif
+
+ /* Set the LINK Version Control and Frame Adjust Value */
+ reg = readl(regs_base + EXYNOS_USBCON_LINKSYSTEM);
+ reg &= ~LINKSYSTEM_FLADJ_MASK;
+ reg |= LINKSYSTEM_FLADJ(0x20);
+ reg |= LINKSYSTEM_XHCI_VERSION_CONTROL;
+ writel(reg, regs_base + EXYNOS_USBCON_LINKSYSTEM);
+
+ /* Tuning the HS Block of phy */
+ if (usbphy_info->hs_tune) {
+ struct exynos_usbphy_hs_tune *tune = usbphy_info->hs_tune;
+
+ /* Set tune value for 2.0(HS/FS) function */
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM0);
+ /* TX VREF TUNE */
+ reg &= ~PHYPARAM0_TXVREFTUNE_MASK;
+ reg |= PHYPARAM0_TXVREFTUNE(tune->tx_vref);
+ /* TX RISE TUNE */
+ reg &= ~PHYPARAM0_TXRISETUNE_MASK;
+ reg |= PHYPARAM0_TXRISETUNE(tune->tx_rise);
+ /* TX RES TUNE */
+ reg &= ~PHYPARAM0_TXRESTUNE_MASK;
+ reg |= PHYPARAM0_TXRESTUNE(tune->tx_res);
+ /* TX PRE EMPHASIS PLUS */
+ if (tune->tx_pre_emp_plus)
+ reg |= PHYPARAM0_TXPREEMPPULSETUNE;
+ else
+ reg &= ~PHYPARAM0_TXPREEMPPULSETUNE;
+ /* TX PRE EMPHASIS */
+ reg &= ~PHYPARAM0_TXPREEMPAMPTUNE_MASK;
+ reg |= PHYPARAM0_TXPREEMPAMPTUNE(tune->tx_pre_emp);
+ /* TX HS XV TUNE */
+ reg &= ~PHYPARAM0_TXHSXVTUNE_MASK;
+ reg |= PHYPARAM0_TXHSXVTUNE(tune->tx_hsxv);
+ /* TX FSLS TUNE */
+ reg &= ~PHYPARAM0_TXFSLSTUNE_MASK;
+ reg |= PHYPARAM0_TXFSLSTUNE(tune->tx_fsls);
+ /* RX SQ TUNE */
+ reg &= ~PHYPARAM0_SQRXTUNE_MASK;
+ reg |= PHYPARAM0_SQRXTUNE(tune->rx_sqrx);
+ /* OTG TUNE */
+ reg &= ~PHYPARAM0_OTGTUNE_MASK;
+ reg |= PHYPARAM0_OTGTUNE(tune->otg);
+ /* COM DIS TUNE */
+ reg &= ~PHYPARAM0_COMPDISTUNE_MASK;
+ reg |= PHYPARAM0_COMPDISTUNE(tune->compdis);
+
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM0);
+ }
+
+ /* Tuning the SS Block of phy */
+ if (usbphy_info->ss_tune) {
+ struct exynos_usbphy_ss_tune *tune = usbphy_info->ss_tune;
+
+ if (usbphy_info->used_phy_port != -1) {
+ if (usbphy_info->used_phy_port == 0)
+ regs_base = usbphy_info->regs_base;
+ else
+ regs_base = usbphy_info->regs_base_2nd;
+ }
+#if !defined(USB_SS_TX_TUNE_PCS)
+ /* Set the PHY Signal Quality Tuning Value */
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM1);
+ /* TX SWING FULL */
+ reg &= ~PHYPARAM1_PCS_TXSWING_FULL_MASK;
+ reg |= PHYPARAM1_PCS_TXSWING_FULL(tune->tx_swing_full);
+ /* TX DE EMPHASIS 3.5 dB */
+ reg &= ~PHYPARAM1_PCS_TXDEEMPH_3P5DB_MASK;
+ reg |= PHYPARAM1_PCS_TXDEEMPH_3P5DB(
+ tune->tx_deemphasis_3p5db);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM1);
+#endif
+
+ /* Set vboost value for eye diagram */
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM2);
+ /* TX VBOOST Value */
+ reg &= ~PHYPARAM2_TX_VBOOST_LVL_MASK;
+ reg |= PHYPARAM2_TX_VBOOST_LVL(tune->tx_boost_level);
+ /* LOS BIAS */
+ reg &= ~PHYPARAM2_LOS_BIAS_MASK;
+ reg |= PHYPARAM2_LOS_BIAS(tune->los_bias);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM2);
+
+ /*
+ * Set pcs_rx_los_mask_val for 14nm PHY to mask the abnormal
+ * LFPS and glitches
+ */
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPCSVAL);
+ reg &= ~PHYPCSVAL_PCS_RX_LOS_MASK_VAL_MASK;
+ reg |= PHYPCSVAL_PCS_RX_LOS_MASK_VAL(tune->los_mask_val);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPCSVAL);
+ }
+}
+
+void samsung_exynos_cal_usb3phy_tune_host(
+ struct exynos_usbphy_info *usbphy_info)
+{
+ void __iomem *regs_base = usbphy_info->regs_base;
+ u32 reg;
+
+#if defined(USB_MUX_UTMI_ENABLE)
+ /* UE_TASK: for utmi 2nd port test : will be removed */
+ if (usbphy_info->regs_base_2nd && usbphy_info->used_phy_port == 1)
+ regs_base = usbphy_info->regs_base_2nd;
+#endif
+
+ /* Set the LINK Version Control and Frame Adjust Value */
+ reg = readl(regs_base + EXYNOS_USBCON_LINKSYSTEM);
+ reg &= ~LINKSYSTEM_FLADJ_MASK;
+ reg |= LINKSYSTEM_FLADJ(0x20);
+ reg |= LINKSYSTEM_XHCI_VERSION_CONTROL;
+ writel(reg, regs_base + EXYNOS_USBCON_LINKSYSTEM);
+
+ /* Tuning the HS Block of phy */
+ if (usbphy_info->hs_tune) {
+ struct exynos_usbphy_hs_tune *tune = usbphy_info->hs_tune;
+
+ /* Set tune value for 2.0(HS/FS) function */
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM0);
+ /* TX VREF TUNE */
+ reg &= ~PHYPARAM0_TXVREFTUNE_MASK;
+ reg |= PHYPARAM0_TXVREFTUNE(tune->tx_vref);
+ /* TX RISE TUNE */
+ reg &= ~PHYPARAM0_TXRISETUNE_MASK;
+ reg |= PHYPARAM0_TXRISETUNE(tune->tx_rise);
+ /* TX RES TUNE */
+ reg &= ~PHYPARAM0_TXRESTUNE_MASK;
+ reg |= PHYPARAM0_TXRESTUNE(tune->tx_res);
+ /* TX PRE EMPHASIS PLUS */
+ if (tune->tx_pre_emp_plus)
+ reg |= PHYPARAM0_TXPREEMPPULSETUNE;
+ else
+ reg &= ~PHYPARAM0_TXPREEMPPULSETUNE;
+ /* TX PRE EMPHASIS */
+ reg &= ~PHYPARAM0_TXPREEMPAMPTUNE_MASK;
+ reg |= PHYPARAM0_TXPREEMPAMPTUNE(tune->tx_pre_emp);
+ /* TX HS XV TUNE */
+ reg &= ~PHYPARAM0_TXHSXVTUNE_MASK;
+ reg |= PHYPARAM0_TXHSXVTUNE(tune->tx_hsxv);
+ /* TX FSLS TUNE */
+ reg &= ~PHYPARAM0_TXFSLSTUNE_MASK;
+ reg |= PHYPARAM0_TXFSLSTUNE(tune->tx_fsls);
+ /* RX SQ TUNE */
+ reg &= ~PHYPARAM0_SQRXTUNE_MASK;
+ reg |= PHYPARAM0_SQRXTUNE(tune->rx_sqrx);
+ /* OTG TUNE */
+ reg &= ~PHYPARAM0_OTGTUNE_MASK;
+ reg |= PHYPARAM0_OTGTUNE(tune->otg);
+ /* COM DIS TUNE */
+ reg &= ~PHYPARAM0_COMPDISTUNE_MASK;
+ reg |= PHYPARAM0_COMPDISTUNE(tune->compdis);
+
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM0);
+ }
+
+ /* Tuning the SS Block of phy */
+ if (usbphy_info->ss_tune) {
+ struct exynos_usbphy_ss_tune *tune = usbphy_info->ss_tune;
+
+ if (usbphy_info->used_phy_port != -1) {
+ if (usbphy_info->used_phy_port == 0)
+ regs_base = usbphy_info->regs_base;
+ else
+ regs_base = usbphy_info->regs_base_2nd;
+ }
+#if !defined(USB_SS_TX_TUNE_PCS)
+ /* Set the PHY Signal Quality Tuning Value */
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM1);
+ /* TX SWING FULL */
+ reg &= ~PHYPARAM1_PCS_TXSWING_FULL_MASK;
+ reg |= PHYPARAM1_PCS_TXSWING_FULL(tune->tx_swing_full);
+ /* TX DE EMPHASIS 3.5 dB */
+ reg &= ~PHYPARAM1_PCS_TXDEEMPH_3P5DB_MASK;
+ reg |= PHYPARAM1_PCS_TXDEEMPH_3P5DB(
+ tune->tx_deemphasis_3p5db);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM1);
+#endif
+
+ /* Set vboost value for eye diagram */
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPARAM2);
+ /* TX VBOOST Value */
+ reg &= ~PHYPARAM2_TX_VBOOST_LVL_MASK;
+ reg |= PHYPARAM2_TX_VBOOST_LVL(tune->tx_boost_level);
+ /* LOS BIAS */
+ reg &= ~PHYPARAM2_LOS_BIAS_MASK;
+ reg |= PHYPARAM2_LOS_BIAS(tune->los_bias);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPARAM2);
+
+ /*
+ * Set pcs_rx_los_mask_val for 14nm PHY to mask the abnormal
+ * LFPS and glitches
+ */
+ reg = readl(regs_base + EXYNOS_USBCON_PHYPCSVAL);
+ reg &= ~PHYPCSVAL_PCS_RX_LOS_MASK_VAL_MASK;
+ reg |= PHYPCSVAL_PCS_RX_LOS_MASK_VAL(tune->los_mask_val);
+ writel(reg, regs_base + EXYNOS_USBCON_PHYPCSVAL);
+ }
+}
--- /dev/null
+#ifndef __PHY_SAMSUNG_USB3_FW_CAL_H__
+#define __PHY_SAMSUNG_USB3_FW_CAL_H__
+
+#define EXYNOS_USBCON_LINKSYSTEM (0x04)
+#define LINKSYSTEM_HOST_SYSTEM_ERR (0x1 << 31)
+#define LINKSYSTEM_PHY_POWER_DOWN (0x1 << 30)
+#define LINKSYSTEM_PHY_SW_RESET (0x1 << 29)
+#define LINKSYSTEM_LINK_SW_RESET (0x1 << 28)
+#define LINKSYSTEM_XHCI_VERSION_CONTROL (0x1 << 27)
+#define LINKSYSTEM_FLADJ_MASK (0x3f << 1)
+#define LINKSYSTEM_FLADJ(_x) ((_x) << 1)
+#define LINKSYSTEM_FORCE_BVALID (0x1 << 7)
+#define LINKSYSTEM_FORCE_VBUSVALID (0x1 << 8)
+
+#define EXYNOS_USBCON_PHYUTMI (0x08)
+#define PHYUTMI_UTMI_SUSPEND_COM_N (0x1 << 12)
+#define PHYUTMI_UTMI_L1_SUSPEND_COM_N (0x1 << 11)
+#define PHYUTMI_VBUSVLDEXTSEL (0x1 << 10)
+#define PHYUTMI_VBUSVLDEXT (0x1 << 9)
+#define PHYUTMI_TXBITSTUFFENH (0x1 << 8)
+#define PHYUTMI_TXBITSTUFFEN (0x1 << 7)
+#define PHYUTMI_OTGDISABLE (0x1 << 6)
+#define PHYUTMI_OTGDISABLE (0x1 << 6)
+#define PHYUTMI_IDPULLUP (0x1 << 5)
+#define PHYUTMI_DRVVBUS (0x1 << 4)
+#define PHYUTMI_DPPULLDOWN (0x1 << 3)
+#define PHYUTMI_DMPULLDOWN (0x1 << 2)
+#define PHYUTMI_FORCESUSPEND (0x1 << 1)
+#define PHYUTMI_FORCESLEEP (0x1 << 0)
+
+#define EXYNOS_USBCON_PHYCLKRST (0x10)
+#define PHYCLKRST_EN_UTMISUSPEND (0x1 << 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 (0x1 << 20)
+#define PHYCLKRST_REF_SSP_EN (0x1 << 19)
+#define PHYCLKRST_REF_CLKDIV2 (0x1 << 18)
+#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11)
+#define PHYCLKRST_MPLL_MULTIPLIER(_x) ((_x) << 11)
+#define PHYCLKRST_FSEL_MASK (0x3f << 5)
+#define PHYCLKRST_FSEL(_x) ((_x) << 5)
+#define PHYCLKRST_RETENABLEN (0x1 << 4)
+#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2)
+#define PHYCLKRST_REFCLKSEL(_x) ((_x) << 2)
+#define PHYCLKRST_PORTRESET (0x1 << 1)
+#define PHYCLKRST_COMMONONN (0x1 << 0)
+
+#define EXYNOS_USBCON_PHYPIPE (0x0c)
+#define PHYPIPE_PHY_CLOCK_SEL (0x1 << 4)
+
+#define EXYNOS_USBCON_PHYREG0 (0x14)
+#define PHYREG0_SSC_REFCLKSEL_MASK (0x1ff << 23)
+#define PHYREG0_SSC_REFCLKSEL(_x) ((_x) << 23)
+#define PHYREG0_SSC_RANGE_MASK (0x7 << 20)
+#define PHYREG0_SSC_RAMGE(_x) ((_x) << 20)
+#define PHYREG0_CR_WRITE (1 << 19)
+#define PHYREG0_CR_READ (1 << 18)
+#define PHYREG0_CR_DATA_MASK (0xffff << 2)
+#define PHYREG0_CR_DATA_IN(_x) ((_x) << 2)
+#define PHYREG0_CR_CR_CAP_DATA (1 << 1)
+#define PHYREG0_CR_CR_CAP_ADDR (1 << 0)
+
+#define EXYNOS_USBCON_PHYREG1 (0x18)
+#define PHYREG1_CR_DATA_OUT_MASK (0xffff << 1)
+#define PHYREG1_CR_DATA_OUT(_x) ((_x) << 1)
+#define PHYREG1_CR_ACK (1 << 0)
+
+#define EXYNOS_USBCON_PHYPARAM0 (0x1c)
+#define PHYPARAM0_REF_USE_PAD (0x1 << 31)
+#define PHYPARAM0_VDATAREFTUNE_MASK (0x3 << 26)
+#define PHYPARAM0_VDATAREFTUNE(_x) ((_x) << 26)
+#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26)
+#define PHYPARAM0_REF_LOSLEVEL_EXT(_x) ((_x & (0x1f << 26)) >> 26)
+#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26)
+#define PHYPARAM0_TXVREFTUNE_MASK (0xf << 22)
+#define PHYPARAM0_TXVREFTUNE_EXT(_x) ((_x & (0xf << 22)) >> 22)
+#define PHYPARAM0_TXVREFTUNE(_x) ((_x) << 22)
+#define PHYPARAM0_TXRISETUNE_MASK (0x3 << 20)
+#define PHYPARAM0_TXRISETUNE_EXT(_x) ((_x & (0x3 << 20)) >> 20)
+#define PHYPARAM0_TXRISETUNE(_x) ((_x) << 20)
+#define PHYPARAM0_TXRESTUNE_MASK (0x3 << 18)
+#define PHYPARAM0_TXRESTUNE_EXT(_x) ((_x & (0x3 << 18)) >> 18)
+#define PHYPARAM0_TXRESTUNE(_x) ((_x) << 18)
+#define PHYPARAM0_TXPREEMPPULSETUNE (0x1 << 17)
+#define PHYPARAM0_TXPREEMPPULSETUNE_EXT(_x) ((_x & (0x1 << 17)) >> 17)
+#define PHYPARAM0_TXPREEMPAMPTUNE_MASK (0x3 << 15)
+#define PHYPARAM0_TXPREEMPAMPTUNE_EXT(_x) ((_x & (0x3 << 15)) >> 15)
+#define PHYPARAM0_TXPREEMPAMPTUNE(_x) ((_x) << 15)
+#define PHYPARAM0_TXHSXVTUNE_MASK (0x3 << 13)
+#define PHYPARAM0_TXHSXVTUNE_EXT(_x) ((_x & (0x3 << 13)) >> 13)
+#define PHYPARAM0_TXHSXVTUNE(_x) ((_x) << 13)
+#define PHYPARAM0_TXFSLSTUNE_MASK (0xf << 9)
+#define PHYPARAM0_TXFSLSTUNE_EXT(_x) ((_x & (0xf << 9)) >> 9)
+#define PHYPARAM0_TXFSLSTUNE(_x) ((_x) << 9)
+#define PHYPARAM0_SQRXTUNE_MASK (0x7 << 6)
+#define PHYPARAM0_SQRXTUNE_EXT(_x) ((_x & (0x7 << 6)) >> 6)
+#define PHYPARAM0_SQRXTUNE(_x) ((_x) << 6)
+#define PHYPARAM0_OTGTUNE_MASK (0x7 << 3)
+#define PHYPARAM0_OTGTUNE_EXT(_x) ((_x & (0x7 << 3)) >> 3)
+#define PHYPARAM0_OTGTUNE(_x) ((_x) << 3)
+#define PHYPARAM0_COMPDISTUNE_MASK (0x7 << 0)
+#define PHYPARAM0_COMPDISTUNE_EXT(_x) (_x & (0x7 << 0))
+#define PHYPARAM0_COMPDISTUNE(_x) ((_x) << 0)
+
+#define EXYNOS_USBCON_PHYPARAM1 (0x20)
+#define PHYPARAM1_TX0_TERM_OFFSET_MASK (0x1f << 26)
+#define PHYPARAM1_TX0_TERM_OFFSET(_x) ((_x) << 26)
+#define PHYPARAM1_PCS_TXSWING_FULL_MASK (0x7f << 12)
+#define PHYPARAM1_PCS_TXSWING_FULL(_x) ((_x) << 12)
+#define PHYPARAM1_PCS_TXDEEMPH_3P5DB_MASK (0x3f << 0)
+#define PHYPARAM1_PCS_TXDEEMPH_3P5DB(_x) ((_x) << 0)
+
+#define EXYNOS_USBCON_PHYTEST (0x28)
+#define PHYTEST_POWERDOWN_SSP (0x1 << 3)
+#define PHYTEST_POWERDOWN_HSP (0x1 << 2)
+
+#define EXYNOS_USBCON_PHYPOWERDOWN (0x28)
+#define PHYPOWERDOWN_VATESTENB (0x1 << 6)
+#define PHYPOWERDOWN_TEST_BURNIN_MASK (0x3 << 4)
+#define PHYPOWERDOWN_TEST_BURNIN ((_x) << 4)
+
+#define EXYNOS_USBCON_PHYBATCHG (0x30)
+#define PHYBATCHG_CHGDET (0x1 << 10)
+#define PHYBATCHG_RIDC (0x1 << 9)
+#define PHYBATCHG_RIDB (0x1 << 8)
+#define PHYBATCHG_RIDA (0x1 << 7)
+#define PHYBATCHG_RIDGND (0x1 << 6)
+#define PHYBATCHG_RIDFLOAT (0x1 << 5)
+#define PHYBATCHG_VDATSRCENB (0x1 << 4)
+#define PHYBATCHG_VDATDETENB (0x1 << 3)
+#define PHYBATCHG_CHRGSEL (0x1 << 2)
+#define PHYBATCHG_ACAENB (0x1 << 1)
+#define PHYBATCHG_DCDENB (0x1 << 0)
+
+#define EXYNOS_USBCON_PHYRESUME (0x34)
+#define PHYRESUME_DIS_BUSPEND_QACT (0x1 << 14)
+#define PHYRESUME_DIS_LINKGATE_QACT (0x1 << 13)
+#define PHYRESUME_DIS_ID0_QACT (0x1 << 12)
+#define PHYRESUME_DIS_VBUSVALID_QACT (0x1 << 11)
+#define PHYRESUME_DIS_BVALID_QACT (0x1 << 10)
+#define PHYRESUME_FORCE_QACT (0x1 << 9)
+#define PHYRESUME_FORCE_OPMODE_MASK (0x3 << 7)
+#define PHYRESUME_FORCE_OPMODE(_x) ((_x) << 7)
+#define PHYRESUME_OPMODE_EN (0x1 << 6)
+#define PHYRESUME_AUTORESUME_EN (0x1 << 5)
+#define PHYRESUME_BYPASS_SEL (0x1 << 4)
+#define PHYRESUME_BYPASS_DM_EN (0x1 << 3)
+#define PHYRESUME_BYPASS_DP_EN (0x1 << 2)
+#define PHYRESUME_BYPASS_DM_DATA (0x1 << 1)
+#define PHYRESUME_BYPASS_DP_DATA (0x1 << 0)
+
+#define EXYNOS_USBCON_PHYPCSVAL (0x3C)
+#define PHYPCSVAL_PCS_RX_LOS_MASK_VAL_MASK (0x3FF << 0)
+#define PHYPCSVAL_PCS_RX_LOS_MASK_VAL(_x) ((_x & 0x3FF) << 0)
+
+#define EXYNOS_USBCON_LINKPORT (0x44)
+#define LINKPORT_HOST_NUM_U3_PORT_MASK (0xf << 13)
+#define LINKPORT_HOST_NUM_U3_PORT(_x) ((_x) << 13)
+#define LINKPORT_HOST_NUM_U2_PORT_MASK (0xf << 9)
+#define LINKPORT_HOST_NUM_U2_PORT(_x) ((_x) << 9)
+#define LINKPORT_HOST_U3_PORT_DISABLE (1 << 8)
+#define LINKPORT_HOST_U2_PORT_DISABLE (1 << 7)
+#define LINKPORT_PORT_POWER_CONTROL (0x1 << 6)
+#define LINKPORT_HOST_PORT_OVCR_U3 (1 << 5)
+#define LINKPORT_HOST_PORT_OVCR_U2 (1 << 4)
+#define LINKPORT_HOST_PORT_OVCR_U3_SEL (1 << 3)
+#define LINKPORT_HOST_PORT_OVCR_U2_SEL (1 << 2)
+#define LINKPORT_PERM_ATTACH_U3 (0x << 1)
+#define LINKPORT_PERM_ATTACH_U2 (0x1 << 0)
+
+#define EXYNOS_USBCON_PHYPARAM2 (0x50)
+#define PHYPARAM2_TX_VBOOST_LVL_MASK (0x7 << 4)
+#define PHYPARAM2_TX_VBOOST_LVL(_x) ((_x) << 4)
+#define PHYPARAM2_LOS_BIAS_MASK (0x7 << 0)
+#define PHYPARAM2_LOS_BIAS(_x) ((_x) << 0)
+
+#define EXYNOS_USBCON_HSPHYCTRL (0x54)
+#define HSPHYCTRL_PHYSWRSTALL (0x1 << 31)
+#define HSPHYCTRL_SIDDQ (0x1 << 6)
+#define HSPHYCTRL_PHYSWRST (0x1 << 0)
+
+#define EXYNOS_USBCON_HSPHYTESTIF (0x5c)
+#define HSPHYTESTIF_LINESTATE_MASK (0x3 << 20)
+#define HSPHYTESTIF_DATAOUT_MASK (0xf << 16)
+#define HSPHYTESTIF_CLKEN (0x1 << 13)
+#define HSPHYTESTIF_DATAOUTSEL (0x2 << 12)
+#define HSPHYTESTIF_ADDR_MASK (0xf << 8)
+#define HSPHYTESTIF_ADDR(_x) ((_x) << 8)
+#define HSPHYTESTIF_DATAIN_MASK (0xff << 0)
+#define HSPHYTESTIF_DATAIN(_x) ((_x) << 0)
+
+#define EXYNOS_USBCON_PHYSELECTION (0x6c)
+#define PHYSEL_U3RST (0x1 << 5)
+#define PHYSEL_UTMI_CLK (0x1 << 4)
+#define PHYSEL_PIPE_CLK (0x1 << 3)
+#define PHYSEL_UTMI (0x1 << 2)
+#define PHYSEL_PIPE (0x1 << 1)
+#define PHYSEL_SIDEBAND (0x1 << 0)
+
+#define EXYNOS_USBCON_HSPHYPLLTUNE (0x70)
+#define HSPHYPLLTUNE_PLL_B_TUNE (0x1 << 6)
+#define HSPHYPLLTUNE_PLL_I_TUNE(_x) ((_x) << 4)
+#define HSPHYPLLTUNE_PLL_I_TUNE_MASK (0x3 << 4)
+#define HSPHYPLLTUNE_PLL_P_TUNE(_x) ((_x) << 0)
+#define HSPHYPLLTUNE_PLL_P_TUNE_MASK (0xf << 0)
+
+#define EXYNOS_USBCON_EHCICTRL (0x80)
+#define EHCICTRL_EHCI64BITEN (0x1 << 31)
+#define EHCICTRL_EN_INCRX_ALIGN (0x1 << 30)
+#define EHCICTRL_EN_INCR4 (0x1 << 29)
+#define EHCICTRL_EN_INCR8 (0x1 << 28)
+#define EHCICTRL_EN_INCR16 (0x1 << 26)
+#define EHCICTRL_EN_AUTO_PPDON_OVRCUR (0x1 << 25)
+#define EHCICTRL_FLADJVAL0_MASK (0x3f << 19)
+#define EHCICTRL_FLADJVAL0(_x) ((_x) << 19)
+#define EHCICTRL_FLADJVAL1_MASK (0x3f << 13)
+#define EHCICTRL_FLADJVAL1(_x) ((_x) << 13)
+#define EHCICTRL_FLADJVAL2_MASK (0x3f << 7)
+#define EHCICTRL_FLADJVAL2(_x) ((_x) << 7)
+#define EHCICTRL_FLADJVALHOST_MASK (0x3f << 1)
+#define EHCICTRL_FLADJVALHOST(_x) ((_x) << 1)
+
+#define EXYNOS_USBCON_OHCICTRL (0x84)
+#define OHCICTRL_RESET_PORT0 (0x1 << 29)
+#define OHCICTRL_HUBSETUP_MIN (0x1 << 4)
+#define OHCICTRL_OHCISUSPLGCY (0x1 << 3)
+#define OHCICTRL_APPSTARTCLK (0x1 << 2)
+#define OHCICTRL_OHCICNTSELN (0x1 << 1)
+#define OHCICTRL_OHCICLKCKTRSTN (0x1 << 0)
+
+#define EXYNOS_USBCON_HOSTLINKCTRL (0x88)
+#define HOSTLINKCTRL_EN_SLEEP_HOST_P2 (0x1 << 31)
+#define HOSTLINKCTRL_EN_SLEEP_HOST_P1 (0x1 << 30)
+#define HOSTLINKCTRL_EN_SLEEP_HOST_P0 (0x1 << 29)
+#define HOSTLINKCTRL_EN_SLEEP_OTG (0x1 << 28)
+#define HOSTLINKCTRL_EN_SUSPEND_HOST_P2 (0x1 << 27)
+#define HOSTLINKCTRL_EN_SUSPEND_HOST_P1 (0x1 << 26)
+#define HOSTLINKCTRL_EN_SUSPEND_HOST_P0 (0x1 << 25)
+#define HOSTLINKCTRL_EN_SUSPEND_OTG (0x1 << 24)
+#define HOSTLINKCTRL_FORCE_HOST_OVRCUR_P2 (0x1 << 17)
+#define HOSTLINKCTRL_FORCE_HOST_OVRCUR (0x1 << 16)
+#define HOSTLINKCTRL_SW_RESET_PORT2 (0x1 << 3)
+#define HOSTLINKCTRL_SW_RESET_PORT1 (0x1 << 2)
+#define HOSTLINKCTRL_SW_RESET_PORT0 (0x1 << 1)
+#define HOSTLINKCTRL_LINKSWRST (0x1 << 0)
+
+#define EXYNOS_USBCON_OTGLINKCTRL (0x8C)
+#define OTGINKCTRL_AVALID (0x1 << 14)
+#define OTGINKCTRL_BVALID (0x1 << 13)
+#define OTGINKCTRL_IDDIG (0x1 << 12)
+#define OTGINKCTRL_VBUSDETECT (0x1 << 11)
+#define OTGINKCTRL_VBUSVLDSEL_MASK (0x3 << 9)
+#define OTGINKCTRL_VBUSVLDSEL(_x) ((_x) << 9)
+#define OTGINKCTRL_LINK_PRST (0x1 << 4)
+#define OTGINKCTRL_SW_RESET_ALL (0x1 << 3)
+#define OTGINKCTRL_SW_RESET (0x1 << 2)
+
+void samsung_exynos_cal_usb3phy_enable(struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_usb3phy_late_enable(struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_usb3phy_disable(struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_usb3phy_tune_each(struct exynos_usbphy_info *usbphy_info,
+ enum exynos_usbphy_tune_para para, int val);
+void samsung_exynos_cal_usb3phy_hs_tune_extract(struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_usb3phy_tune_dev(struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_usb3phy_tune_host(struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_cr_write(struct exynos_usbphy_info *usbphy_info, u16 addr, u16 data);
+u16 samsung_exynos_cal_cr_read(struct exynos_usbphy_info *usbphy_info, u16 addr);
+void samsung_cal_usb3phy_tune(struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_usb3phy_config_host_mode(
+ struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_usb3phy_enable_dp_pullup(
+ struct exynos_usbphy_info *usbphy_info);
+void samsung_exynos_cal_usb3phy_disable_dp_pullup(
+ struct exynos_usbphy_info *usbphy_info);
+
+#endif
This driver provides PHY support for that phy which part for the
AM335x SoC.
+config DUAL_ROLE_USB_INTF
+ bool "Generic DUAL ROLE sysfs interface"
+ help
+ A generic sysfs interface to track and change the state of
+ dual role usb phys. The usb phy drivers can register to
+ this interface to expose it capabilities to the userspace
+ and thereby allowing userspace to change the port mode.
+
config TWL6030_USB
tristate "TWL6030 USB Transceiver Driver"
depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS
struct phy_ops {
int (*init)(struct phy *phy);
int (*exit)(struct phy *phy);
+ int (*tune)(struct phy *phy, int phy_state);
+ int (*vendor_set)(struct phy *phy, int is_enable, int is_cancel);
+ void (*conn)(struct phy *phy, int is_conn);
+ int (*ilbk)(struct phy *phy);
+ int (*set)(struct phy *phy, int option, void *info);
int (*power_on)(struct phy *phy);
int (*power_off)(struct phy *phy);
int (*set_mode)(struct phy *phy, enum phy_mode mode);
void phy_pm_runtime_forbid(struct phy *phy);
int phy_init(struct phy *phy);
int phy_exit(struct phy *phy);
+int phy_tune(struct phy *phy, int phy_state);
+int phy_vendor_set(struct phy *phy, int is_enable, int is_cancel);
+void phy_conn(struct phy *phy, int is_conn);
+int phy_ilbk(struct phy *phy);
+int phy_set(struct phy *phy, int option, void *info);
int phy_power_on(struct phy *phy);
int phy_power_off(struct phy *phy);
int phy_set_mode(struct phy *phy, enum phy_mode mode);
return -ENOSYS;
}
+static inline int phy_tune(struct phy *phy, int phy_state)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
+static inline int phy_vendor_set(struct phy *phy, int is_enable, int is_rewa)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
+static inline int phy_conn(struct phy *phy, int is_conn)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
+static inline int phy_ilbk(struct phy *phy)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
+static inline int phy_set(struct phy *phy, int option, void *info)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
static inline int phy_power_on(struct phy *phy)
{
if (!phy)