From: Kisang Lee Date: Thu, 17 May 2018 11:55:47 +0000 (+0900) Subject: [9610] phy: usb: add PHY driver for USBDRD3 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=56cd25b484fe24e2661f2d31e069c70f15b5b714;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [9610] phy: usb: add PHY driver for USBDRD3 Change-Id: I7d79fa369409f6cb3c51e21a16ec9a385f97d7e7 Signed-off-by: Kisang Lee --- diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 48a365e303e5..ce50fa87cd99 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -273,6 +273,83 @@ out: } 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; diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index b9651fc0308e..b2af8eb12621 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -79,6 +79,62 @@ config PHY_EXYNOS5_USBDRD 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 @@ -95,10 +151,19 @@ config PHY_EXYNOS5250_SATA 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. diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile index 068a92999b43..75a7418d5b11 100644 --- a/drivers/phy/samsung/Makefile +++ b/drivers/phy/samsung/Makefile @@ -1,7 +1,8 @@ # 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 @@ -10,4 +11,10 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o +obj-$(CONFIG_PHY_EXYNOS_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 diff --git a/drivers/phy/samsung/phy-exynos-debug.h b/drivers/phy/samsung/phy-exynos-debug.h new file mode 100644 index 000000000000..0a5a70f8d015 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-debug.h @@ -0,0 +1,93 @@ +/** + * 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 + * + * 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; +}; diff --git a/drivers/phy/samsung/phy-exynos-debugfs.c b/drivers/phy/samsung/phy-exynos-debugfs.c new file mode 100644 index 000000000000..985aecd1fbd7 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-debugfs.c @@ -0,0 +1,816 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "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; +} diff --git a/drivers/phy/samsung/phy-exynos-displayport.c b/drivers/phy/samsung/phy-exynos-displayport.c new file mode 100644 index 000000000000..57d9ef546ba1 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-displayport.c @@ -0,0 +1,175 @@ +/* + * Samsung EXYNOS SoC series MIPI DISPLAYPORT PHY driver + * + * Copyright (C) 2016 Samsung Electronics Co., Ltd. + * Author: Kwangje Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define 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"); diff --git a/drivers/phy/samsung/phy-exynos-dp-debugfs.c b/drivers/phy/samsung/phy-exynos-dp-debugfs.c new file mode 100644 index 000000000000..78b66b5f74c1 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-dp-debugfs.c @@ -0,0 +1,505 @@ +/* + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "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; +} diff --git a/drivers/phy/samsung/phy-exynos-mipi.c b/drivers/phy/samsung/phy-exynos-mipi.c new file mode 100644 index 000000000000..0173ca2a589d --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-mipi.c @@ -0,0 +1,680 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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"); diff --git a/drivers/phy/samsung/phy-exynos-usb3p1-reg.h b/drivers/phy/samsung/phy-exynos-usb3p1-reg.h new file mode 100644 index 000000000000..f8374fdf70b3 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usb3p1-reg.h @@ -0,0 +1,295 @@ +/* + * 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_ */ diff --git a/drivers/phy/samsung/phy-exynos-usb3p1.c b/drivers/phy/samsung/phy-exynos-usb3p1.c new file mode 100644 index 000000000000..aff021b6138e --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usb3p1.c @@ -0,0 +1,1682 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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 +#include +#endif + +#include + +#else + +#include "types.h" +#ifndef __BOOT__ +#include "customfunctions.h" +#include "mct.h" +#else +#include +#include +#include +#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; +} diff --git a/drivers/phy/samsung/phy-exynos-usb3p1.h b/drivers/phy/samsung/phy-exynos-usb3p1.h new file mode 100644 index 000000000000..bb6c5837a611 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usb3p1.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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_ */ diff --git a/drivers/phy/samsung/phy-exynos-usbdp-reg-cmn.h b/drivers/phy/samsung/phy-exynos-usbdp-reg-cmn.h new file mode 100644 index 000000000000..1aa9dadb3f86 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdp-reg-cmn.h @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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_ */ diff --git a/drivers/phy/samsung/phy-exynos-usbdp-reg-dp.h b/drivers/phy/samsung/phy-exynos-usbdp-reg-dp.h new file mode 100644 index 000000000000..6a43084973a9 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdp-reg-dp.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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_ */ diff --git a/drivers/phy/samsung/phy-exynos-usbdp-reg-pcs.h b/drivers/phy/samsung/phy-exynos-usbdp-reg-pcs.h new file mode 100644 index 000000000000..550b93d2cc8d --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdp-reg-pcs.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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_ */ diff --git a/drivers/phy/samsung/phy-exynos-usbdp-reg-trsv.h b/drivers/phy/samsung/phy-exynos-usbdp-reg-trsv.h new file mode 100644 index 000000000000..72d4f60f425f --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdp-reg-trsv.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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_ */ diff --git a/drivers/phy/samsung/phy-exynos-usbdp-reg.h b/drivers/phy/samsung/phy-exynos-usbdp-reg.h new file mode 100644 index 000000000000..e790da2b6ca9 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdp-reg.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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_ */ diff --git a/drivers/phy/samsung/phy-exynos-usbdp.c b/drivers/phy/samsung/phy-exynos-usbdp.c new file mode 100644 index 000000000000..0d861d97a83b --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdp.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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 +#include +#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) +{ + +} diff --git a/drivers/phy/samsung/phy-exynos-usbdp.h b/drivers/phy/samsung/phy-exynos-usbdp.h new file mode 100644 index 000000000000..2cd394c803fa --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdp.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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_ */ diff --git a/drivers/phy/samsung/phy-exynos-usbdrd.c b/drivers/phy/samsung/phy-exynos-usbdrd.c new file mode 100644 index 000000000000..8fa77e342e09 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdrd.c @@ -0,0 +1,1914 @@ +/* + * 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 + * Minho Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if IS_ENABLED(CONFIG_EXYNOS_OTP) +#include +#endif +#ifdef CONFIG_OF +#include +#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 "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:phy_exynos_usbdrd"); diff --git a/drivers/phy/samsung/phy-exynos-usbdrd.h b/drivers/phy/samsung/phy-exynos-usbdrd.h new file mode 100644 index 000000000000..bbe4ed383f22 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdrd.h @@ -0,0 +1,195 @@ +/* + * 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__ */ diff --git a/drivers/phy/samsung/phy-exynos-usbdrd3.c b/drivers/phy/samsung/phy-exynos-usbdrd3.c new file mode 100644 index 000000000000..5ab61ef8e26d --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-usbdrd3.c @@ -0,0 +1,1644 @@ +/* + * 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 + * Minho Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if IS_ENABLED(CONFIG_EXYNOS_OTP) +#include +#endif +#ifdef CONFIG_OF +#include +#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 "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:phy_exynos_usbdrd"); diff --git a/drivers/phy/samsung/phy-samsung-usb-cal.h b/drivers/phy/samsung/phy-samsung-usb-cal.h new file mode 100644 index 000000000000..8f01655e9702 --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-usb-cal.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * + * 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__ */ diff --git a/drivers/phy/samsung/phy-samsung-usb3-cal-combo.h b/drivers/phy/samsung/phy-samsung-usb3-cal-combo.h new file mode 100644 index 000000000000..abbabf8aa342 --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-usb3-cal-combo.h @@ -0,0 +1,174 @@ +#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 diff --git a/drivers/phy/samsung/phy-samsung-usb3-cal.c b/drivers/phy/samsung/phy-samsung-usb3-cal.c new file mode 100644 index 000000000000..68685ed2a76c --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-usb3-cal.c @@ -0,0 +1,1207 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Sung-Hyun Na + * Author: Minho Lee + * + * 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 +#include +#endif + +#include + +#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); + } +} diff --git a/drivers/phy/samsung/phy-samsung-usb3-cal.h b/drivers/phy/samsung/phy-samsung-usb3-cal.h new file mode 100644 index 000000000000..af1e41645c7a --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-usb3-cal.h @@ -0,0 +1,278 @@ +#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 diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 15e822e55a72..20b2bcf34072 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -83,6 +83,14 @@ config AM335X_PHY_USB 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 diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index e694d4008c4a..216f3c3d7f2c 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -44,6 +44,11 @@ enum phy_mode { 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); @@ -137,6 +142,11 @@ void phy_pm_runtime_allow(struct phy *phy); 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); @@ -234,6 +244,41 @@ static inline int phy_exit(struct phy *phy) 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)