ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/usb/phy/Kconfig b/marvell/linux/drivers/usb/phy/Kconfig
new file mode 100644
index 0000000..92d44da
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/Kconfig
@@ -0,0 +1,202 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Physical Layer USB driver configuration
+#
+menu "USB Physical Layer drivers"
+
+config USB_PHY
+	def_bool y
+
+#
+# USB Transceiver Drivers
+#
+config AB8500_USB
+	tristate "AB8500 USB Transceiver Driver"
+	depends on AB8500_CORE
+	select USB_PHY
+	help
+	  Enable this to support the USB OTG transceiver in AB8500 chip.
+	  This transceiver supports high and full speed devices plus,
+	  in host mode, low speed.
+
+config FSL_USB2_OTG
+	tristate "Freescale USB OTG Transceiver Driver"
+	depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM=y && PM
+	depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
+	select USB_PHY
+	help
+	  Enable this to support Freescale USB OTG transceiver.
+
+config ISP1301_OMAP
+	tristate "Philips ISP1301 with OMAP OTG"
+	depends on I2C && ARCH_OMAP_OTG
+	depends on USB
+	depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
+	select USB_PHY
+	help
+	  If you say yes here you get support for the Philips ISP1301
+	  USB-On-The-Go transceiver working with the OMAP OTG controller.
+	  The ISP1301 is a full speed USB  transceiver which is used in
+	  products including H2, H3, and H4 development boards for Texas
+	  Instruments OMAP processors.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called phy-isp1301-omap.
+
+config KEYSTONE_USB_PHY
+	tristate "Keystone USB PHY Driver"
+	depends on ARCH_KEYSTONE || COMPILE_TEST
+	depends on NOP_USB_XCEIV
+	help
+	  Enable this to support Keystone USB phy. This driver provides
+	  interface to interact with USB 2.0 and USB 3.0 PHY that is part
+	  of the Keystone SOC.
+
+config NOP_USB_XCEIV
+	tristate "NOP USB Transceiver Driver"
+	depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, NOP can't be built-in
+	select USB_PHY
+	help
+	  This driver is to be used by all the usb transceiver which are either
+	  built-in with usb ip or which are autonomous and doesn't require any
+	  phy programming such as ISP1x04 etc.
+
+config AM335X_CONTROL_USB
+	tristate
+
+config AM335X_PHY_USB
+	tristate "AM335x USB PHY Driver"
+	depends on ARM || COMPILE_TEST
+	depends on NOP_USB_XCEIV
+	select USB_PHY
+	select AM335X_CONTROL_USB
+	select USB_COMMON
+	help
+	  This driver provides PHY support for that phy which part for the
+	  AM335x SoC.
+
+config TWL6030_USB
+	tristate "TWL6030 USB Transceiver Driver"
+	depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS
+	depends on OF
+	help
+	  Enable this to support the USB OTG transceiver on TWL6030
+	  family chips. This TWL6030 transceiver has the VBUS and ID GND
+	  and OTG SRP events capabilities. For all other transceiver functionality
+	  UTMI PHY is embedded in OMAP4430. The internal PHY configurations APIs
+	  are hooked to this driver through platform_data structure.
+	  The definition of internal PHY APIs are in the mach-omap2 layer.
+
+config USB_GPIO_VBUS
+	tristate "GPIO based peripheral-only VBUS sensing 'transceiver'"
+	depends on GPIOLIB || COMPILE_TEST
+	depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
+	select USB_PHY
+	help
+	  Provides simple GPIO VBUS sensing for controllers with an
+	  internal transceiver via the usb_phy interface, and
+	  optionally control of a D+ pullup GPIO as well as a VBUS
+	  current limit regulator.
+
+config OMAP_OTG
+	tristate "OMAP USB OTG controller driver"
+	depends on ARCH_OMAP_OTG && EXTCON
+	help
+	  Enable this to support some transceivers on OMAP1 platforms. OTG
+	  controller is needed to switch between host and peripheral modes.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called phy-omap-otg.
+
+config TAHVO_USB
+	tristate "Tahvo USB transceiver driver"
+	depends on MFD_RETU
+	depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
+	select USB_PHY
+	help
+	  Enable this to support USB transceiver on Tahvo. This is used
+	  at least on Nokia 770.
+
+config TAHVO_USB_HOST_BY_DEFAULT
+	depends on TAHVO_USB
+	bool "Device in USB host mode by default"
+	help
+	  Say Y here, if you want the device to enter USB host mode
+	  by default on bootup.
+
+config USB_ISP1301
+	tristate "NXP ISP1301 USB transceiver support"
+	depends on USB || USB_GADGET
+	depends on I2C
+	select USB_PHY
+	help
+	  Say Y here to add support for the NXP ISP1301 USB transceiver driver.
+	  This chip is typically used as USB transceiver for USB host, gadget
+	  and OTG drivers (to be selected separately).
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called phy-isp1301.
+
+config USB_MV_OTG
+	tristate "Marvell USB OTG support"
+	depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG
+	depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
+	select USB_PHY
+	help
+	  Say Y here if you want to build Marvell USB OTG transciever
+	  driver in kernel (including PXA and MMP series). This driver
+	  implements role switch between EHCI host driver and gadget driver.
+
+	  To compile this driver as a module, choose M here.
+
+config MV_USB2_PHY
+	tristate "ASR USB 2.0 PHY Driver"
+	select USB_PHY
+	help
+	  Enable this to support Marvell USB 2.0 PHY driver for Marvell
+	  SoC. This driver will do the PHY initialization and shutdown.
+	  The PHY driver will be used by Marvell udc/ehci/otg driver.
+
+	  To compile this driver as a module, choose M here.
+
+config USB_MXS_PHY
+	tristate "Freescale MXS USB PHY support"
+	depends on ARCH_MXC || ARCH_MXS
+	select STMP_DEVICE
+	select USB_PHY
+	help
+	  Enable this to support the Freescale MXS USB PHY.
+
+	  MXS Phy is used by some of the i.MX SoCs, for example imx23/28/6x.
+
+config USB_TEGRA_PHY
+	tristate "NVIDIA Tegra USB PHY Driver"
+	depends on ARCH_TEGRA
+	select USB_COMMON
+	select USB_PHY
+	select USB_ULPI
+	help
+	  This driver provides PHY support for the USB controllers found
+	  on NVIDIA Tegra SoC's.
+
+config USB_ULPI
+	bool "Generic ULPI Transceiver Driver"
+	depends on ARM || ARM64
+	select USB_ULPI_VIEWPORT
+	help
+	  Enable this to support ULPI connected USB OTG transceivers which
+	  are likely found on embedded boards.
+
+config USB_ULPI_VIEWPORT
+	bool
+	help
+	  Provides read/write operations to the ULPI phy register set for
+	  controllers with a viewport register (e.g. Chipidea/ARC controllers).
+
+config FUSB301_TYPEC_PHY
+	bool "FUSB301 Typec Controller"
+	depends on CPU_ASR1901
+	help
+	  Enable this to support FUSB301 typec controller driver which is used
+	  for usb typec connection detection
+endmenu
diff --git a/marvell/linux/drivers/usb/phy/Makefile b/marvell/linux/drivers/usb/phy/Makefile
new file mode 100644
index 0000000..0a0128c
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/Makefile
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for physical layer USB drivers
+#
+obj-$(CONFIG_USB_PHY)			+= phy.o
+obj-$(CONFIG_OF)			+= of.o
+
+# transceiver drivers, keep the list sorted
+
+obj-$(CONFIG_AB8500_USB)		+= phy-ab8500-usb.o
+obj-$(CONFIG_FSL_USB2_OTG)		+= phy-fsl-usb.o
+obj-$(CONFIG_ISP1301_OMAP)		+= phy-isp1301-omap.o
+obj-$(CONFIG_NOP_USB_XCEIV)		+= phy-generic.o
+obj-$(CONFIG_TAHVO_USB)			+= phy-tahvo.o
+obj-$(CONFIG_AM335X_CONTROL_USB)	+= phy-am335x-control.o
+obj-$(CONFIG_AM335X_PHY_USB)		+= phy-am335x.o
+obj-$(CONFIG_OMAP_OTG)			+= phy-omap-otg.o
+obj-$(CONFIG_TWL6030_USB)		+= phy-twl6030-usb.o
+obj-$(CONFIG_USB_TEGRA_PHY)		+= phy-tegra-usb.o
+obj-$(CONFIG_USB_GPIO_VBUS)		+= phy-gpio-vbus-usb.o
+obj-$(CONFIG_USB_ISP1301)		+= phy-isp1301.o
+obj-$(CONFIG_MV_USB2_PHY)		+= phy-mv-usb2.o phy-mv-connector.o
+obj-$(CONFIG_USB_DWC2)			+= phy-mv-usb2.o phy-mv-connector.o
+obj-$(CONFIG_USB_88PM80X)		+= phy-mv-connector.o
+obj-$(CONFIG_USB_MV_OTG)		+= phy-mv-usb.o
+obj-$(CONFIG_USB_MXS_PHY)		+= phy-mxs-usb.o
+obj-$(CONFIG_USB_ULPI)			+= phy-ulpi.o
+obj-$(CONFIG_USB_ULPI_VIEWPORT)		+= phy-ulpi-viewport.o
+obj-$(CONFIG_KEYSTONE_USB_PHY)		+= phy-keystone.o
+obj-$(CONFIG_USB_DWC3)			+= phy-asr-usb3.o phy-mv-connector.o
+obj-$(CONFIG_FUSB301_TYPEC_PHY)		+= fusb301_typec.o
+
diff --git a/marvell/linux/drivers/usb/phy/fusb301_typec.c b/marvell/linux/drivers/usb/phy/fusb301_typec.c
new file mode 100644
index 0000000..53a0b8e
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/fusb301_typec.c
@@ -0,0 +1,281 @@
+/*
+ * Base driver for fusb301 typec controller
+ *
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/notifier.h>
+#include <linux/err.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/platform_data/mv_usb.h>
+#include <linux/debugfs.h>
+
+#define FUSB301_REG_00		0x00
+#define FUSB301_REG_01		0x01
+#define FUSB301_REG_02		0x02
+#define FUSB301_REG_03		0x03
+#define FUSB301_REG_04		0x04
+#define FUSB301_REG_05		0x05
+#define FUSB301_REG_06		0x06
+#define FUSB301_REG_07		0x07
+#define FUSB301_REG_08		0x08
+#define FUSB301_REG_09		0x09
+#define FUSB301_REG_0A		0x0A
+#define FUSB301_REG_0B		0x0B
+#define FUSB301_REG_0C		0x0C
+#define FUSB301_REG_0D		0x0D
+#define FUSB301_REG_0E		0x0E
+#define FUSB301_REG_0F		0x0F
+#define FUSB301_REG_10		0x10
+#define FUSB301_REG_11		0x11
+#define FUSB301_REG_12		0x12
+#define FUSB301_REG_13		0x13
+
+#define FUSB301_ID			(0x12)
+
+struct fusb301_device_info {
+	struct device *dev;
+	struct i2c_client *client;
+};
+
+static struct fusb301_device_info *g_info;
+
+static int fusb301_write_reg(struct i2c_client *client, u8 reg, u8 data)
+{
+	int ret = 0;
+	if (!client)
+		return -EINVAL;
+	ret = i2c_smbus_write_byte_data(client, reg, data);
+	if (ret < 0) {
+		dev_err(&client->dev, "fusb301: failed to write reg-0x%02x\n",
+			reg);
+		return ret;
+	}
+	return 0;
+}
+
+static int fusb301_read_reg(struct i2c_client *client, u8 reg, u8 *data)
+{
+	int ret = 0;
+	if (!client || !data)
+		return -EINVAL;
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "fusb301: failed to read reg-0x%02x\n",
+			reg);
+		return ret;
+	}
+	*data = ret;
+	return 0;
+}
+
+static int fusb301_device_init(struct fusb301_device_info *info)
+{
+	unsigned char value;
+
+#ifdef CONFIG_USB_DWC3_DUAL_ROLE
+	value = (0x1 << 4);
+#else
+	value = (0x1 << 2);
+#endif
+	fusb301_write_reg(info->client, FUSB301_REG_02, value);
+
+	return 0;
+}
+
+static int fusb301_get_typec_dir(unsigned int *dir)
+{
+	int ret = 0;
+	unsigned char value;
+
+	/* debounce time */
+	msleep(87);
+	ret = fusb301_read_reg(g_info->client, FUSB301_REG_11, &value);
+	if (ret) {
+		printk("!!!fusb301 read failed\n");
+		return ret;
+	}
+
+	dev_info(g_info->dev,
+		"fusb301 reg11: 0x%02x\n", value);
+
+	if ((value & (0x3 << 4)) == (0x1 << 4))
+		*dir = USB_TYPEC_DIR_CC1;
+	else if ((value & (0x3 << 4)) == (0x2 << 4))
+		*dir = USB_TYPEC_DIR_CC2;
+	else
+		dev_err(g_info->dev,
+			"fusb301 reg11 error: 0x%02x\n", value);
+
+	return ret;
+}
+
+static int fusb301_dt_init(struct device_node *np,
+		struct device *dev,
+		struct fusb301_device_info *pinfo)
+{
+	int ret = 0;
+
+	return ret;
+}
+
+int detect_fusb301_version(struct fusb301_device_info *info)
+{
+	int ret = 0;
+	u8 value;
+
+	ret = fusb301_read_reg(info->client, FUSB301_REG_01, &value);
+	if (ret)
+		return ret;
+
+	switch (value) {
+	case FUSB301_ID:
+		dev_info(info->dev,
+			"fusb301 chip version: 0x%02x\n", value);
+		break;
+	default:
+		dev_err(info->dev,
+			"fusb301 unknown version: 0x%02x\n", value);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+static int fusb301_typec_probe(struct i2c_client *client,
+				 const struct i2c_device_id *id)
+{
+	struct fusb301_device_info *info;
+	struct device_node *node = client->dev.of_node;
+	int ret = 0;
+
+	info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		dev_err(&client->dev, "failed to allocate device info data\n");
+		return -ENOMEM;
+	}
+
+	info->client = client;
+	info->dev = &client->dev;
+	i2c_set_clientdata(client, info);
+	dev_set_drvdata(info->dev, info);
+
+	ret = detect_fusb301_version(info);
+	if (ret)
+		goto out;
+
+	ret = fusb301_dt_init(node, &client->dev, info);
+	if (ret)
+		goto out;
+
+	fusb301_device_init(info);
+	pxa_usb_set_extern_call(PXA_USB_DEV_OTG, typec, get_typec_dir,
+				fusb301_get_typec_dir);
+	g_info = info;
+	dev_info(&client->dev, "fusb301 probe finished\n");
+	return 0;
+
+out:
+	return ret;
+}
+
+static int fusb301_typec_remove(struct i2c_client *client)
+{
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int fusb301_typec_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int fusb301_typec_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops fusb301_pm_ops = {
+	.suspend = fusb301_typec_suspend,
+	.resume = fusb301_typec_resume,
+};
+#endif
+
+static void fusb301_typec_shutdown(struct i2c_client *client)
+{
+}
+
+static const struct i2c_device_id fusb301_id[] = {
+	{"fusb301-typec", -1},
+	{}
+};
+
+static const struct of_device_id fusb301_dt_match[] = {
+	{ .compatible = "asr,fusb301-typec", },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(of, fusb301_dt_match);
+
+static struct i2c_driver fusb301_typec_driver = {
+	.driver = {.name = "fusb301-typec",
+#ifdef CONFIG_PM
+		   .pm = &fusb301_pm_ops,
+#endif
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(fusb301_dt_match),
+	},
+	.probe = fusb301_typec_probe,
+	.remove = fusb301_typec_remove,
+	.shutdown = fusb301_typec_shutdown,
+	.id_table = fusb301_id,
+};
+
+static int __init fusb301_typec_init(void)
+{
+	int ret;
+	ret = i2c_add_driver(&fusb301_typec_driver);
+	if (ret)
+		pr_err("Unable to register fusb301 typec driver");
+	return ret;
+}
+
+subsys_initcall(fusb301_typec_init);
+
+static void __exit fusb301_typec_exit(void)
+{
+	i2c_del_driver(&fusb301_typec_driver);
+}
+module_exit(fusb301_typec_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("FUSB301 TypeC Driver");
diff --git a/marvell/linux/drivers/usb/phy/of.c b/marvell/linux/drivers/usb/phy/of.c
new file mode 100644
index 0000000..1ab134f
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/of.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB of helper code
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/usb/of.h>
+#include <linux/usb/otg.h>
+
+static const char *const usbphy_modes[] = {
+	[USBPHY_INTERFACE_MODE_UNKNOWN]	= "",
+	[USBPHY_INTERFACE_MODE_UTMI]	= "utmi",
+	[USBPHY_INTERFACE_MODE_UTMIW]	= "utmi_wide",
+	[USBPHY_INTERFACE_MODE_ULPI]	= "ulpi",
+	[USBPHY_INTERFACE_MODE_SERIAL]	= "serial",
+	[USBPHY_INTERFACE_MODE_HSIC]	= "hsic",
+};
+
+/**
+ * of_usb_get_phy_mode - Get phy mode for given device_node
+ * @np:	Pointer to the given device_node
+ *
+ * The function gets phy interface string from property 'phy_type',
+ * and returns the corresponding enum usb_phy_interface
+ */
+enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np)
+{
+	const char *phy_type;
+	int err, i;
+
+	err = of_property_read_string(np, "phy_type", &phy_type);
+	if (err < 0)
+		return USBPHY_INTERFACE_MODE_UNKNOWN;
+
+	for (i = 0; i < ARRAY_SIZE(usbphy_modes); i++)
+		if (!strcmp(phy_type, usbphy_modes[i]))
+			return i;
+
+	return USBPHY_INTERFACE_MODE_UNKNOWN;
+}
+EXPORT_SYMBOL_GPL(of_usb_get_phy_mode);
diff --git a/marvell/linux/drivers/usb/phy/phy-ab8500-usb.c b/marvell/linux/drivers/usb/phy/phy-ab8500-usb.c
new file mode 100644
index 0000000..4bb4b1d
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-ab8500-usb.c
@@ -0,0 +1,989 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * USB transceiver driver for AB8500 family chips
+ *
+ * Copyright (C) 2010-2013 ST-Ericsson AB
+ * Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * Avinash Kumar <avinash.kumar@stericsson.com>
+ * Thirupathi Chippakurthy <thirupathi.chippakurthy@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/otg.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/usb/musb-ux500.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
+
+/* Bank AB8500_SYS_CTRL2_BLOCK */
+#define AB8500_MAIN_WD_CTRL_REG 0x01
+
+/* Bank AB8500_USB */
+#define AB8500_USB_LINE_STAT_REG 0x80
+#define AB8505_USB_LINE_STAT_REG 0x94
+#define AB8500_USB_PHY_CTRL_REG 0x8A
+
+/* Bank AB8500_DEVELOPMENT */
+#define AB8500_BANK12_ACCESS 0x00
+
+/* Bank AB8500_DEBUG */
+#define AB8500_USB_PHY_TUNE1 0x05
+#define AB8500_USB_PHY_TUNE2 0x06
+#define AB8500_USB_PHY_TUNE3 0x07
+
+/* Bank AB8500_INTERRUPT */
+#define AB8500_IT_SOURCE2_REG 0x01
+
+#define AB8500_BIT_OTG_STAT_ID (1 << 0)
+#define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0)
+#define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1)
+#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0)
+#define AB8500_BIT_WD_CTRL_KICK (1 << 1)
+#define AB8500_BIT_SOURCE2_VBUSDET (1 << 7)
+
+#define AB8500_WD_KICK_DELAY_US 100 /* usec */
+#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */
+#define AB8500_V20_31952_DISABLE_DELAY_US 100 /* usec */
+
+/* Usb line status register */
+enum ab8500_usb_link_status {
+	USB_LINK_NOT_CONFIGURED_8500 = 0,
+	USB_LINK_STD_HOST_NC_8500,
+	USB_LINK_STD_HOST_C_NS_8500,
+	USB_LINK_STD_HOST_C_S_8500,
+	USB_LINK_HOST_CHG_NM_8500,
+	USB_LINK_HOST_CHG_HS_8500,
+	USB_LINK_HOST_CHG_HS_CHIRP_8500,
+	USB_LINK_DEDICATED_CHG_8500,
+	USB_LINK_ACA_RID_A_8500,
+	USB_LINK_ACA_RID_B_8500,
+	USB_LINK_ACA_RID_C_NM_8500,
+	USB_LINK_ACA_RID_C_HS_8500,
+	USB_LINK_ACA_RID_C_HS_CHIRP_8500,
+	USB_LINK_HM_IDGND_8500,
+	USB_LINK_RESERVED_8500,
+	USB_LINK_NOT_VALID_LINK_8500,
+};
+
+enum ab8505_usb_link_status {
+	USB_LINK_NOT_CONFIGURED_8505 = 0,
+	USB_LINK_STD_HOST_NC_8505,
+	USB_LINK_STD_HOST_C_NS_8505,
+	USB_LINK_STD_HOST_C_S_8505,
+	USB_LINK_CDP_8505,
+	USB_LINK_RESERVED0_8505,
+	USB_LINK_RESERVED1_8505,
+	USB_LINK_DEDICATED_CHG_8505,
+	USB_LINK_ACA_RID_A_8505,
+	USB_LINK_ACA_RID_B_8505,
+	USB_LINK_ACA_RID_C_NM_8505,
+	USB_LINK_RESERVED2_8505,
+	USB_LINK_RESERVED3_8505,
+	USB_LINK_HM_IDGND_8505,
+	USB_LINK_CHARGERPORT_NOT_OK_8505,
+	USB_LINK_CHARGER_DM_HIGH_8505,
+	USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8505,
+	USB_LINK_STD_UPSTREAM_NO_IDGNG_NO_VBUS_8505,
+	USB_LINK_STD_UPSTREAM_8505,
+	USB_LINK_CHARGER_SE1_8505,
+	USB_LINK_CARKIT_CHGR_1_8505,
+	USB_LINK_CARKIT_CHGR_2_8505,
+	USB_LINK_ACA_DOCK_CHGR_8505,
+	USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8505,
+	USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8505,
+	USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8505,
+	USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8505,
+	USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505,
+};
+
+enum ab8500_usb_mode {
+	USB_IDLE = 0,
+	USB_PERIPHERAL,
+	USB_HOST,
+	USB_DEDICATED_CHG
+};
+
+/* Register USB_LINK_STATUS interrupt */
+#define AB8500_USB_FLAG_USE_LINK_STATUS_IRQ	(1 << 0)
+/* Register ID_WAKEUP_F interrupt */
+#define AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ	(1 << 1)
+/* Register VBUS_DET_F interrupt */
+#define AB8500_USB_FLAG_USE_VBUS_DET_IRQ	(1 << 2)
+/* Driver is using the ab-iddet driver*/
+#define AB8500_USB_FLAG_USE_AB_IDDET		(1 << 3)
+/* Enable setting regulators voltage */
+#define AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE	(1 << 4)
+
+struct ab8500_usb {
+	struct usb_phy phy;
+	struct device *dev;
+	struct ab8500 *ab8500;
+	unsigned vbus_draw;
+	struct work_struct phy_dis_work;
+	enum ab8500_usb_mode mode;
+	struct clk *sysclk;
+	struct regulator *v_ape;
+	struct regulator *v_musb;
+	struct regulator *v_ulpi;
+	int saved_v_ulpi;
+	int previous_link_status_state;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *pins_sleep;
+	bool enabled_charging_detection;
+	unsigned int flags;
+};
+
+static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x)
+{
+	return container_of(x, struct ab8500_usb, phy);
+}
+
+static void ab8500_usb_wd_workaround(struct ab8500_usb *ab)
+{
+	abx500_set_register_interruptible(ab->dev,
+		AB8500_SYS_CTRL2_BLOCK,
+		AB8500_MAIN_WD_CTRL_REG,
+		AB8500_BIT_WD_CTRL_ENABLE);
+
+	udelay(AB8500_WD_KICK_DELAY_US);
+
+	abx500_set_register_interruptible(ab->dev,
+		AB8500_SYS_CTRL2_BLOCK,
+		AB8500_MAIN_WD_CTRL_REG,
+		(AB8500_BIT_WD_CTRL_ENABLE
+		| AB8500_BIT_WD_CTRL_KICK));
+
+	udelay(AB8500_WD_V11_DISABLE_DELAY_US);
+
+	abx500_set_register_interruptible(ab->dev,
+		AB8500_SYS_CTRL2_BLOCK,
+		AB8500_MAIN_WD_CTRL_REG,
+		0);
+}
+
+static void ab8500_usb_regulator_enable(struct ab8500_usb *ab)
+{
+	int ret, volt;
+
+	ret = regulator_enable(ab->v_ape);
+	if (ret)
+		dev_err(ab->dev, "Failed to enable v-ape\n");
+
+	if (ab->flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE) {
+		ab->saved_v_ulpi = regulator_get_voltage(ab->v_ulpi);
+		if (ab->saved_v_ulpi < 0)
+			dev_err(ab->dev, "Failed to get v_ulpi voltage\n");
+
+		ret = regulator_set_voltage(ab->v_ulpi, 1300000, 1350000);
+		if (ret < 0)
+			dev_err(ab->dev, "Failed to set the Vintcore to 1.3V, ret=%d\n",
+					ret);
+
+		ret = regulator_set_load(ab->v_ulpi, 28000);
+		if (ret < 0)
+			dev_err(ab->dev, "Failed to set optimum mode (ret=%d)\n",
+					ret);
+	}
+
+	ret = regulator_enable(ab->v_ulpi);
+	if (ret)
+		dev_err(ab->dev, "Failed to enable vddulpivio18\n");
+
+	if (ab->flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE) {
+		volt = regulator_get_voltage(ab->v_ulpi);
+		if ((volt != 1300000) && (volt != 1350000))
+			dev_err(ab->dev, "Vintcore is not set to 1.3V volt=%d\n",
+					volt);
+	}
+
+	ret = regulator_enable(ab->v_musb);
+	if (ret)
+		dev_err(ab->dev, "Failed to enable musb_1v8\n");
+}
+
+static void ab8500_usb_regulator_disable(struct ab8500_usb *ab)
+{
+	int ret;
+
+	regulator_disable(ab->v_musb);
+
+	regulator_disable(ab->v_ulpi);
+
+	/* USB is not the only consumer of Vintcore, restore old settings */
+	if (ab->flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE) {
+		if (ab->saved_v_ulpi > 0) {
+			ret = regulator_set_voltage(ab->v_ulpi,
+					ab->saved_v_ulpi, ab->saved_v_ulpi);
+			if (ret < 0)
+				dev_err(ab->dev, "Failed to set the Vintcore to %duV, ret=%d\n",
+						ab->saved_v_ulpi, ret);
+		}
+
+		ret = regulator_set_load(ab->v_ulpi, 0);
+		if (ret < 0)
+			dev_err(ab->dev, "Failed to set optimum mode (ret=%d)\n",
+					ret);
+	}
+
+	regulator_disable(ab->v_ape);
+}
+
+static void ab8500_usb_wd_linkstatus(struct ab8500_usb *ab, u8 bit)
+{
+	/* Workaround for v2.0 bug # 31952 */
+	if (is_ab8500_2p0(ab->ab8500)) {
+		abx500_mask_and_set_register_interruptible(ab->dev,
+				AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+				bit, bit);
+		udelay(AB8500_V20_31952_DISABLE_DELAY_US);
+	}
+}
+
+static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host)
+{
+	u8 bit;
+	bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN :
+		AB8500_BIT_PHY_CTRL_DEVICE_EN;
+
+	/* mux and configure USB pins to DEFAULT state */
+	ab->pinctrl = pinctrl_get_select(ab->dev, PINCTRL_STATE_DEFAULT);
+	if (IS_ERR(ab->pinctrl))
+		dev_err(ab->dev, "could not get/set default pinstate\n");
+
+	if (clk_prepare_enable(ab->sysclk))
+		dev_err(ab->dev, "can't prepare/enable clock\n");
+
+	ab8500_usb_regulator_enable(ab);
+
+	abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+			bit, bit);
+}
+
+static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host)
+{
+	u8 bit;
+	bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN :
+		AB8500_BIT_PHY_CTRL_DEVICE_EN;
+
+	ab8500_usb_wd_linkstatus(ab, bit);
+
+	abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+			bit, 0);
+
+	/* Needed to disable the phy.*/
+	ab8500_usb_wd_workaround(ab);
+
+	clk_disable_unprepare(ab->sysclk);
+
+	ab8500_usb_regulator_disable(ab);
+
+	if (!IS_ERR(ab->pinctrl)) {
+		/* configure USB pins to SLEEP state */
+		ab->pins_sleep = pinctrl_lookup_state(ab->pinctrl,
+				PINCTRL_STATE_SLEEP);
+
+		if (IS_ERR(ab->pins_sleep))
+			dev_dbg(ab->dev, "could not get sleep pinstate\n");
+		else if (pinctrl_select_state(ab->pinctrl, ab->pins_sleep))
+			dev_err(ab->dev, "could not set pins to sleep state\n");
+
+		/*
+		 * as USB pins are shared with iddet, release them to allow
+		 * iddet to request them
+		 */
+		pinctrl_put(ab->pinctrl);
+	}
+}
+
+#define ab8500_usb_host_phy_en(ab)	ab8500_usb_phy_enable(ab, true)
+#define ab8500_usb_host_phy_dis(ab)	ab8500_usb_phy_disable(ab, true)
+#define ab8500_usb_peri_phy_en(ab)	ab8500_usb_phy_enable(ab, false)
+#define ab8500_usb_peri_phy_dis(ab)	ab8500_usb_phy_disable(ab, false)
+
+static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
+		enum ab8505_usb_link_status lsts)
+{
+	enum ux500_musb_vbus_id_status event = 0;
+
+	dev_dbg(ab->dev, "ab8505_usb_link_status_update %d\n", lsts);
+
+	/*
+	 * Spurious link_status interrupts are seen at the time of
+	 * disconnection of a device in RIDA state
+	 */
+	if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8505 &&
+			(lsts == USB_LINK_STD_HOST_NC_8505))
+		return 0;
+
+	ab->previous_link_status_state = lsts;
+
+	switch (lsts) {
+	case USB_LINK_ACA_RID_B_8505:
+		event = UX500_MUSB_RIDB;
+		/* Fall through */
+	case USB_LINK_NOT_CONFIGURED_8505:
+	case USB_LINK_RESERVED0_8505:
+	case USB_LINK_RESERVED1_8505:
+	case USB_LINK_RESERVED2_8505:
+	case USB_LINK_RESERVED3_8505:
+		ab->mode = USB_IDLE;
+		ab->phy.otg->default_a = false;
+		ab->vbus_draw = 0;
+		if (event != UX500_MUSB_RIDB)
+			event = UX500_MUSB_NONE;
+		/*
+		 * Fallback to default B_IDLE as nothing
+		 * is connected
+		 */
+		ab->phy.otg->state = OTG_STATE_B_IDLE;
+		usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
+		break;
+
+	case USB_LINK_ACA_RID_C_NM_8505:
+		event = UX500_MUSB_RIDC;
+		/* Fall through */
+	case USB_LINK_STD_HOST_NC_8505:
+	case USB_LINK_STD_HOST_C_NS_8505:
+	case USB_LINK_STD_HOST_C_S_8505:
+	case USB_LINK_CDP_8505:
+		if (ab->mode == USB_IDLE) {
+			ab->mode = USB_PERIPHERAL;
+			ab8500_usb_peri_phy_en(ab);
+			atomic_notifier_call_chain(&ab->phy.notifier,
+					UX500_MUSB_PREPARE, &ab->vbus_draw);
+			usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
+		}
+		if (event != UX500_MUSB_RIDC)
+			event = UX500_MUSB_VBUS;
+		break;
+
+	case USB_LINK_ACA_RID_A_8505:
+	case USB_LINK_ACA_DOCK_CHGR_8505:
+		event = UX500_MUSB_RIDA;
+		/* Fall through */
+	case USB_LINK_HM_IDGND_8505:
+		if (ab->mode == USB_IDLE) {
+			ab->mode = USB_HOST;
+			ab8500_usb_host_phy_en(ab);
+			atomic_notifier_call_chain(&ab->phy.notifier,
+					UX500_MUSB_PREPARE, &ab->vbus_draw);
+		}
+		ab->phy.otg->default_a = true;
+		if (event != UX500_MUSB_RIDA)
+			event = UX500_MUSB_ID;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		break;
+
+	case USB_LINK_DEDICATED_CHG_8505:
+		ab->mode = USB_DEDICATED_CHG;
+		event = UX500_MUSB_CHARGER;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
+		enum ab8500_usb_link_status lsts)
+{
+	enum ux500_musb_vbus_id_status event = 0;
+
+	dev_dbg(ab->dev, "ab8500_usb_link_status_update %d\n", lsts);
+
+	/*
+	 * Spurious link_status interrupts are seen in case of a
+	 * disconnection of a device in IDGND and RIDA stage
+	 */
+	if (ab->previous_link_status_state == USB_LINK_HM_IDGND_8500 &&
+			(lsts == USB_LINK_STD_HOST_C_NS_8500 ||
+			 lsts == USB_LINK_STD_HOST_NC_8500))
+		return 0;
+
+	if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8500 &&
+			lsts == USB_LINK_STD_HOST_NC_8500)
+		return 0;
+
+	ab->previous_link_status_state = lsts;
+
+	switch (lsts) {
+	case USB_LINK_ACA_RID_B_8500:
+		event = UX500_MUSB_RIDB;
+		/* Fall through */
+	case USB_LINK_NOT_CONFIGURED_8500:
+	case USB_LINK_NOT_VALID_LINK_8500:
+		ab->mode = USB_IDLE;
+		ab->phy.otg->default_a = false;
+		ab->vbus_draw = 0;
+		if (event != UX500_MUSB_RIDB)
+			event = UX500_MUSB_NONE;
+		/* Fallback to default B_IDLE as nothing is connected */
+		ab->phy.otg->state = OTG_STATE_B_IDLE;
+		usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
+		break;
+
+	case USB_LINK_ACA_RID_C_NM_8500:
+	case USB_LINK_ACA_RID_C_HS_8500:
+	case USB_LINK_ACA_RID_C_HS_CHIRP_8500:
+		event = UX500_MUSB_RIDC;
+		/* Fall through */
+	case USB_LINK_STD_HOST_NC_8500:
+	case USB_LINK_STD_HOST_C_NS_8500:
+	case USB_LINK_STD_HOST_C_S_8500:
+	case USB_LINK_HOST_CHG_NM_8500:
+	case USB_LINK_HOST_CHG_HS_8500:
+	case USB_LINK_HOST_CHG_HS_CHIRP_8500:
+		if (ab->mode == USB_IDLE) {
+			ab->mode = USB_PERIPHERAL;
+			ab8500_usb_peri_phy_en(ab);
+			atomic_notifier_call_chain(&ab->phy.notifier,
+					UX500_MUSB_PREPARE, &ab->vbus_draw);
+			usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
+		}
+		if (event != UX500_MUSB_RIDC)
+			event = UX500_MUSB_VBUS;
+		break;
+
+	case USB_LINK_ACA_RID_A_8500:
+		event = UX500_MUSB_RIDA;
+		/* Fall through */
+	case USB_LINK_HM_IDGND_8500:
+		if (ab->mode == USB_IDLE) {
+			ab->mode = USB_HOST;
+			ab8500_usb_host_phy_en(ab);
+			atomic_notifier_call_chain(&ab->phy.notifier,
+					UX500_MUSB_PREPARE, &ab->vbus_draw);
+		}
+		ab->phy.otg->default_a = true;
+		if (event != UX500_MUSB_RIDA)
+			event = UX500_MUSB_ID;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		break;
+
+	case USB_LINK_DEDICATED_CHG_8500:
+		ab->mode = USB_DEDICATED_CHG;
+		event = UX500_MUSB_CHARGER;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
+		break;
+
+	case USB_LINK_RESERVED_8500:
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Connection Sequence:
+ *   1. Link Status Interrupt
+ *   2. Enable AB clock
+ *   3. Enable AB regulators
+ *   4. Enable USB phy
+ *   5. Reset the musb controller
+ *   6. Switch the ULPI GPIO pins to fucntion mode
+ *   7. Enable the musb Peripheral5 clock
+ *   8. Restore MUSB context
+ */
+static int abx500_usb_link_status_update(struct ab8500_usb *ab)
+{
+	u8 reg;
+	int ret = 0;
+
+	if (is_ab8500(ab->ab8500)) {
+		enum ab8500_usb_link_status lsts;
+
+		ret = abx500_get_register_interruptible(ab->dev,
+				AB8500_USB, AB8500_USB_LINE_STAT_REG, &reg);
+		if (ret < 0)
+			return ret;
+		lsts = (reg >> 3) & 0x0F;
+		ret = ab8500_usb_link_status_update(ab, lsts);
+	} else if (is_ab8505(ab->ab8500)) {
+		enum ab8505_usb_link_status lsts;
+
+		ret = abx500_get_register_interruptible(ab->dev,
+				AB8500_USB, AB8505_USB_LINE_STAT_REG, &reg);
+		if (ret < 0)
+			return ret;
+		lsts = (reg >> 3) & 0x1F;
+		ret = ab8505_usb_link_status_update(ab, lsts);
+	}
+
+	return ret;
+}
+
+/*
+ * Disconnection Sequence:
+ *   1. Disconnect Interrupt
+ *   2. Disable regulators
+ *   3. Disable AB clock
+ *   4. Disable the Phy
+ *   5. Link Status Interrupt
+ *   6. Disable Musb Clock
+ */
+static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data)
+{
+	struct ab8500_usb *ab = (struct ab8500_usb *) data;
+	enum usb_phy_events event = USB_EVENT_NONE;
+
+	/* Link status will not be updated till phy is disabled. */
+	if (ab->mode == USB_HOST) {
+		ab->phy.otg->default_a = false;
+		ab->vbus_draw = 0;
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		ab8500_usb_host_phy_dis(ab);
+		ab->mode = USB_IDLE;
+	}
+
+	if (ab->mode == USB_PERIPHERAL) {
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				event, &ab->vbus_draw);
+		ab8500_usb_peri_phy_dis(ab);
+		atomic_notifier_call_chain(&ab->phy.notifier,
+				UX500_MUSB_CLEAN, &ab->vbus_draw);
+		ab->mode = USB_IDLE;
+		ab->phy.otg->default_a = false;
+		ab->vbus_draw = 0;
+	}
+
+	if (is_ab8500_2p0(ab->ab8500)) {
+		if (ab->mode == USB_DEDICATED_CHG) {
+			ab8500_usb_wd_linkstatus(ab,
+					AB8500_BIT_PHY_CTRL_DEVICE_EN);
+			abx500_mask_and_set_register_interruptible(ab->dev,
+					AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+					AB8500_BIT_PHY_CTRL_DEVICE_EN, 0);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ab8500_usb_link_status_irq(int irq, void *data)
+{
+	struct ab8500_usb *ab = (struct ab8500_usb *)data;
+
+	abx500_usb_link_status_update(ab);
+
+	return IRQ_HANDLED;
+}
+
+static void ab8500_usb_phy_disable_work(struct work_struct *work)
+{
+	struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
+						phy_dis_work);
+
+	if (!ab->phy.otg->host)
+		ab8500_usb_host_phy_dis(ab);
+
+	if (!ab->phy.otg->gadget)
+		ab8500_usb_peri_phy_dis(ab);
+}
+
+static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend)
+{
+	/* TODO */
+	return 0;
+}
+
+static int ab8500_usb_set_peripheral(struct usb_otg *otg,
+					struct usb_gadget *gadget)
+{
+	struct ab8500_usb *ab;
+
+	if (!otg)
+		return -ENODEV;
+
+	ab = phy_to_ab(otg->usb_phy);
+
+	ab->phy.otg->gadget = gadget;
+
+	/* Some drivers call this function in atomic context.
+	 * Do not update ab8500 registers directly till this
+	 * is fixed.
+	 */
+
+	if ((ab->mode != USB_IDLE) && !gadget) {
+		ab->mode = USB_IDLE;
+		schedule_work(&ab->phy_dis_work);
+	}
+
+	return 0;
+}
+
+static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct ab8500_usb *ab;
+
+	if (!otg)
+		return -ENODEV;
+
+	ab = phy_to_ab(otg->usb_phy);
+
+	ab->phy.otg->host = host;
+
+	/* Some drivers call this function in atomic context.
+	 * Do not update ab8500 registers directly till this
+	 * is fixed.
+	 */
+
+	if ((ab->mode != USB_IDLE) && !host) {
+		ab->mode = USB_IDLE;
+		schedule_work(&ab->phy_dis_work);
+	}
+
+	return 0;
+}
+
+static void ab8500_usb_restart_phy(struct ab8500_usb *ab)
+{
+	abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+			AB8500_BIT_PHY_CTRL_DEVICE_EN,
+			AB8500_BIT_PHY_CTRL_DEVICE_EN);
+
+	udelay(100);
+
+	abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+			AB8500_BIT_PHY_CTRL_DEVICE_EN,
+			0);
+
+	abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+			AB8500_BIT_PHY_CTRL_HOST_EN,
+			AB8500_BIT_PHY_CTRL_HOST_EN);
+
+	udelay(100);
+
+	abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_USB, AB8500_USB_PHY_CTRL_REG,
+			AB8500_BIT_PHY_CTRL_HOST_EN,
+			0);
+}
+
+static int ab8500_usb_regulator_get(struct ab8500_usb *ab)
+{
+	int err;
+
+	ab->v_ape = devm_regulator_get(ab->dev, "v-ape");
+	if (IS_ERR(ab->v_ape)) {
+		dev_err(ab->dev, "Could not get v-ape supply\n");
+		err = PTR_ERR(ab->v_ape);
+		return err;
+	}
+
+	ab->v_ulpi = devm_regulator_get(ab->dev, "vddulpivio18");
+	if (IS_ERR(ab->v_ulpi)) {
+		dev_err(ab->dev, "Could not get vddulpivio18 supply\n");
+		err = PTR_ERR(ab->v_ulpi);
+		return err;
+	}
+
+	ab->v_musb = devm_regulator_get(ab->dev, "musb_1v8");
+	if (IS_ERR(ab->v_musb)) {
+		dev_err(ab->dev, "Could not get musb_1v8 supply\n");
+		err = PTR_ERR(ab->v_musb);
+		return err;
+	}
+
+	return 0;
+}
+
+static int ab8500_usb_irq_setup(struct platform_device *pdev,
+		struct ab8500_usb *ab)
+{
+	int err;
+	int irq;
+
+	if (ab->flags & AB8500_USB_FLAG_USE_LINK_STATUS_IRQ) {
+		irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS");
+		if (irq < 0)
+			return irq;
+		err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+				ab8500_usb_link_status_irq,
+				IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
+				"usb-link-status", ab);
+		if (err < 0) {
+			dev_err(ab->dev, "request_irq failed for link status irq\n");
+			return err;
+		}
+	}
+
+	if (ab->flags & AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ) {
+		irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F");
+		if (irq < 0)
+			return irq;
+		err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+				ab8500_usb_disconnect_irq,
+				IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
+				"usb-id-fall", ab);
+		if (err < 0) {
+			dev_err(ab->dev, "request_irq failed for ID fall irq\n");
+			return err;
+		}
+	}
+
+	if (ab->flags & AB8500_USB_FLAG_USE_VBUS_DET_IRQ) {
+		irq = platform_get_irq_byname(pdev, "VBUS_DET_F");
+		if (irq < 0)
+			return irq;
+		err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+				ab8500_usb_disconnect_irq,
+				IRQF_NO_SUSPEND | IRQF_SHARED | IRQF_ONESHOT,
+				"usb-vbus-fall", ab);
+		if (err < 0) {
+			dev_err(ab->dev, "request_irq failed for Vbus fall irq\n");
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void ab8500_usb_set_ab8500_tuning_values(struct ab8500_usb *ab)
+{
+	int err;
+
+	/* Enable the PBT/Bank 0x12 access */
+	err = abx500_set_register_interruptible(ab->dev,
+			AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to enable bank12 access err=%d\n",
+				err);
+
+	err = abx500_set_register_interruptible(ab->dev,
+			AB8500_DEBUG, AB8500_USB_PHY_TUNE1, 0xC8);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n",
+				err);
+
+	err = abx500_set_register_interruptible(ab->dev,
+			AB8500_DEBUG, AB8500_USB_PHY_TUNE2, 0x00);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n",
+				err);
+
+	err = abx500_set_register_interruptible(ab->dev,
+			AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x78);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
+				err);
+
+	/* Switch to normal mode/disable Bank 0x12 access */
+	err = abx500_set_register_interruptible(ab->dev,
+			AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x00);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to switch bank12 access err=%d\n",
+				err);
+}
+
+static void ab8500_usb_set_ab8505_tuning_values(struct ab8500_usb *ab)
+{
+	int err;
+
+	/* Enable the PBT/Bank 0x12 access */
+	err = abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS,
+			0x01, 0x01);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to enable bank12 access err=%d\n",
+				err);
+
+	err = abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_DEBUG, AB8500_USB_PHY_TUNE1,
+			0xC8, 0xC8);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n",
+				err);
+
+	err = abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_DEBUG, AB8500_USB_PHY_TUNE2,
+			0x60, 0x60);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n",
+				err);
+
+	err = abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_DEBUG, AB8500_USB_PHY_TUNE3,
+			0xFC, 0x80);
+
+	if (err < 0)
+		dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n",
+				err);
+
+	/* Switch to normal mode/disable Bank 0x12 access */
+	err = abx500_mask_and_set_register_interruptible(ab->dev,
+			AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS,
+			0x00, 0x00);
+	if (err < 0)
+		dev_err(ab->dev, "Failed to switch bank12 access err=%d\n",
+				err);
+}
+
+static int ab8500_usb_probe(struct platform_device *pdev)
+{
+	struct ab8500_usb	*ab;
+	struct ab8500		*ab8500;
+	struct usb_otg		*otg;
+	int err;
+	int rev;
+
+	ab8500 = dev_get_drvdata(pdev->dev.parent);
+	rev = abx500_get_chip_id(&pdev->dev);
+
+	if (is_ab8500_1p1_or_earlier(ab8500)) {
+		dev_err(&pdev->dev, "Unsupported AB8500 chip rev=%d\n", rev);
+		return -ENODEV;
+	}
+
+	ab = devm_kzalloc(&pdev->dev, sizeof(*ab), GFP_KERNEL);
+	if (!ab)
+		return -ENOMEM;
+
+	otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+	if (!otg)
+		return -ENOMEM;
+
+	ab->dev			= &pdev->dev;
+	ab->ab8500		= ab8500;
+	ab->phy.dev		= ab->dev;
+	ab->phy.otg		= otg;
+	ab->phy.label		= "ab8500";
+	ab->phy.set_suspend	= ab8500_usb_set_suspend;
+	ab->phy.otg->state	= OTG_STATE_UNDEFINED;
+
+	otg->usb_phy		= &ab->phy;
+	otg->set_host		= ab8500_usb_set_host;
+	otg->set_peripheral	= ab8500_usb_set_peripheral;
+
+	if (is_ab8500(ab->ab8500)) {
+		ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ |
+			AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ |
+			AB8500_USB_FLAG_USE_VBUS_DET_IRQ |
+			AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE;
+	} else if (is_ab8505(ab->ab8500)) {
+		ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ |
+			AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ |
+			AB8500_USB_FLAG_USE_VBUS_DET_IRQ |
+			AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE;
+	}
+
+	/* Disable regulator voltage setting for AB8500 <= v2.0 */
+	if (is_ab8500_2p0_or_earlier(ab->ab8500))
+		ab->flags &= ~AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE;
+
+	platform_set_drvdata(pdev, ab);
+
+	/* all: Disable phy when called from set_host and set_peripheral */
+	INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work);
+
+	err = ab8500_usb_regulator_get(ab);
+	if (err)
+		return err;
+
+	ab->sysclk = devm_clk_get(ab->dev, "sysclk");
+	if (IS_ERR(ab->sysclk)) {
+		dev_err(ab->dev, "Could not get sysclk.\n");
+		return PTR_ERR(ab->sysclk);
+	}
+
+	err = ab8500_usb_irq_setup(pdev, ab);
+	if (err < 0)
+		return err;
+
+	err = usb_add_phy(&ab->phy, USB_PHY_TYPE_USB2);
+	if (err) {
+		dev_err(&pdev->dev, "Can't register transceiver\n");
+		return err;
+	}
+
+	if (is_ab8500(ab->ab8500) && !is_ab8500_2p0_or_earlier(ab->ab8500))
+		/* Phy tuning values for AB8500 > v2.0 */
+		ab8500_usb_set_ab8500_tuning_values(ab);
+	else if (is_ab8505(ab->ab8500))
+		/* Phy tuning values for AB8505 */
+		ab8500_usb_set_ab8505_tuning_values(ab);
+
+	/* Needed to enable ID detection. */
+	ab8500_usb_wd_workaround(ab);
+
+	/*
+	 * This is required for usb-link-status to work properly when a
+	 * cable is connected at boot time.
+	 */
+	ab8500_usb_restart_phy(ab);
+
+	abx500_usb_link_status_update(ab);
+
+	dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev);
+
+	return 0;
+}
+
+static int ab8500_usb_remove(struct platform_device *pdev)
+{
+	struct ab8500_usb *ab = platform_get_drvdata(pdev);
+
+	cancel_work_sync(&ab->phy_dis_work);
+
+	usb_remove_phy(&ab->phy);
+
+	if (ab->mode == USB_HOST)
+		ab8500_usb_host_phy_dis(ab);
+	else if (ab->mode == USB_PERIPHERAL)
+		ab8500_usb_peri_phy_dis(ab);
+
+	return 0;
+}
+
+static const struct platform_device_id ab8500_usb_devtype[] = {
+	{ .name = "ab8500-usb", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype);
+
+static struct platform_driver ab8500_usb_driver = {
+	.probe		= ab8500_usb_probe,
+	.remove		= ab8500_usb_remove,
+	.id_table	= ab8500_usb_devtype,
+	.driver		= {
+		.name	= "abx5x0-usb",
+	},
+};
+
+static int __init ab8500_usb_init(void)
+{
+	return platform_driver_register(&ab8500_usb_driver);
+}
+subsys_initcall(ab8500_usb_init);
+
+static void __exit ab8500_usb_exit(void)
+{
+	platform_driver_unregister(&ab8500_usb_driver);
+}
+module_exit(ab8500_usb_exit);
+
+MODULE_AUTHOR("ST-Ericsson AB");
+MODULE_DESCRIPTION("AB8500 family usb transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/drivers/usb/phy/phy-am335x-control.c b/marvell/linux/drivers/usb/phy/phy-am335x-control.c
new file mode 100644
index 0000000..d16dfc3
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-am335x-control.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+#include "phy-am335x-control.h"
+
+struct am335x_control_usb {
+	struct device *dev;
+	void __iomem *phy_reg;
+	void __iomem *wkup;
+	spinlock_t lock;
+	struct phy_control phy_ctrl;
+};
+
+#define AM335X_USB0_CTRL		0x0
+#define AM335X_USB1_CTRL		0x8
+#define AM335x_USB_WKUP			0x0
+
+#define USBPHY_CM_PWRDN		(1 << 0)
+#define USBPHY_OTG_PWRDN	(1 << 1)
+#define USBPHY_OTGVDET_EN	(1 << 19)
+#define USBPHY_OTGSESSEND_EN	(1 << 20)
+
+#define AM335X_PHY0_WK_EN	(1 << 0)
+#define AM335X_PHY1_WK_EN	(1 << 8)
+
+static void am335x_phy_wkup(struct  phy_control *phy_ctrl, u32 id, bool on)
+{
+	struct am335x_control_usb *usb_ctrl;
+	u32 val;
+	u32 reg;
+
+	usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl);
+
+	switch (id) {
+	case 0:
+		reg = AM335X_PHY0_WK_EN;
+		break;
+	case 1:
+		reg = AM335X_PHY1_WK_EN;
+		break;
+	default:
+		WARN_ON(1);
+		return;
+	}
+
+	spin_lock(&usb_ctrl->lock);
+	val = readl(usb_ctrl->wkup);
+
+	if (on)
+		val |= reg;
+	else
+		val &= ~reg;
+
+	writel(val, usb_ctrl->wkup);
+	spin_unlock(&usb_ctrl->lock);
+}
+
+static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id,
+				enum usb_dr_mode dr_mode, bool on)
+{
+	struct am335x_control_usb *usb_ctrl;
+	u32 val;
+	u32 reg;
+
+	usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl);
+
+	switch (id) {
+	case 0:
+		reg = AM335X_USB0_CTRL;
+		break;
+	case 1:
+		reg = AM335X_USB1_CTRL;
+		break;
+	default:
+		WARN_ON(1);
+		return;
+	}
+
+	val = readl(usb_ctrl->phy_reg + reg);
+	if (on) {
+		if (dr_mode == USB_DR_MODE_HOST) {
+			val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN |
+					USBPHY_OTGVDET_EN);
+			val |= USBPHY_OTGSESSEND_EN;
+		} else {
+			val &= ~(USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN);
+			val |= USBPHY_OTGVDET_EN | USBPHY_OTGSESSEND_EN;
+		}
+	} else {
+		val |= USBPHY_CM_PWRDN | USBPHY_OTG_PWRDN;
+	}
+
+	writel(val, usb_ctrl->phy_reg + reg);
+
+	/*
+	 * Give the PHY ~1ms to complete the power up operation.
+	 * Tests have shown unstable behaviour if other USB PHY related
+	 * registers are written too shortly after such a transition.
+	 */
+	if (on)
+		mdelay(1);
+}
+
+static const struct phy_control ctrl_am335x = {
+	.phy_power = am335x_phy_power,
+	.phy_wkup = am335x_phy_wkup,
+};
+
+static const struct of_device_id omap_control_usb_id_table[] = {
+	{ .compatible = "ti,am335x-usb-ctrl-module", .data = &ctrl_am335x },
+	{}
+};
+MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
+
+static struct platform_driver am335x_control_driver;
+static int match(struct device *dev, const void *data)
+{
+	const struct device_node *node = (const struct device_node *)data;
+	return dev->of_node == node &&
+		dev->driver == &am335x_control_driver.driver;
+}
+
+struct phy_control *am335x_get_phy_control(struct device *dev)
+{
+	struct device_node *node;
+	struct am335x_control_usb *ctrl_usb;
+
+	node = of_parse_phandle(dev->of_node, "ti,ctrl_mod", 0);
+	if (!node)
+		return NULL;
+
+	dev = bus_find_device(&platform_bus_type, NULL, node, match);
+	of_node_put(node);
+	if (!dev)
+		return NULL;
+
+	ctrl_usb = dev_get_drvdata(dev);
+	put_device(dev);
+	if (!ctrl_usb)
+		return NULL;
+	return &ctrl_usb->phy_ctrl;
+}
+EXPORT_SYMBOL_GPL(am335x_get_phy_control);
+
+static int am335x_control_usb_probe(struct platform_device *pdev)
+{
+	struct resource	*res;
+	struct am335x_control_usb *ctrl_usb;
+	const struct of_device_id *of_id;
+	const struct phy_control *phy_ctrl;
+
+	of_id = of_match_node(omap_control_usb_id_table, pdev->dev.of_node);
+	if (!of_id)
+		return -EINVAL;
+
+	phy_ctrl = of_id->data;
+
+	ctrl_usb = devm_kzalloc(&pdev->dev, sizeof(*ctrl_usb), GFP_KERNEL);
+	if (!ctrl_usb)
+		return -ENOMEM;
+
+	ctrl_usb->dev = &pdev->dev;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
+	ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ctrl_usb->phy_reg))
+		return PTR_ERR(ctrl_usb->phy_reg);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wakeup");
+	ctrl_usb->wkup = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ctrl_usb->wkup))
+		return PTR_ERR(ctrl_usb->wkup);
+
+	spin_lock_init(&ctrl_usb->lock);
+	ctrl_usb->phy_ctrl = *phy_ctrl;
+
+	dev_set_drvdata(ctrl_usb->dev, ctrl_usb);
+	return 0;
+}
+
+static struct platform_driver am335x_control_driver = {
+	.probe		= am335x_control_usb_probe,
+	.driver		= {
+		.name	= "am335x-control-usb",
+		.of_match_table = omap_control_usb_id_table,
+	},
+};
+
+module_platform_driver(am335x_control_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/marvell/linux/drivers/usb/phy/phy-am335x-control.h b/marvell/linux/drivers/usb/phy/phy-am335x-control.h
new file mode 100644
index 0000000..cd4acfc
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-am335x-control.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _AM335x_PHY_CONTROL_H_
+#define _AM335x_PHY_CONTROL_H_
+
+struct phy_control {
+	void (*phy_power)(struct phy_control *phy_ctrl, u32 id,
+			enum usb_dr_mode dr_mode, bool on);
+	void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on);
+};
+
+static inline void phy_ctrl_power(struct phy_control *phy_ctrl, u32 id,
+				enum usb_dr_mode dr_mode, bool on)
+{
+	phy_ctrl->phy_power(phy_ctrl, id, dr_mode, on);
+}
+
+static inline void phy_ctrl_wkup(struct phy_control *phy_ctrl, u32 id, bool on)
+{
+	phy_ctrl->phy_wkup(phy_ctrl, id, on);
+}
+
+struct phy_control *am335x_get_phy_control(struct device *dev);
+
+#endif
diff --git a/marvell/linux/drivers/usb/phy/phy-am335x.c b/marvell/linux/drivers/usb/phy/phy-am335x.c
new file mode 100644
index 0000000..f5f0568
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-am335x.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/usb_phy_generic.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/usb/of.h>
+
+#include "phy-am335x-control.h"
+#include "phy-generic.h"
+
+struct am335x_phy {
+	struct usb_phy_generic usb_phy_gen;
+	struct phy_control *phy_ctrl;
+	int id;
+	enum usb_dr_mode dr_mode;
+};
+
+static int am335x_init(struct usb_phy *phy)
+{
+	struct am335x_phy *am_phy = dev_get_drvdata(phy->dev);
+
+	phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true);
+	return 0;
+}
+
+static void am335x_shutdown(struct usb_phy *phy)
+{
+	struct am335x_phy *am_phy = dev_get_drvdata(phy->dev);
+
+	phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
+}
+
+static int am335x_phy_probe(struct platform_device *pdev)
+{
+	struct am335x_phy *am_phy;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	am_phy = devm_kzalloc(dev, sizeof(*am_phy), GFP_KERNEL);
+	if (!am_phy)
+		return -ENOMEM;
+
+	am_phy->phy_ctrl = am335x_get_phy_control(dev);
+	if (!am_phy->phy_ctrl)
+		return -EPROBE_DEFER;
+
+	am_phy->id = of_alias_get_id(pdev->dev.of_node, "phy");
+	if (am_phy->id < 0) {
+		dev_err(&pdev->dev, "Missing PHY id: %d\n", am_phy->id);
+		return am_phy->id;
+	}
+
+	am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1);
+
+	ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, NULL);
+	if (ret)
+		return ret;
+
+	am_phy->usb_phy_gen.phy.init = am335x_init;
+	am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown;
+
+	platform_set_drvdata(pdev, am_phy);
+	device_init_wakeup(dev, true);
+
+	/*
+	 * If we leave PHY wakeup enabled then AM33XX wakes up
+	 * immediately from DS0. To avoid this we mark dev->power.can_wakeup
+	 * to false. The same is checked in suspend routine to decide
+	 * on whether to enable PHY wakeup or not.
+	 * PHY wakeup works fine in standby mode, there by allowing us to
+	 * handle remote wakeup, wakeup on disconnect and connect.
+	 */
+
+	device_set_wakeup_enable(dev, false);
+	phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
+
+	return usb_add_phy_dev(&am_phy->usb_phy_gen.phy);
+}
+
+static int am335x_phy_remove(struct platform_device *pdev)
+{
+	struct am335x_phy *am_phy = platform_get_drvdata(pdev);
+
+	usb_remove_phy(&am_phy->usb_phy_gen.phy);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int am335x_phy_suspend(struct device *dev)
+{
+	struct am335x_phy *am_phy = dev_get_drvdata(dev);
+
+	/*
+	 * Enable phy wakeup only if dev->power.can_wakeup is true.
+	 * Make sure to enable wakeup to support remote wakeup	in
+	 * standby mode ( same is not supported in OFF(DS0) mode).
+	 * Enable it by doing
+	 * echo enabled > /sys/bus/platform/devices/<usb-phy-id>/power/wakeup
+	 */
+
+	if (device_may_wakeup(dev))
+		phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true);
+
+	phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, false);
+
+	return 0;
+}
+
+static int am335x_phy_resume(struct device *dev)
+{
+	struct am335x_phy	*am_phy = dev_get_drvdata(dev);
+
+	phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, am_phy->dr_mode, true);
+
+	if (device_may_wakeup(dev))
+		phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(am335x_pm_ops, am335x_phy_suspend, am335x_phy_resume);
+
+static const struct of_device_id am335x_phy_ids[] = {
+	{ .compatible = "ti,am335x-usb-phy" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, am335x_phy_ids);
+
+static struct platform_driver am335x_phy_driver = {
+	.probe          = am335x_phy_probe,
+	.remove         = am335x_phy_remove,
+	.driver         = {
+		.name   = "am335x-phy-driver",
+		.pm = &am335x_pm_ops,
+		.of_match_table = am335x_phy_ids,
+	},
+};
+
+module_platform_driver(am335x_phy_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/marvell/linux/drivers/usb/phy/phy-asr-usb3.c b/marvell/linux/drivers/usb/phy/phy-asr-usb3.c
new file mode 100644
index 0000000..f9288cf
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-asr-usb3.c
@@ -0,0 +1,1134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for asr usb phy driver
+ *
+ * Copyright 2022 ASR Microelectronics (Shanghai) Co., Ltd.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/usb/phy.h>
+#include <linux/platform_data/mv_usb.h>
+#include <linux/cputype.h>
+#include <soc/asr/addr-map.h>
+
+/*
+ *
+ *	THE BASE ADDRESSES
+ *
+ */
+#define	USB_PHY_PORTB_OFFSET	(0x100000)
+
+#define	PUPHY_REG_OFFSET	(0x400)
+
+#define	PUPHY_REG00	(PUPHY_REG_OFFSET + (0x0 << 2))
+#define	PUPHY_REG01	(PUPHY_REG_OFFSET + (0x1 << 2))
+#define	PUPHY_REG02	(PUPHY_REG_OFFSET + (0x2 << 2))
+#define	PUPHY_REG03	(PUPHY_REG_OFFSET + (0x3 << 2))
+#define	PUPHY_REG04	(PUPHY_REG_OFFSET + (0x4 << 2))
+#define	PUPHY_REG05	(PUPHY_REG_OFFSET + (0x5 << 2))
+#define	PUPHY_REG06	(PUPHY_REG_OFFSET + (0x6 << 2))
+#define	PUPHY_REG07	(PUPHY_REG_OFFSET + (0x7 << 2))
+#define	PUPHY_REG08	(PUPHY_REG_OFFSET + (0x8 << 2))
+#define	PUPHY_REG09	(PUPHY_REG_OFFSET + (0x9 << 2))
+#define	PUPHY_REG0A	(PUPHY_REG_OFFSET + (0xA << 2))
+#define	PUPHY_REG0B	(PUPHY_REG_OFFSET + (0xB << 2))
+#define	PUPHY_REG0C	(PUPHY_REG_OFFSET + (0xC << 2))
+#define	PUPHY_REG0D	(PUPHY_REG_OFFSET + (0xD << 2))
+#define	PUPHY_REG0E	(PUPHY_REG_OFFSET + (0xE << 2))
+#define	PUPHY_REG0F	(PUPHY_REG_OFFSET + (0xF << 2))
+#define	PUPHY_REG10	(PUPHY_REG_OFFSET + (0x10 << 2))
+#define	PUPHY_REG11	(PUPHY_REG_OFFSET + (0x11 << 2))
+#define	PUPHY_REG13	(PUPHY_REG_OFFSET + (0x13 << 2))
+#define	PUPHY_REG15	(PUPHY_REG_OFFSET + (0x15 << 2))
+#define	PUPHY_REG16	(PUPHY_REG_OFFSET + (0x16 << 2))
+#define	PUPHY_REG17	(PUPHY_REG_OFFSET + (0x17 << 2))
+#define	PUPHY_REG18	(PUPHY_REG_OFFSET + (0x18 << 2))
+#define	PUPHY_REG19	(PUPHY_REG_OFFSET + (0x19 << 2))
+#define	PUPHY_REG1B	(PUPHY_REG_OFFSET + (0x1B << 2))
+#define	PUPHY_REG1E	(PUPHY_REG_OFFSET + (0x1E << 2))
+#define	PUPHY_REG20	(PUPHY_REG_OFFSET + (0x20 << 2))
+#define	PUPHY_REG21	(PUPHY_REG_OFFSET + (0x21 << 2))
+#define	PUPHY_REG2D	(PUPHY_REG_OFFSET + (0x2D << 2))
+
+#define PUPHY_REG06_SUSPEND_MASK		(0xFF300)
+#define PUPHY_REG06_SUSPEND_VAL			(0xAA200)
+#define PUPHY_REG06_SUSPEND_VAL2		(0x00000)
+
+#define PHY_SUSPEND_PLL			(0x1 << 13)
+#define PHY_SUSPEND_PHY			(0x1 << 12)
+/*
+ *
+ *	THE BASE ADDRESSES
+ *
+ */
+#define	USB2_PHY0_OFFSET	(0x0)
+/*	PHY REGS */
+#define USB2_PHY0_REG00	(USB2_PHY0_OFFSET+0x00)
+#define USB2_PHY0_REG01	(USB2_PHY0_OFFSET+0x04)
+#define USB2_PHY0_REG02	(USB2_PHY0_OFFSET+0x08)
+#define USB2_PHY0_REG03	(USB2_PHY0_OFFSET+0x0C)
+#define USB2_PHY0_REG04	(USB2_PHY0_OFFSET+0x10)
+#define USB2_PHY0_REG05	(USB2_PHY0_OFFSET+0x14)
+#define USB2_PHY0_REG06	(USB2_PHY0_OFFSET+0x18)
+#define USB2_PHY0_REG07	(USB2_PHY0_OFFSET+0x1C)
+#define USB2_PHY0_REG08	(USB2_PHY0_OFFSET+0x20)
+#define USB2_PHY0_REG09	(USB2_PHY0_OFFSET+0x24)
+#define USB2_PHY0_REG0A	(USB2_PHY0_OFFSET+0x28)
+#define USB2_PHY0_REG0B	(USB2_PHY0_OFFSET+0x2C)
+#define USB2_PHY0_REG0C	(USB2_PHY0_OFFSET+0x30)
+#define USB2_PHY0_REG0D	(USB2_PHY0_OFFSET+0x34)
+#define USB2_PHY0_REG0E	(USB2_PHY0_OFFSET+0x38)
+#define USB2_PHY0_REG0F	(USB2_PHY0_OFFSET+0x3C)
+#define USB2_PHY0_REG10	(USB2_PHY0_OFFSET+0x40)
+
+#ifdef CONFIG_CPU_ASR1901
+#define USB2_PHY0_REG25	(USB2_PHY0_OFFSET+0x98)
+#define USB2_PHY0_REG29	(USB2_PHY0_OFFSET+0x94)
+#else
+#define USB2_PHY0_REG25	(USB2_PHY0_OFFSET+0x94)
+#define USB2_PHY0_REG29	(USB2_PHY0_OFFSET+0xA4)
+#endif
+
+#define USB2_PLL_BIT_RDY		(0x1 << 0)
+#define USB2_CFG_HS_SRCS_SEL		(0x1 << 0)
+
+#ifdef CONFIG_CPU_ASR1901
+#define APMU_USB_PHY_READ 0x118
+#define USB_VBUS_STS  (0x1 << 2)
+#else
+#define APMU_USB_PHY_READ 0x7C
+#define USB_VBUS_STS  (0x1 << 15)
+#endif
+
+#define PORTA_TYPEC_CHECK_PIN_OFFSET	(0x01e048)
+#define PORTA_TYPEC_CHECK_REG_OFFSET	(0x020)
+
+#define PHY_USB2_PLL_BIT_RDY			(0x1 << 0)
+#define PHY_USB2_CFG_HS_SRCS_SEL		(0x1 << 0)
+#define PHY_USB2_SUSPEND_PLL			(0x1 << 13)
+#define PHY_USB2_SUSPEND_PHY			(0x1 << 12)
+
+#ifdef CONFIG_CPU_ASR1901
+#define PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN			(0x1 << 6)
+#else
+#define PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN			(0x1 << 14)
+#endif
+
+#define PHY_USB2_DCP_DET_PULL_UP_DOWN				((0x1 << 11) | (0x1 << 8))
+#define PHY_USB2_DCP_DET_PULL_UP_NONE				((0x1 << 11) | (0x1 << 10))
+#define PHY_USB2_DCP_DET_PULL_DOWN_DOWN			((0x1 << 10) | (0x1 << 8))
+#define PHY_USB2_DCP_DET_PULL_MASK				(0xF << 8)
+#define PHY_USB2_DRV_PULLDOWN_OVERRIDE_MASK			(0xF << 8)
+#define PHY_USB2_DRV_PULLDOWN_OVERRIDE_VAL			(0x9 << 8)
+
+#define PMUA_RTERM_CAL_REG	(0x3f8)
+
+#define APB_SPARE_REG24		(0x15c)
+/*
+ * struct asr_usb3_phy - transceiver driver state
+ * @phy: transceiver structure
+ * @dev: The parent device supplied to the probe function
+ * @clk: usb phy clock
+ * @base: usb phy register memory base
+ */
+struct asr_usb3_phy {
+	struct usb_phy	phy;
+	struct mv_usb_platform_data *pdata;
+	struct device	*dev;
+	struct clk	*clk;
+	void __iomem	*base;
+};
+
+static u32 usb3_phy_res_size;
+extern void dwc3_force_usb2_mode(void);
+extern u32 dwc3_get_line_status(void);
+extern u32 dwc3_usb_is_running(void);
+static void usb31_rterm_cal_porta(void __iomem *base);
+u32 usb31_rterm_cal_value;
+
+static int asr_usb_host_phy_private(struct usb_phy *phy, u32 option)
+{
+	struct asr_usb3_phy *asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+	void __iomem *phy_base = asr_usb3_phy->base;
+	int count = 0x100000;
+
+	pr_info("asr usb phy host priviate: %d\n", option);
+
+	if (option) {
+		;
+	} else {
+		writel(readl(phy_base + USB2_PHY0_REG10) | 0x1,
+				phy_base + USB2_PHY0_REG10);
+		while((readl(phy_base + USB2_PHY0_REG10) & 0x1) && (count--));
+		if (unlikely(count == 0))
+			pr_err("phy rd_hshost_disc_raw clear failed\n");
+	}
+
+	return 0;
+
+}
+
+static int asr_usb_get_vbus(struct usb_phy *phy)
+{
+	int ret;
+	u32 reg32;
+
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	reg32 = __raw_readl(apmu_base + APMU_USB_PHY_READ);
+	if (reg32 & USB_VBUS_STS)
+		ret = 1;
+	else
+		ret = 0;
+
+	return ret;
+
+}
+
+static int asr_usb_phy_charger_detect(struct usb_phy *phy)
+{
+	struct asr_usb3_phy *asr_usb3_phy;
+	void __iomem *base;
+	u32 reg32, reg32_ovrid;	
+	int charger_type_bc12 = NULL_CHARGER;
+
+	if (dwc3_usb_is_running()) {
+		return DEFAULT_CHARGER;
+	}
+
+	asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+	base = asr_usb3_phy->base;
+
+	reg32_ovrid = reg32 = readl(base + USB2_PHY0_REG25);
+	reg32 &= ~(PHY_USB2_DRV_PULLDOWN_OVERRIDE_MASK);
+	reg32 |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_VAL;
+	writel(reg32, base + USB2_PHY0_REG25);
+
+	reg32 = readl(base + USB2_PHY0_REG29);
+	reg32 |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+	writel(reg32, base + USB2_PHY0_REG29);
+
+	udelay(10);
+
+	reg32 = readl(base + USB2_PHY0_REG04);
+	reg32 &= ~(0xff << 8);
+	reg32 |= (0x12 << 8);
+	writel(reg32, base + USB2_PHY0_REG04);
+
+	if ((dwc3_get_line_status() & (0x3 << 8)) == (0x1 << 8)) {
+		charger_type_bc12 = NONE_STANDARD_CHARGER;
+	} else {
+		charger_type_bc12 = DCP_CHARGER;
+	}
+
+	/* restore reg29 and reg25 */
+	reg32 = readl(base + USB2_PHY0_REG29);
+	reg32 &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+	writel(reg32, base + USB2_PHY0_REG29);
+
+	writel(reg32_ovrid, base + USB2_PHY0_REG25);
+
+	return charger_type_bc12;
+}
+
+int asr1903_usb3_phy_suspend(struct usb_phy *phy, int suspend)
+{
+	struct asr_usb3_phy *asr_usb3_phy;
+	void __iomem *base;
+	u32 val;
+
+	asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+	base = asr_usb3_phy->base;
+
+	pr_info("USB3 PHY set_suspend: %d...\n", suspend);
+
+	if (suspend) {
+		val = readl(base + USB2_PHY0_REG0B);
+		val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0B);
+
+		val = readl(base + USB2_PHY0_REG0A);
+		val |= (PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0A);
+
+		/* pulldown dp and pulldown dm */
+		val = readl(base + USB2_PHY0_REG25);
+		val &= ~PHY_USB2_DCP_DET_PULL_MASK;
+		val |= PHY_USB2_DCP_DET_PULL_DOWN_DOWN;
+		writel(val, base + USB2_PHY0_REG25);
+
+		val = readl(base + USB2_PHY0_REG29);
+		val |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + USB2_PHY0_REG29);
+	} else {
+		/* disable dp/dm pull override */
+		val = readl(base + USB2_PHY0_REG29);
+		val &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + USB2_PHY0_REG29);
+
+		val = readl(base + USB2_PHY0_REG04);
+		val &= ~(0xff << 8);
+		val |= (0x12 << 8);
+		writel(val, base + USB2_PHY0_REG04);
+
+		val = readl(base + USB2_PHY0_REG0A);
+		val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0A);
+	}
+
+	pr_info("USB2 PHY set_suspend done\n");
+	return 0;
+}
+
+int asr_usb3_phy_suspend(struct usb_phy *phy, int suspend)
+{
+	struct asr_usb3_phy *asr_usb3_phy;
+	void __iomem *base;
+	u32 val, typec_dir = USB_TYPEC_DIR_CC2;
+	int ret;
+
+	if (cpu_is_asr1903())
+		return asr1903_usb3_phy_suspend(phy, suspend);
+
+	asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+	base = asr_usb3_phy->base;
+	pr_info("USB3 PHY set_suspend: %d...\n", suspend);
+	if (cpu_is_asr1901() || cpu_is_asr1906()) {
+		/* port B */
+		if (usb3_phy_res_size == (USB_PHY_PORTB_OFFSET + 0x1000))
+			base = base + USB_PHY_PORTB_OFFSET;
+	}
+	if (suspend) {
+		val = readl(base + USB2_PHY0_REG0B);
+		val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0B);
+
+		val = readl(base + USB2_PHY0_REG0A);
+		val |= (PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0A);
+
+		/* USB3 phy suspend */
+		val = readl(base + PUPHY_REG06);
+		val &= ~PUPHY_REG06_SUSPEND_MASK;
+		val |= PUPHY_REG06_SUSPEND_VAL;
+		writel(val, base + PUPHY_REG06);
+
+		/* pulldown dp and pulldown dm */
+		val = readl(base + USB2_PHY0_REG25);
+		val &= ~PHY_USB2_DCP_DET_PULL_MASK;
+		val |= PHY_USB2_DCP_DET_PULL_DOWN_DOWN;
+		writel(val, base + USB2_PHY0_REG25);
+
+		val = readl(base + USB2_PHY0_REG29);
+		val |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + USB2_PHY0_REG29);
+	} else {
+		/* disable dp/dm pull override */
+		val = readl(base + USB2_PHY0_REG29);
+		val &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + USB2_PHY0_REG29);
+
+		val = readl(base + USB2_PHY0_REG04);
+		val &= ~(0xff << 8);
+		val |= (0x12 << 8);
+		writel(val, base + USB2_PHY0_REG04);
+
+		/* USB3 phy resume */
+		val = readl(base + PUPHY_REG06);
+		val &= ~PUPHY_REG06_SUSPEND_VAL;
+#if 0
+		if (cpu_is_asr1901() || cpu_is_asr1906())
+			/* override/cfg term to avoid drop to HS/SS from SSP */
+			val |= (0x3 << 8);
+#endif
+		writel(val, base + PUPHY_REG06);
+
+		val = readl(base + USB2_PHY0_REG0A);
+		val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0A);
+
+		if (cpu_is_asr1901() || cpu_is_asr1906()) {
+			ret = pxa_usb_extern_call(PXA_USB_DEV_OTG, typec, get_typec_dir, &typec_dir);
+			if (ret) {
+				/* with gpio typec dir */
+				pr_info("!!!!!!!get_typec_dir failed\n");
+				writel((readl(base + PUPHY_REG08) & (~((0x1<<1)))), base + PUPHY_REG08);
+			} else {
+				pr_info("sw set typec dir: %d\n", typec_dir);
+				/* sw write typec dir */
+				val = readl(base + PUPHY_REG08);
+				val |= (0x1 << 1);
+				if (typec_dir == USB_TYPEC_DIR_CC2)
+					val |= (0x1 << 2);
+				else
+					val &= ~(0x1 << 2);
+				writel(val, base + PUPHY_REG08);
+			}
+
+			usb31_rterm_cal_porta(base);
+		}
+	}
+
+	pr_info("USB3 PHY set_suspend done\n");
+	return 0;
+}
+
+int asr1903_usb3_phy_suspend2(struct usb_phy *phy, int suspend)
+{
+	struct asr_usb3_phy *asr_usb3_phy;
+	void __iomem *base;
+	u32 val;
+
+	asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+	base = asr_usb3_phy->base;
+
+	pr_info("USB3 PHY set_suspend2: %d...\n", suspend);
+
+	if (suspend) {
+		val = readl(base + USB2_PHY0_REG0B);
+		val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0B);
+
+		val = readl(base + USB2_PHY0_REG0A);
+		val |= (PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0A);
+	} else {
+		/* disable dp/dm pull override */
+		val = readl(base + USB2_PHY0_REG29);
+		val &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + USB2_PHY0_REG29);
+
+		val = readl(base + USB2_PHY0_REG04);
+		val &= ~(0xff << 8);
+		val |= (0x12 << 8);
+		writel(val, base + USB2_PHY0_REG04);
+
+		val = readl(base + USB2_PHY0_REG0A);
+		val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0A);
+	}
+
+	pr_info("USB2 PHY set_suspend2 done\n");
+	return 0;
+}
+
+extern u32 dwc3_is_superspeed_plus(void);
+
+int asr_usb3_phy_suspend2(struct usb_phy *phy, int suspend)
+{
+	struct asr_usb3_phy *asr_usb3_phy;
+	void __iomem *base;
+	u32 val, typec_dir = USB_TYPEC_DIR_CC2;
+	int ret;
+
+	if (cpu_is_asr1903())
+		return asr1903_usb3_phy_suspend2(phy, suspend);
+
+	asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+	base = asr_usb3_phy->base;
+	pr_info("USB3 PHY set_suspend2: %d...\n", suspend);
+	if (cpu_is_asr1901() || cpu_is_asr1906()) {
+		/* port B */
+		if (usb3_phy_res_size == (USB_PHY_PORTB_OFFSET + 0x1000))
+			base = base + USB_PHY_PORTB_OFFSET;
+	}
+	if (suspend) {
+		val = readl(base + PUPHY_REG03);
+		val &= ~(0x1 << 11);
+		writel(val, base + PUPHY_REG03);
+
+		val = readl(base + USB2_PHY0_REG0B);
+		val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0B);
+
+		val = readl(base + USB2_PHY0_REG0A);
+		val |= (PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0A);
+
+		/* USB3 phy suspend */
+		val = readl(base + PUPHY_REG06);
+		val &= ~PUPHY_REG06_SUSPEND_MASK;
+		if (dwc3_is_superspeed_plus())
+			val |= PUPHY_REG06_SUSPEND_VAL2;
+		else
+			val |= PUPHY_REG06_SUSPEND_VAL;
+		writel(val, base + PUPHY_REG06);
+	} else {
+		val = readl(base + PUPHY_REG03);
+		val |= (0x1 << 11);
+		writel(val, base + PUPHY_REG03);
+
+		/* disable dp/dm pull override */
+		val = readl(base + USB2_PHY0_REG29);
+		val &= ~PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + USB2_PHY0_REG29);
+
+		val = readl(base + USB2_PHY0_REG04);
+		val &= ~(0xff << 8);
+		val |= (0x12 << 8);
+		writel(val, base + USB2_PHY0_REG04);
+
+		/* USB3 phy resume */
+		val = readl(base + PUPHY_REG06);
+		val &= ~PUPHY_REG06_SUSPEND_MASK;
+#if 0
+		if (cpu_is_asr1901() || cpu_is_asr1906())
+			/* override/cfg term to avoid drop to HS/SS from SSP */
+			val |= (0x3 << 8);
+#endif
+		writel(val, base + PUPHY_REG06);
+
+		val = readl(base + USB2_PHY0_REG0A);
+		val &= ~(PHY_SUSPEND_PLL | PHY_SUSPEND_PHY);
+		writel(val, base + USB2_PHY0_REG0A);
+
+		if (cpu_is_asr1901() || cpu_is_asr1906()) {
+			ret = pxa_usb_extern_call(PXA_USB_DEV_OTG, typec, get_typec_dir, &typec_dir);
+			if (ret) {
+				/* with gpio typec dir */
+				pr_info("!!!!!!!get_typec_dir failed\n");
+				writel((readl(base + PUPHY_REG08) & (~((0x1<<1)))), base + PUPHY_REG08);
+			} else {
+				pr_info("sw set typec dir: %d\n", typec_dir);
+				/* sw write typec dir */
+				val = readl(base + PUPHY_REG08);
+				val |= (0x1 << 1);
+				if (typec_dir == USB_TYPEC_DIR_CC2)
+					val |= (0x1 << 2);
+				else
+					val &= ~(0x1 << 2);
+				writel(val, base + PUPHY_REG08);
+			}
+
+			usb31_rterm_cal_porta(base);
+		}
+	}
+
+	pr_info("USB3 PHY set_suspend2 done\n");
+	return 0;
+}
+
+static void asr1906_rterm_cal_porta(void __iomem *base)
+{
+	u32 regval;
+	int timeout;
+	bool rc_restared = false;
+	void __iomem *apbs_base = regs_addr_get_va(REGS_ADDR_APBS);
+
+do_reterm_cal:
+	pr_info("RTERM Test for ASR1906\n");
+	if (!(readl(base + PUPHY_REG20) & (0x1 << 24))) {
+		pr_info("skip lane0 rterm calibration: 0x%x\n", readl(base + PUPHY_REG20));
+		return;
+	}
+
+	timeout = 100;
+	while (timeout--) {
+		regval = readl(base + PUPHY_REG21);
+		if ((regval & (0x3 << 16)) == (0x3 << 16))
+			goto latch_out;
+
+		udelay(1);
+	}
+	if (timeout <= 0) {
+		pr_err("usbphy rterm cal failed\n");
+		if (!rc_restared) {
+			regval = readl(apbs_base + APB_SPARE_REG24);
+			regval &= ~0x1;
+			writel(regval, apbs_base + APB_SPARE_REG24);
+
+			regval = readl(apbs_base + APB_SPARE_REG24);
+			regval |= 0x1;
+			writel(regval, apbs_base + APB_SPARE_REG24);
+			udelay(100);
+			pr_err("apbs-reg24: 0x%x\n", readl(apbs_base + APB_SPARE_REG24));
+			rc_restared = true;
+			goto do_reterm_cal;
+		} else {
+			BUG();
+		}
+	}
+
+latch_out:
+	/* latch rterm value */
+	writel((0x1 << 20), base + PUPHY_REG11);
+	pr_info("PUPHY21: 0x%x PUPHY11: 0x%x\n",
+		readl(base + PUPHY_REG21), readl(base + PUPHY_REG11));
+}
+
+static void asr1901_a0_rterm_cal_porta(void __iomem *base)
+{
+	u32 regval, regval2;
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+
+	pr_info("RTERM Test for ASR1901 a0+\n");
+	if (!(readl(base + PUPHY_REG20) & (0x1 << 24))) {
+		pr_info("skip lane0 rterm calibration: 0x%x\n", readl(base + PUPHY_REG20));
+		return;
+	}
+
+	/* pll_reg2 set to 0xC0 */
+	writel(((readl(base + PUPHY_REG16) & 0xFFFF00FF) | 0xC000), base + PUPHY_REG16);
+
+	/* pll_reg7[5] of lane0, disable select refclk_100_n/p 100Mhz input */
+	writel((readl(base + PUPHY_REG17) & (~(0x1<<21))), base + PUPHY_REG17);
+
+	regval = readl(base + PUPHY_REG11);
+	writel((regval & 0xFFFF0000) | (0x13CB), base + PUPHY_REG11);
+
+	usb31_rterm_cal_value = regval = readl(apmu_base + PMUA_RTERM_CAL_REG);
+	if ((regval & (0x1 << 24)) != 0) {
+		pr_info("USB31 RTERM Done: 0x%x\n", regval);
+	} else {
+		regval = 0xCB00;	/* default manu rterm value */
+		pr_info("!!!set manu reterm: 0x%x\n", regval);
+	}
+
+	regval2 = readl(base + PUPHY_REG11);
+	writel(((regval2 & 0xFFFFFF00) | ((regval & 0xFF00) >> 8)), base + PUPHY_REG11);
+
+	/* set force_rc_calib */
+	writel(readl(base + PUPHY_REG1E) | (0x1 << 1), base + PUPHY_REG1E);
+#if 0
+	/* override/cfg term to avoid drop to HS/SS from SSP */
+	writel((readl(base + PUPHY_REG06) | (0x3<<8)), base + PUPHY_REG06);
+#endif
+}
+
+static void usb31_rterm_cal_porta(void __iomem *base)
+{
+	u32 regval, regval2;
+	int timeout = 1000000 / 5;
+
+	if (cpu_is_asr1901_a0_plus()) {
+		asr1901_a0_rterm_cal_porta(base);
+		return;
+	}
+
+	if (cpu_is_asr1906()) {
+		asr1906_rterm_cal_porta(base);
+		return;
+	}
+
+	pr_info("RTERM Test for USB PortA\n");
+	/* Override mpu_u3 to 0 */
+	writel((readl(base + PUPHY_REG08) | (0x1<<1)), base + PUPHY_REG08);
+
+	/* pll_reg2 set to 0xC0 */
+	writel(((readl(base + PUPHY_REG16) & 0xFFFF00FF) | 0xC000), base + PUPHY_REG16);
+
+	/* pll_reg7[5] of lane0, disable select refclk_100_n/p 100Mhz input */
+	writel((readl(base + PUPHY_REG17) & (~(0x1<<21))), base + PUPHY_REG17);
+
+	while (timeout--) {
+		udelay(5);
+		regval = readl(base + PUPHY_REG21);
+		/* read until readonly_reg3[0] == 1 */
+	    if ((regval & (0x1 << 24)) != 0) {
+			pr_info("USB31 RTERM Done\n");
+			udelay(5);
+			regval = readl(base + PUPHY_REG21);
+			regval2 = readl(base + PUPHY_REG11);
+			writel(((regval2 & 0xFFFFFF00) | ((regval & 0xFF00) >> 8)), base + PUPHY_REG11);
+			/* set force_rc_calib */
+			writel(readl(base + PUPHY_REG1E) | (0x1 << 1), base + PUPHY_REG1E);
+	       	break;
+	    }
+	}
+	if (timeout <= 0) {
+		pr_info("pu_test_info is 0x%x\n", readl(base + PUPHY_REG21));
+		WARN(1, "USB31 rterm cal timeout");
+	}
+	usb31_rterm_cal_value = readl(base + PUPHY_REG21);
+
+	/* Cancle mpu_u3 override */
+	writel((readl(base + PUPHY_REG08) & (~((0x1<<1)))), base + PUPHY_REG08);
+#if 0
+	/* override/cfg term to avoid drop to HS/SS from SSP */
+	writel((readl(base + PUPHY_REG06) | (0x3<<8)), base + PUPHY_REG06);
+#endif
+}
+
+static void usb31_rterm_cal_portb(void __iomem *base, void __iomem *base0)
+{
+	u32 regval, regval2;
+	int timeout = 1000000 / 5;
+	
+	pr_info("RTERM Test for USB PortB\n");
+
+	/* Override mpu_u3 to 0 */
+	writel((readl(base + PUPHY_REG06) | ((0x1<<17)|(0x1<<15))), base + PUPHY_REG06);
+
+	/* pll_reg2 set to 0xC0 */
+	writel(((readl(base0 + PUPHY_REG16) & 0xFFFF00FF) | 0xC000), base0 + PUPHY_REG16);
+
+	/* pll_reg7[5] of lane0, disable select refclk_100_n/p 100Mhz input */
+	writel((readl(base0 + PUPHY_REG17) & (~(0x1<<21))), base0 + PUPHY_REG17);
+
+	regval = readl(base + PUPHY_REG11);
+	if (cpu_is_asr1901_z1())
+		/* write rterm_cal_reg1 to 0x1399 */
+		writel((regval & 0xFFFF0000) | (0x1399), base + PUPHY_REG11);
+	else
+		/* write rterm_cal_reg1 to 0x1387 */
+		writel((regval & 0xFFFF0000) | (0x1387), base + PUPHY_REG11);
+
+	while (timeout--) {
+		udelay(5);
+		regval = readl(base0 + PUPHY_REG21);
+		/* read until readonly_reg3[0] == 1 */
+	    if ((regval & (0x1 << 24)) != 0) {
+			pr_info("USB31 RTERM Done\n");
+			udelay(5);
+			regval = readl(base0 + PUPHY_REG21);
+			regval2 = readl(base + PUPHY_REG11);
+			writel(((regval2 & 0xFFFFFF00) | ((regval & 0xFF00) >> 8)), base + PUPHY_REG11);
+			/* set force_rc_calib */
+			writel(readl(base + PUPHY_REG1E) | (0x1 << 1), base + PUPHY_REG1E);
+	       	break;
+	    }
+	}
+	if (timeout <= 0) {
+		pr_info("pu_test_info is 0x%x\n", readl(base0 + PUPHY_REG21));
+		WARN(1, "USB31 rterm cal timeout");
+	}
+
+	/* Cancle mpu_u3 override */
+	writel((readl(base + PUPHY_REG06) & (~((0x1<<17)|(0x1<<15)))), base + PUPHY_REG06);
+
+#if 0
+	/* override/cfg term to avoid drop to HS/SS from SSP */
+	writel((readl(base + PUPHY_REG06) | (0x3<<8)), base + PUPHY_REG06);
+#endif
+}
+
+static int asr_usb3_phy_dumpcfg(struct usb_phy *phy)
+{
+	struct asr_usb3_phy *asr_usb3_phy;
+	void __iomem *base;
+
+	asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+	base = asr_usb3_phy->base;
+
+	if (!cpu_is_asr1903()) {
+		pr_err_ratelimited("dwc3 phy: %08x: %08x | %08x: %08x | %08x: %08x\n",
+			(u32)(base + PUPHY_REG04), readl(base + PUPHY_REG04),
+			(u32)(base + PUPHY_REG20), readl(base + PUPHY_REG20),
+			(u32)(base + PUPHY_REG21), readl(base + PUPHY_REG21));
+	}
+	pr_err_ratelimited("dwc3 phy: %08x: %08x\n", (u32)(base + USB2_PHY0_REG0E), readl(base + USB2_PHY0_REG0E));
+	return 0;
+}
+
+static void usb30_phy_init(void __iomem *base)
+{
+	u32 pll_wait_us = 400;
+	u32 val;
+
+	if (cpu_is_asr1903())
+		goto usb2_init;
+
+	/* USB PHY config
+	* step.1
+	* config phphy, sw init done, ref clk cfg=2
+	* open allclk en bits
+	*/
+	writel(0x97C, base + PUPHY_REG02);
+
+	/* step.2 wait for PUPHY PLL READY */
+	while((readl(base + PUPHY_REG02) & 0x1) != 0x1)
+	{
+		udelay(1);
+		pll_wait_us--;
+		if (pll_wait_us == 0)
+			break;
+	}
+
+	/* step.3 override pipe_phystatus to 0 if puphy pll not ready */
+	if (pll_wait_us == 0) {
+		dwc3_force_usb2_mode();
+		writel((readl(base + PUPHY_REG10) | (0x1 << 10)), base + PUPHY_REG10);
+	}
+
+	/* update for mode_2x and ls_mode */
+	if (cpu_is_asr1828() && (!cpu_is_asr1828_a0())) {
+		val = readl(base + PUPHY_REG10);
+		val &= ~(0x1 << 8);
+		writel(val, (base + PUPHY_REG10));
+
+		val = readl(base + PUPHY_REG13);
+		val &= ~(0x1 << 11);
+		writel(val, (base + PUPHY_REG13));
+	}
+	/* Write 1s to clear phy err status */
+	writel(0xFFFFFFFF, base + PUPHY_REG04);
+
+usb2_init:
+	/* step.4 wait for USB2 PHY PLL READY */
+	pll_wait_us = 400;
+	while (((readl(base + USB2_PHY0_REG01) & USB2_PLL_BIT_RDY) != USB2_PLL_BIT_RDY)
+		&& (pll_wait_us--))
+	{
+		udelay(1);
+	}
+
+	/* 
+	* Step 5&6
+	* Release usb2 phy internal reset and enable clock gating
+	*/
+	writel(0x60ef, base + USB2_PHY0_REG01);
+	writel(0x1C, base + USB2_PHY0_REG0D);
+
+	/*
+	* STEP.7
+	* 0x1: serial mode, 0x0: parallel mode
+	* USB2_PHY0_REG06	(USB2_BASE+0x18)
+	*
+	* serial mode
+	* writel((readl(base + USB2_PHY0_REG06) | 0x1), base + USB2_PHY0_REG06);
+	* parallel mode
+	*
+	* set to serial mode for stability
+	*/
+	writel(readl(base + USB2_PHY0_REG06) | (0x1), base + USB2_PHY0_REG06);
+
+	/* increase tx dac by 10% */
+	/* writel((readl(base + USB2_PHY0_REG29) & (~0x1F)) | (0x1B), base + USB2_PHY0_REG29); */
+
+	/* Write 1s to clear phy err status */
+	writel(0xFFFFFFFF, base + USB2_PHY0_REG0E);
+}
+
+static void usb31_phy_init(void __iomem *base)
+{
+	u32 val;
+	u32 pll_wait_us = 400;
+
+	/*
+	* CONFIG PLL, default puphy clk is 100M clk for pcie
+	* should be config to usb 38.4M
+	*/
+	val = readl(base + PUPHY_REG16);
+	val &= 0xFFFF0FFF;
+	val |= (0x6 << 13); /* 38.4M */
+	writel(val, base + PUPHY_REG16);
+
+	val = readl(base + PUPHY_REG17);
+	val &= (~(0x1 << 21));
+	writel(val, base + PUPHY_REG17);
+
+	val = readl(base + PUPHY_REG03);
+	val |= (0x3 << 8);
+	writel(val, base + PUPHY_REG03);
+
+	if (cpu_is_asr1906())
+		val = 0x18C;
+	else
+		val = 0x18B;
+	writel(val, base + PUPHY_REG1B);
+
+	writel(0x8014, base + PUPHY_REG07);
+#if 0
+	val = readl(base + PUPHY_REG07);
+	val &= ~(0x3ff<<2);
+	val |= ((512 & 0x3ff) << 2);
+	writel(val, base + PUPHY_REG07);
+#endif
+
+	val = readl(base + PUPHY_REG03);
+	val |= (0x1 << 2);
+	writel(val, base + PUPHY_REG03);
+
+	val = 0x97dfdf30;
+	writel(val, base + PUPHY_REG18);
+
+	val = readl(base + PUPHY_REG19);
+	val &= ~(0x1 << 5 | 0x1 << 6);
+	writel(val, base + PUPHY_REG19);
+
+
+	/* USB PHY config
+	* step.1
+	* config phphy, sw init done, ref clk cfg=2
+	* open allclk en bits
+	*/
+	writel(0xB7C, base + PUPHY_REG02);
+
+	/* step.2 wait for PUPHY PLL READY */
+	while((readl(base + PUPHY_REG02) & 0x1) != 0x1)
+	{
+		udelay(1);
+		pll_wait_us--;
+		if (pll_wait_us == 0)
+			break;
+	}
+
+	/* step.3 override pipe_phystatus to 0 if puphy pll not ready */
+	if (pll_wait_us == 0) {
+		dwc3_force_usb2_mode();
+		writel((readl(base + PUPHY_REG10) | (0x1 << 10)), base + PUPHY_REG10);
+	}
+	
+	/* settings for u1/u2/u3 */
+	if (!cpu_is_asr1901_z1()) {
+		val = readl(base + PUPHY_REG15);
+		val &= ~(0xF << 16);
+		val |= (0x3 << 16); /*  */
+		writel(val, base + PUPHY_REG15);
+
+		val = readl(base + PUPHY_REG07);
+		val &= ~(0x3ff << 2);
+		val |= (200 << 2); /*  */
+		writel(val, base + PUPHY_REG07);
+			
+		if (cpu_is_asr1901_z2()) {		
+			val = readl(base + PUPHY_REG2D);
+			val &= ~(0x7 << 22);
+			val |= (0X7 << 22); /* tx swing */
+			writel(val, base + PUPHY_REG2D);
+		} else if (cpu_is_asr1901_a0_plus()) {
+			val = readl(base + PUPHY_REG2D);
+			val &= ~(0x1ff << 16);
+			val |= (0X1C0 << 16); /* old rxeq value */
+			writel(val, base + PUPHY_REG2D);
+		} else if (cpu_is_asr1906()) {
+			val = readl(base + PUPHY_REG2D);
+			val &= ~(0x1ff << 16);
+			val |= (0X1f6 << 16); /* rxeq */
+			writel(val, base + PUPHY_REG2D);
+		} else {
+			val = readl(base + PUPHY_REG2D);
+			val &= ~(0x1ff << 16);
+			val |= (0X1f6 << 16); /* rxeq */
+			writel(val, base + PUPHY_REG2D);
+		}
+	}
+	/* Write 1s to clear phy err status */
+	writel(0xFFFFFFFF, base + PUPHY_REG04);
+
+	/* step.4 wait for USB2 PHY PLL READY */
+	pll_wait_us = 400;
+	while (((readl(base + USB2_PHY0_REG01) & USB2_PLL_BIT_RDY) != USB2_PLL_BIT_RDY)
+		&& (pll_wait_us--))
+	{
+		udelay(1);
+	}
+
+	/*
+	* Step 5&6
+	* Release usb2 phy internal reset and enable clock gating
+	*/
+	writel(0x60ef, base + USB2_PHY0_REG01);
+	writel(0x1C, base + USB2_PHY0_REG0D);
+
+	/* STEP.7
+	* 0x1: serial mode, 0x0: parallel mode
+	* USB2_PHY0_REG06	(USB2_BASE+0x18)
+	*
+	* serial mode
+	* writel((readl(base + USB2_PHY0_REG06) | 0x1), base + USB2_PHY0_REG06);
+	* parallel mode
+	*/
+	writel((readl(base + USB2_PHY0_REG06) & (~0x1)), base + USB2_PHY0_REG06);
+
+	/* Write 1s to clear phy err status */
+	writel(0xFFFFFFFF, base + USB2_PHY0_REG0E);
+}
+
+static int asr_usb3_phy_preinit(struct asr_usb3_phy *asr_phy)
+{
+	unsigned int loops = 400;
+	void __iomem *base = asr_phy->base;
+	unsigned int val;
+
+	pr_info("%s\n", __func__);
+	/* WAIT FOR PHY PLL RDY */
+	loops = 400;
+	while (((readl(base + USB2_PHY0_REG01) & USB2_PLL_BIT_RDY) != USB2_PLL_BIT_RDY)
+		&& (loops--))
+	{
+		udelay(1);
+	}
+	if (loops == 0)
+		pr_err("!!!!!!!!!!phy pll not ready after 400us\n");
+
+	/* pulldown dp and pulldown dm */
+	val = readl(base + USB2_PHY0_REG25);
+	val &= ~PHY_USB2_DCP_DET_PULL_MASK;
+	val |= PHY_USB2_DCP_DET_PULL_DOWN_DOWN;
+	writel(val, base + USB2_PHY0_REG25);
+
+	val = readl(base + USB2_PHY0_REG29);
+	val |= PHY_USB2_DRV_PULLDOWN_OVERRIDE_EN;
+	writel(val, base + USB2_PHY0_REG29);
+
+	/* for dwc3_get_line_status */
+	val = readl(base + USB2_PHY0_REG04);
+	val &= ~(0xff << 8);
+	val |= (0x12 << 8);
+	writel(val, base + USB2_PHY0_REG04);
+
+	return 0;
+}
+
+static int asr_usb3_phy_init(struct usb_phy *phy)
+{
+	struct asr_usb3_phy *asr_usb3_phy;
+	void __iomem *base;
+	u32 val;
+
+	pr_info("USB3 PHY init start...\n");
+	/* enable usb3 phy */
+	asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+
+	if (asr_usb3_phy->clk)
+		clk_prepare_enable(asr_usb3_phy->clk);
+
+	base = asr_usb3_phy->base;
+	if (cpu_is_asr1901() || cpu_is_asr1906()) {
+		/* port B */
+		if (usb3_phy_res_size == (USB_PHY_PORTB_OFFSET + 0x1000)) {
+			usb31_rterm_cal_portb((base + USB_PHY_PORTB_OFFSET), base);
+			usb31_phy_init(base + USB_PHY_PORTB_OFFSET);
+		} else { /* port A */
+			val = readl(base + PUPHY_REG08);
+			val |= 0x1;
+			writel(val, base + PUPHY_REG08);
+
+			/* pll_reg2 set to 0xC0 */
+			writel(((readl(base + PUPHY_REG16) & 0xFFFF00FF) | 0xC000), base + PUPHY_REG16);
+
+			/* pll_reg7[5] of lane0, disable select refclk_100_n/p 100Mhz input */
+			writel((readl(base + PUPHY_REG17) & (~(0x1<<21))), base + PUPHY_REG17);
+
+			usb31_phy_init(base);
+		}
+	} else {
+		usb30_phy_init(base);
+	}
+
+	pr_info("USB3 PHY init done...\n");
+	return 0;
+}
+
+void asr_usb3_phy_shutdown(struct usb_phy *phy)
+{
+	struct asr_usb3_phy *asr_usb3_phy;
+	void __iomem *base;
+
+	asr_usb3_phy = container_of(phy, struct asr_usb3_phy, phy);
+	base = asr_usb3_phy->base;
+	pr_info("USB3 PHY shutdown ...\n");
+	writel(0x60ef, base + USB2_PHY0_REG01);
+
+	if (asr_usb3_phy->clk)
+		clk_disable_unprepare(asr_usb3_phy->clk);
+	pr_info("USB3 PHY shutdown done\n");
+}
+
+static int asr_usb3_phy_probe(struct platform_device *pdev)
+{
+	struct asr_usb3_phy *mv_phy;
+	struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int ret;
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "failed to get platform data\n");
+		/* return -ENODEV; */
+	}
+
+	mv_phy = devm_kzalloc(dev, sizeof(*mv_phy), GFP_KERNEL);
+	if (!mv_phy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mv_phy->base = ioremap(res->start, resource_size(res));
+	if (IS_ERR(mv_phy->base))
+		return PTR_ERR(mv_phy->base);
+	usb3_phy_res_size = resource_size(res);
+	mv_phy->pdata = pdata;
+
+	mv_phy->phy.dev = &pdev->dev;
+	mv_phy->phy.label = "asr-usb3-phy";
+	mv_phy->phy.init = asr_usb3_phy_init;
+	mv_phy->phy.shutdown = asr_usb3_phy_shutdown;
+	mv_phy->phy.get_vbus = asr_usb_get_vbus;
+	mv_phy->phy.set_suspend = asr_usb3_phy_suspend;
+	mv_phy->phy.set_suspend2 = asr_usb3_phy_suspend2;
+	mv_phy->phy.charger_detect = asr_usb_phy_charger_detect;
+	mv_phy->phy.dump_cfg = asr_usb3_phy_dumpcfg;
+	mv_phy->phy.phy_private2 = asr_usb_host_phy_private;
+
+	ret = usb_add_phy(&mv_phy->phy, USB_PHY_TYPE_USB3);
+	if (ret)
+		goto err;
+
+	mv_phy->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mv_phy->clk))
+		return PTR_ERR(mv_phy->clk);
+
+	clk_prepare_enable(mv_phy->clk);
+	asr_usb3_phy_preinit(mv_phy);
+
+	platform_set_drvdata(pdev, mv_phy);
+
+	dev_info(&pdev->dev, "Initialized ASR USB 3.0 PHY\n");
+err:
+	return ret;
+}
+
+static int asr_usb3_phy_remove(struct platform_device *pdev)
+{
+	struct asr_usb3_phy *asr_usb3_phy = platform_get_drvdata(pdev);
+
+	usb_remove_phy(&asr_usb3_phy->phy);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id asr_usb3_phy_id_table[] = {
+	{ .compatible = "asr,asr-usb3-phy" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, asr_usb3_phy_id_table);
+#endif
+
+static struct platform_driver asr_usb3_phy_driver = {
+	.probe		= asr_usb3_phy_probe,
+	.remove		= asr_usb3_phy_remove,
+	.driver		= {
+		.name	= "asr-usb3-phy",
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_OF
+		.of_match_table = of_match_ptr(asr_usb3_phy_id_table),
+#endif
+	},
+};
+
+static int __init asr_usb3_phydrv_init(void)
+{
+	return platform_driver_register(&asr_usb3_phy_driver);
+}
+
+static void __exit asr_usb3_phydrv_exit(void)
+{
+	platform_driver_unregister(&asr_usb3_phy_driver);
+}
+
+arch_initcall(asr_usb3_phydrv_init);
+module_exit(asr_usb3_phydrv_exit);
+
+MODULE_DESCRIPTION("ASR USB 3.0/3.1 PHY controller");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:asr-usb3-phy");
diff --git a/marvell/linux/drivers/usb/phy/phy-fsl-usb.c b/marvell/linux/drivers/usb/phy/phy-fsl-usb.c
new file mode 100644
index 0000000..446c7bf
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-fsl-usb.c
@@ -0,0 +1,1180 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2007,2008 Freescale semiconductor, Inc.
+ *
+ * Author: Li Yang <LeoLi@freescale.com>
+ *         Jerry Huang <Chang-Ming.Huang@freescale.com>
+ *
+ * Initialization based on code from Shlomi Gridish.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+#include <linux/time.h>
+#include <linux/fsl_devices.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+
+#include <asm/unaligned.h>
+
+#include "phy-fsl-usb.h"
+
+#ifdef VERBOSE
+#define VDBG(fmt, args...) pr_debug("[%s]  " fmt, \
+				 __func__, ## args)
+#else
+#define VDBG(stuff...)	do {} while (0)
+#endif
+
+#define DRIVER_VERSION "Rev. 1.55"
+#define DRIVER_AUTHOR "Jerry Huang/Li Yang"
+#define DRIVER_DESC "Freescale USB OTG Transceiver Driver"
+#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION
+
+static const char driver_name[] = "fsl-usb2-otg";
+
+const pm_message_t otg_suspend_state = {
+	.event = 1,
+};
+
+#define HA_DATA_PULSE
+
+static struct usb_dr_mmap *usb_dr_regs;
+static struct fsl_otg *fsl_otg_dev;
+static int srp_wait_done;
+
+/* FSM timers */
+struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr,
+	*b_ase0_brst_tmr, *b_se0_srp_tmr;
+
+/* Driver specific timers */
+struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr,
+	*b_srp_wait_tmr, *a_wait_enum_tmr;
+
+static struct list_head active_timers;
+
+static const struct fsl_otg_config fsl_otg_initdata = {
+	.otg_port = 1,
+};
+
+#ifdef CONFIG_PPC32
+static u32 _fsl_readl_be(const unsigned __iomem *p)
+{
+	return in_be32(p);
+}
+
+static u32 _fsl_readl_le(const unsigned __iomem *p)
+{
+	return in_le32(p);
+}
+
+static void _fsl_writel_be(u32 v, unsigned __iomem *p)
+{
+	out_be32(p, v);
+}
+
+static void _fsl_writel_le(u32 v, unsigned __iomem *p)
+{
+	out_le32(p, v);
+}
+
+static u32 (*_fsl_readl)(const unsigned __iomem *p);
+static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
+
+#define fsl_readl(p)		(*_fsl_readl)((p))
+#define fsl_writel(v, p)	(*_fsl_writel)((v), (p))
+
+#else
+#define fsl_readl(addr)		readl(addr)
+#define fsl_writel(val, addr)	writel(val, addr)
+#endif /* CONFIG_PPC32 */
+
+int write_ulpi(u8 addr, u8 data)
+{
+	u32 temp;
+
+	temp = 0x60000000 | (addr << 16) | data;
+	fsl_writel(temp, &usb_dr_regs->ulpiview);
+	return 0;
+}
+
+/* -------------------------------------------------------------*/
+/* Operations that will be called from OTG Finite State Machine */
+
+/* Charge vbus for vbus pulsing in SRP */
+void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on)
+{
+	u32 tmp;
+
+	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
+
+	if (on)
+		/* stop discharging, start charging */
+		tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) |
+			OTGSC_CTRL_VBUS_CHARGE;
+	else
+		/* stop charging */
+		tmp &= ~OTGSC_CTRL_VBUS_CHARGE;
+
+	fsl_writel(tmp, &usb_dr_regs->otgsc);
+}
+
+/* Discharge vbus through a resistor to ground */
+void fsl_otg_dischrg_vbus(int on)
+{
+	u32 tmp;
+
+	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
+
+	if (on)
+		/* stop charging, start discharging */
+		tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) |
+			OTGSC_CTRL_VBUS_DISCHARGE;
+	else
+		/* stop discharging */
+		tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE;
+
+	fsl_writel(tmp, &usb_dr_regs->otgsc);
+}
+
+/* A-device driver vbus, controlled through PP bit in PORTSC */
+void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on)
+{
+	u32 tmp;
+
+	if (on) {
+		tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS;
+		fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc);
+	} else {
+		tmp = fsl_readl(&usb_dr_regs->portsc) &
+		      ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER;
+		fsl_writel(tmp, &usb_dr_regs->portsc);
+	}
+}
+
+/*
+ * Pull-up D+, signalling connect by periperal. Also used in
+ * data-line pulsing in SRP
+ */
+void fsl_otg_loc_conn(struct otg_fsm *fsm, int on)
+{
+	u32 tmp;
+
+	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
+
+	if (on)
+		tmp |= OTGSC_CTRL_DATA_PULSING;
+	else
+		tmp &= ~OTGSC_CTRL_DATA_PULSING;
+
+	fsl_writel(tmp, &usb_dr_regs->otgsc);
+}
+
+/*
+ * Generate SOF by host.  This is controlled through suspend/resume the
+ * port.  In host mode, controller will automatically send SOF.
+ * Suspend will block the data on the port.
+ */
+void fsl_otg_loc_sof(struct otg_fsm *fsm, int on)
+{
+	u32 tmp;
+
+	tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS;
+	if (on)
+		tmp |= PORTSC_PORT_FORCE_RESUME;
+	else
+		tmp |= PORTSC_PORT_SUSPEND;
+
+	fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc);
+
+}
+
+/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */
+void fsl_otg_start_pulse(struct otg_fsm *fsm)
+{
+	u32 tmp;
+
+	srp_wait_done = 0;
+#ifdef HA_DATA_PULSE
+	tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK;
+	tmp |= OTGSC_HA_DATA_PULSE;
+	fsl_writel(tmp, &usb_dr_regs->otgsc);
+#else
+	fsl_otg_loc_conn(1);
+#endif
+
+	fsl_otg_add_timer(fsm, b_data_pulse_tmr);
+}
+
+void b_data_pulse_end(unsigned long foo)
+{
+#ifdef HA_DATA_PULSE
+#else
+	fsl_otg_loc_conn(0);
+#endif
+
+	/* Do VBUS pulse after data pulse */
+	fsl_otg_pulse_vbus();
+}
+
+void fsl_otg_pulse_vbus(void)
+{
+	srp_wait_done = 0;
+	fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1);
+	/* start the timer to end vbus charge */
+	fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr);
+}
+
+void b_vbus_pulse_end(unsigned long foo)
+{
+	fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0);
+
+	/*
+	 * As USB3300 using the same a_sess_vld and b_sess_vld voltage
+	 * we need to discharge the bus for a while to distinguish
+	 * residual voltage of vbus pulsing and A device pull up
+	 */
+	fsl_otg_dischrg_vbus(1);
+	fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr);
+}
+
+void b_srp_end(unsigned long foo)
+{
+	fsl_otg_dischrg_vbus(0);
+	srp_wait_done = 1;
+
+	if ((fsl_otg_dev->phy.otg->state == OTG_STATE_B_SRP_INIT) &&
+	    fsl_otg_dev->fsm.b_sess_vld)
+		fsl_otg_dev->fsm.b_srp_done = 1;
+}
+
+/*
+ * Workaround for a_host suspending too fast.  When a_bus_req=0,
+ * a_host will start by SRP.  It needs to set b_hnp_enable before
+ * actually suspending to start HNP
+ */
+void a_wait_enum(unsigned long foo)
+{
+	VDBG("a_wait_enum timeout\n");
+	if (!fsl_otg_dev->phy.otg->host->b_hnp_enable)
+		fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr);
+	else
+		otg_statemachine(&fsl_otg_dev->fsm);
+}
+
+/* The timeout callback function to set time out bit */
+void set_tmout(unsigned long indicator)
+{
+	*(int *)indicator = 1;
+}
+
+/* Initialize timers */
+int fsl_otg_init_timers(struct otg_fsm *fsm)
+{
+	/* FSM used timers */
+	a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE,
+				(unsigned long)&fsm->a_wait_vrise_tmout);
+	if (!a_wait_vrise_tmr)
+		return -ENOMEM;
+
+	a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON,
+				(unsigned long)&fsm->a_wait_bcon_tmout);
+	if (!a_wait_bcon_tmr)
+		return -ENOMEM;
+
+	a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS,
+				(unsigned long)&fsm->a_aidl_bdis_tmout);
+	if (!a_aidl_bdis_tmr)
+		return -ENOMEM;
+
+	b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST,
+				(unsigned long)&fsm->b_ase0_brst_tmout);
+	if (!b_ase0_brst_tmr)
+		return -ENOMEM;
+
+	b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP,
+				(unsigned long)&fsm->b_se0_srp);
+	if (!b_se0_srp_tmr)
+		return -ENOMEM;
+
+	b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL,
+				(unsigned long)&fsm->b_srp_done);
+	if (!b_srp_fail_tmr)
+		return -ENOMEM;
+
+	a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10,
+				(unsigned long)&fsm);
+	if (!a_wait_enum_tmr)
+		return -ENOMEM;
+
+	/* device driver used timers */
+	b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0);
+	if (!b_srp_wait_tmr)
+		return -ENOMEM;
+
+	b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end,
+				TB_DATA_PLS, 0);
+	if (!b_data_pulse_tmr)
+		return -ENOMEM;
+
+	b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end,
+				TB_VBUS_PLS, 0);
+	if (!b_vbus_pulse_tmr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/* Uninitialize timers */
+void fsl_otg_uninit_timers(void)
+{
+	/* FSM used timers */
+	kfree(a_wait_vrise_tmr);
+	kfree(a_wait_bcon_tmr);
+	kfree(a_aidl_bdis_tmr);
+	kfree(b_ase0_brst_tmr);
+	kfree(b_se0_srp_tmr);
+	kfree(b_srp_fail_tmr);
+	kfree(a_wait_enum_tmr);
+
+	/* device driver used timers */
+	kfree(b_srp_wait_tmr);
+	kfree(b_data_pulse_tmr);
+	kfree(b_vbus_pulse_tmr);
+}
+
+static struct fsl_otg_timer *fsl_otg_get_timer(enum otg_fsm_timer t)
+{
+	struct fsl_otg_timer *timer;
+
+	/* REVISIT: use array of pointers to timers instead */
+	switch (t) {
+	case A_WAIT_VRISE:
+		timer = a_wait_vrise_tmr;
+		break;
+	case A_WAIT_BCON:
+		timer = a_wait_vrise_tmr;
+		break;
+	case A_AIDL_BDIS:
+		timer = a_wait_vrise_tmr;
+		break;
+	case B_ASE0_BRST:
+		timer = a_wait_vrise_tmr;
+		break;
+	case B_SE0_SRP:
+		timer = a_wait_vrise_tmr;
+		break;
+	case B_SRP_FAIL:
+		timer = a_wait_vrise_tmr;
+		break;
+	case A_WAIT_ENUM:
+		timer = a_wait_vrise_tmr;
+		break;
+	default:
+		timer = NULL;
+	}
+
+	return timer;
+}
+
+/* Add timer to timer list */
+void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer)
+{
+	struct fsl_otg_timer *timer = gtimer;
+	struct fsl_otg_timer *tmp_timer;
+
+	/*
+	 * Check if the timer is already in the active list,
+	 * if so update timer count
+	 */
+	list_for_each_entry(tmp_timer, &active_timers, list)
+	    if (tmp_timer == timer) {
+		timer->count = timer->expires;
+		return;
+	}
+	timer->count = timer->expires;
+	list_add_tail(&timer->list, &active_timers);
+}
+
+static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+{
+	struct fsl_otg_timer *timer;
+
+	timer = fsl_otg_get_timer(t);
+	if (!timer)
+		return;
+
+	fsl_otg_add_timer(fsm, timer);
+}
+
+/* Remove timer from the timer list; clear timeout status */
+void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer)
+{
+	struct fsl_otg_timer *timer = gtimer;
+	struct fsl_otg_timer *tmp_timer, *del_tmp;
+
+	list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list)
+		if (tmp_timer == timer)
+			list_del(&timer->list);
+}
+
+static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+{
+	struct fsl_otg_timer *timer;
+
+	timer = fsl_otg_get_timer(t);
+	if (!timer)
+		return;
+
+	fsl_otg_del_timer(fsm, timer);
+}
+
+/* Reset controller, not reset the bus */
+void otg_reset_controller(void)
+{
+	u32 command;
+
+	command = fsl_readl(&usb_dr_regs->usbcmd);
+	command |= (1 << 1);
+	fsl_writel(command, &usb_dr_regs->usbcmd);
+	while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1))
+		;
+}
+
+/* Call suspend/resume routines in host driver */
+int fsl_otg_start_host(struct otg_fsm *fsm, int on)
+{
+	struct usb_otg *otg = fsm->otg;
+	struct device *dev;
+	struct fsl_otg *otg_dev =
+		container_of(otg->usb_phy, struct fsl_otg, phy);
+	u32 retval = 0;
+
+	if (!otg->host)
+		return -ENODEV;
+	dev = otg->host->controller;
+
+	/*
+	 * Update a_vbus_vld state as a_vbus_vld int is disabled
+	 * in device mode
+	 */
+	fsm->a_vbus_vld =
+		!!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID);
+	if (on) {
+		/* start fsl usb host controller */
+		if (otg_dev->host_working)
+			goto end;
+		else {
+			otg_reset_controller();
+			VDBG("host on......\n");
+			if (dev->driver->pm && dev->driver->pm->resume) {
+				retval = dev->driver->pm->resume(dev);
+				if (fsm->id) {
+					/* default-b */
+					fsl_otg_drv_vbus(fsm, 1);
+					/*
+					 * Workaround: b_host can't driver
+					 * vbus, but PP in PORTSC needs to
+					 * be 1 for host to work.
+					 * So we set drv_vbus bit in
+					 * transceiver to 0 thru ULPI.
+					 */
+					write_ulpi(0x0c, 0x20);
+				}
+			}
+
+			otg_dev->host_working = 1;
+		}
+	} else {
+		/* stop fsl usb host controller */
+		if (!otg_dev->host_working)
+			goto end;
+		else {
+			VDBG("host off......\n");
+			if (dev && dev->driver) {
+				if (dev->driver->pm && dev->driver->pm->suspend)
+					retval = dev->driver->pm->suspend(dev);
+				if (fsm->id)
+					/* default-b */
+					fsl_otg_drv_vbus(fsm, 0);
+			}
+			otg_dev->host_working = 0;
+		}
+	}
+end:
+	return retval;
+}
+
+/*
+ * Call suspend and resume function in udc driver
+ * to stop and start udc driver.
+ */
+int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+	struct usb_otg *otg = fsm->otg;
+	struct device *dev;
+
+	if (!otg->gadget || !otg->gadget->dev.parent)
+		return -ENODEV;
+
+	VDBG("gadget %s\n", on ? "on" : "off");
+	dev = otg->gadget->dev.parent;
+
+	if (on) {
+		if (dev->driver->resume)
+			dev->driver->resume(dev);
+	} else {
+		if (dev->driver->suspend)
+			dev->driver->suspend(dev, otg_suspend_state);
+	}
+
+	return 0;
+}
+
+/*
+ * Called by initialization code of host driver.  Register host controller
+ * to the OTG.  Suspend host for OTG role detection.
+ */
+static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct fsl_otg *otg_dev;
+
+	if (!otg)
+		return -ENODEV;
+
+	otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
+	if (otg_dev != fsl_otg_dev)
+		return -ENODEV;
+
+	otg->host = host;
+
+	otg_dev->fsm.a_bus_drop = 0;
+	otg_dev->fsm.a_bus_req = 1;
+
+	if (host) {
+		VDBG("host off......\n");
+
+		otg->host->otg_port = fsl_otg_initdata.otg_port;
+		otg->host->is_b_host = otg_dev->fsm.id;
+		/*
+		 * must leave time for hub_wq to finish its thing
+		 * before yanking the host driver out from under it,
+		 * so suspend the host after a short delay.
+		 */
+		otg_dev->host_working = 1;
+		schedule_delayed_work(&otg_dev->otg_event, 100);
+		return 0;
+	} else {
+		/* host driver going away */
+		if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) &
+		      OTGSC_STS_USB_ID)) {
+			/* Mini-A cable connected */
+			struct otg_fsm *fsm = &otg_dev->fsm;
+
+			otg->state = OTG_STATE_UNDEFINED;
+			fsm->protocol = PROTO_UNDEF;
+		}
+	}
+
+	otg_dev->host_working = 0;
+
+	otg_statemachine(&otg_dev->fsm);
+
+	return 0;
+}
+
+/* Called by initialization code of udc.  Register udc to OTG. */
+static int fsl_otg_set_peripheral(struct usb_otg *otg,
+					struct usb_gadget *gadget)
+{
+	struct fsl_otg *otg_dev;
+
+	if (!otg)
+		return -ENODEV;
+
+	otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
+	VDBG("otg_dev 0x%x\n", (int)otg_dev);
+	VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev);
+	if (otg_dev != fsl_otg_dev)
+		return -ENODEV;
+
+	if (!gadget) {
+		if (!otg->default_a)
+			otg->gadget->ops->vbus_draw(otg->gadget, 0);
+		usb_gadget_vbus_disconnect(otg->gadget);
+		otg->gadget = 0;
+		otg_dev->fsm.b_bus_req = 0;
+		otg_statemachine(&otg_dev->fsm);
+		return 0;
+	}
+
+	otg->gadget = gadget;
+	otg->gadget->is_a_peripheral = !otg_dev->fsm.id;
+
+	otg_dev->fsm.b_bus_req = 1;
+
+	/* start the gadget right away if the ID pin says Mini-B */
+	pr_debug("ID pin=%d\n", otg_dev->fsm.id);
+	if (otg_dev->fsm.id == 1) {
+		fsl_otg_start_host(&otg_dev->fsm, 0);
+		otg_drv_vbus(&otg_dev->fsm, 0);
+		fsl_otg_start_gadget(&otg_dev->fsm, 1);
+	}
+
+	return 0;
+}
+
+/*
+ * Delayed pin detect interrupt processing.
+ *
+ * When the Mini-A cable is disconnected from the board,
+ * the pin-detect interrupt happens before the disconnect
+ * interrupts for the connected device(s).  In order to
+ * process the disconnect interrupt(s) prior to switching
+ * roles, the pin-detect interrupts are delayed, and handled
+ * by this routine.
+ */
+static void fsl_otg_event(struct work_struct *work)
+{
+	struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work);
+	struct otg_fsm *fsm = &og->fsm;
+
+	if (fsm->id) {		/* switch to gadget */
+		fsl_otg_start_host(fsm, 0);
+		otg_drv_vbus(fsm, 0);
+		fsl_otg_start_gadget(fsm, 1);
+	}
+}
+
+/* B-device start SRP */
+static int fsl_otg_start_srp(struct usb_otg *otg)
+{
+	struct fsl_otg *otg_dev;
+
+	if (!otg || otg->state != OTG_STATE_B_IDLE)
+		return -ENODEV;
+
+	otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
+	if (otg_dev != fsl_otg_dev)
+		return -ENODEV;
+
+	otg_dev->fsm.b_bus_req = 1;
+	otg_statemachine(&otg_dev->fsm);
+
+	return 0;
+}
+
+/* A_host suspend will call this function to start hnp */
+static int fsl_otg_start_hnp(struct usb_otg *otg)
+{
+	struct fsl_otg *otg_dev;
+
+	if (!otg)
+		return -ENODEV;
+
+	otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
+	if (otg_dev != fsl_otg_dev)
+		return -ENODEV;
+
+	pr_debug("start_hnp...\n");
+
+	/* clear a_bus_req to enter a_suspend state */
+	otg_dev->fsm.a_bus_req = 0;
+	otg_statemachine(&otg_dev->fsm);
+
+	return 0;
+}
+
+/*
+ * Interrupt handler.  OTG/host/peripheral share the same int line.
+ * OTG driver clears OTGSC interrupts and leaves USB interrupts
+ * intact.  It needs to have knowledge of some USB interrupts
+ * such as port change.
+ */
+irqreturn_t fsl_otg_isr(int irq, void *dev_id)
+{
+	struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm;
+	struct usb_otg *otg = ((struct fsl_otg *)dev_id)->phy.otg;
+	u32 otg_int_src, otg_sc;
+
+	otg_sc = fsl_readl(&usb_dr_regs->otgsc);
+	otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8);
+
+	/* Only clear otg interrupts */
+	fsl_writel(otg_sc, &usb_dr_regs->otgsc);
+
+	/*FIXME: ID change not generate when init to 0 */
+	fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+	otg->default_a = (fsm->id == 0);
+
+	/* process OTG interrupts */
+	if (otg_int_src) {
+		if (otg_int_src & OTGSC_INTSTS_USB_ID) {
+			fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+			otg->default_a = (fsm->id == 0);
+			/* clear conn information */
+			if (fsm->id)
+				fsm->b_conn = 0;
+			else
+				fsm->a_conn = 0;
+
+			if (otg->host)
+				otg->host->is_b_host = fsm->id;
+			if (otg->gadget)
+				otg->gadget->is_a_peripheral = !fsm->id;
+			VDBG("ID int (ID is %d)\n", fsm->id);
+
+			if (fsm->id) {	/* switch to gadget */
+				schedule_delayed_work(
+					&((struct fsl_otg *)dev_id)->otg_event,
+					100);
+			} else {	/* switch to host */
+				cancel_delayed_work(&
+						    ((struct fsl_otg *)dev_id)->
+						    otg_event);
+				fsl_otg_start_gadget(fsm, 0);
+				otg_drv_vbus(fsm, 1);
+				fsl_otg_start_host(fsm, 1);
+			}
+			return IRQ_HANDLED;
+		}
+	}
+	return IRQ_NONE;
+}
+
+static struct otg_fsm_ops fsl_otg_ops = {
+	.chrg_vbus = fsl_otg_chrg_vbus,
+	.drv_vbus = fsl_otg_drv_vbus,
+	.loc_conn = fsl_otg_loc_conn,
+	.loc_sof = fsl_otg_loc_sof,
+	.start_pulse = fsl_otg_start_pulse,
+
+	.add_timer = fsl_otg_fsm_add_timer,
+	.del_timer = fsl_otg_fsm_del_timer,
+
+	.start_host = fsl_otg_start_host,
+	.start_gadget = fsl_otg_start_gadget,
+};
+
+/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */
+static int fsl_otg_conf(struct platform_device *pdev)
+{
+	struct fsl_otg *fsl_otg_tc;
+	int status;
+
+	if (fsl_otg_dev)
+		return 0;
+
+	/* allocate space to fsl otg device */
+	fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL);
+	if (!fsl_otg_tc)
+		return -ENOMEM;
+
+	fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+	if (!fsl_otg_tc->phy.otg) {
+		kfree(fsl_otg_tc);
+		return -ENOMEM;
+	}
+
+	INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event);
+
+	INIT_LIST_HEAD(&active_timers);
+	status = fsl_otg_init_timers(&fsl_otg_tc->fsm);
+	if (status) {
+		pr_info("Couldn't init OTG timers\n");
+		goto err;
+	}
+	mutex_init(&fsl_otg_tc->fsm.lock);
+
+	/* Set OTG state machine operations */
+	fsl_otg_tc->fsm.ops = &fsl_otg_ops;
+
+	/* initialize the otg structure */
+	fsl_otg_tc->phy.label = DRIVER_DESC;
+	fsl_otg_tc->phy.dev = &pdev->dev;
+
+	fsl_otg_tc->phy.otg->usb_phy = &fsl_otg_tc->phy;
+	fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host;
+	fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral;
+	fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp;
+	fsl_otg_tc->phy.otg->start_srp = fsl_otg_start_srp;
+
+	fsl_otg_dev = fsl_otg_tc;
+
+	/* Store the otg transceiver */
+	status = usb_add_phy(&fsl_otg_tc->phy, USB_PHY_TYPE_USB2);
+	if (status) {
+		pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n");
+		goto err;
+	}
+
+	return 0;
+err:
+	fsl_otg_uninit_timers();
+	kfree(fsl_otg_tc->phy.otg);
+	kfree(fsl_otg_tc);
+	return status;
+}
+
+/* OTG Initialization */
+int usb_otg_start(struct platform_device *pdev)
+{
+	struct fsl_otg *p_otg;
+	struct usb_phy *otg_trans = usb_get_phy(USB_PHY_TYPE_USB2);
+	struct otg_fsm *fsm;
+	int status;
+	struct resource *res;
+	u32 temp;
+	struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+	p_otg = container_of(otg_trans, struct fsl_otg, phy);
+	fsm = &p_otg->fsm;
+
+	/* Initialize the state machine structure with default values */
+	SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED);
+	fsm->otg = p_otg->phy.otg;
+
+	/* We don't require predefined MEM/IRQ resource index */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENXIO;
+
+	/* We don't request_mem_region here to enable resource sharing
+	 * with host/device */
+
+	usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap));
+	p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs;
+	pdata->regs = (void *)usb_dr_regs;
+
+	if (pdata->init && pdata->init(pdev) != 0)
+		return -EINVAL;
+
+#ifdef CONFIG_PPC32
+	if (pdata->big_endian_mmio) {
+		_fsl_readl = _fsl_readl_be;
+		_fsl_writel = _fsl_writel_be;
+	} else {
+		_fsl_readl = _fsl_readl_le;
+		_fsl_writel = _fsl_writel_le;
+	}
+#endif
+
+	/* request irq */
+	p_otg->irq = platform_get_irq(pdev, 0);
+	if (p_otg->irq < 0)
+		return p_otg->irq;
+	status = request_irq(p_otg->irq, fsl_otg_isr,
+				IRQF_SHARED, driver_name, p_otg);
+	if (status) {
+		dev_dbg(p_otg->phy.dev, "can't get IRQ %d, error %d\n",
+			p_otg->irq, status);
+		iounmap(p_otg->dr_mem_map);
+		kfree(p_otg->phy.otg);
+		kfree(p_otg);
+		return status;
+	}
+
+	/* stop the controller */
+	temp = fsl_readl(&p_otg->dr_mem_map->usbcmd);
+	temp &= ~USB_CMD_RUN_STOP;
+	fsl_writel(temp, &p_otg->dr_mem_map->usbcmd);
+
+	/* reset the controller */
+	temp = fsl_readl(&p_otg->dr_mem_map->usbcmd);
+	temp |= USB_CMD_CTRL_RESET;
+	fsl_writel(temp, &p_otg->dr_mem_map->usbcmd);
+
+	/* wait reset completed */
+	while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET)
+		;
+
+	/* configure the VBUSHS as IDLE(both host and device) */
+	temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0);
+	fsl_writel(temp, &p_otg->dr_mem_map->usbmode);
+
+	/* configure PHY interface */
+	temp = fsl_readl(&p_otg->dr_mem_map->portsc);
+	temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW);
+	switch (pdata->phy_mode) {
+	case FSL_USB2_PHY_ULPI:
+		temp |= PORTSC_PTS_ULPI;
+		break;
+	case FSL_USB2_PHY_UTMI_WIDE:
+		temp |= PORTSC_PTW_16BIT;
+		/* fall through */
+	case FSL_USB2_PHY_UTMI:
+		temp |= PORTSC_PTS_UTMI;
+		/* fall through */
+	default:
+		break;
+	}
+	fsl_writel(temp, &p_otg->dr_mem_map->portsc);
+
+	if (pdata->have_sysif_regs) {
+		/* configure control enable IO output, big endian register */
+		temp = __raw_readl(&p_otg->dr_mem_map->control);
+		temp |= USB_CTRL_IOENB;
+		__raw_writel(temp, &p_otg->dr_mem_map->control);
+	}
+
+	/* disable all interrupt and clear all OTGSC status */
+	temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
+	temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK;
+	temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE;
+	fsl_writel(temp, &p_otg->dr_mem_map->otgsc);
+
+	/*
+	 * The identification (id) input is FALSE when a Mini-A plug is inserted
+	 * in the devices Mini-AB receptacle. Otherwise, this input is TRUE.
+	 * Also: record initial state of ID pin
+	 */
+	if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
+		p_otg->phy.otg->state = OTG_STATE_UNDEFINED;
+		p_otg->fsm.id = 1;
+	} else {
+		p_otg->phy.otg->state = OTG_STATE_A_IDLE;
+		p_otg->fsm.id = 0;
+	}
+
+	pr_debug("initial ID pin=%d\n", p_otg->fsm.id);
+
+	/* enable OTG ID pin interrupt */
+	temp = fsl_readl(&p_otg->dr_mem_map->otgsc);
+	temp |= OTGSC_INTR_USB_ID_EN;
+	temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN);
+	fsl_writel(temp, &p_otg->dr_mem_map->otgsc);
+
+	return 0;
+}
+
+/*
+ * state file in sysfs
+ */
+static ssize_t show_fsl_usb2_otg_state(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct otg_fsm *fsm = &fsl_otg_dev->fsm;
+	char *next = buf;
+	unsigned size = PAGE_SIZE;
+	int t;
+
+	mutex_lock(&fsm->lock);
+
+	/* basic driver infomation */
+	t = scnprintf(next, size,
+			DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n",
+			DRIVER_VERSION);
+	size -= t;
+	next += t;
+
+	/* Registers */
+	t = scnprintf(next, size,
+			"OTGSC:   0x%08x\n"
+			"PORTSC:  0x%08x\n"
+			"USBMODE: 0x%08x\n"
+			"USBCMD:  0x%08x\n"
+			"USBSTS:  0x%08x\n"
+			"USBINTR: 0x%08x\n",
+			fsl_readl(&usb_dr_regs->otgsc),
+			fsl_readl(&usb_dr_regs->portsc),
+			fsl_readl(&usb_dr_regs->usbmode),
+			fsl_readl(&usb_dr_regs->usbcmd),
+			fsl_readl(&usb_dr_regs->usbsts),
+			fsl_readl(&usb_dr_regs->usbintr));
+	size -= t;
+	next += t;
+
+	/* State */
+	t = scnprintf(next, size,
+		      "OTG state: %s\n\n",
+		      usb_otg_state_string(fsl_otg_dev->phy.otg->state));
+	size -= t;
+	next += t;
+
+	/* State Machine Variables */
+	t = scnprintf(next, size,
+			"a_bus_req: %d\n"
+			"b_bus_req: %d\n"
+			"a_bus_resume: %d\n"
+			"a_bus_suspend: %d\n"
+			"a_conn: %d\n"
+			"a_sess_vld: %d\n"
+			"a_srp_det: %d\n"
+			"a_vbus_vld: %d\n"
+			"b_bus_resume: %d\n"
+			"b_bus_suspend: %d\n"
+			"b_conn: %d\n"
+			"b_se0_srp: %d\n"
+			"b_ssend_srp: %d\n"
+			"b_sess_vld: %d\n"
+			"id: %d\n",
+			fsm->a_bus_req,
+			fsm->b_bus_req,
+			fsm->a_bus_resume,
+			fsm->a_bus_suspend,
+			fsm->a_conn,
+			fsm->a_sess_vld,
+			fsm->a_srp_det,
+			fsm->a_vbus_vld,
+			fsm->b_bus_resume,
+			fsm->b_bus_suspend,
+			fsm->b_conn,
+			fsm->b_se0_srp,
+			fsm->b_ssend_srp,
+			fsm->b_sess_vld,
+			fsm->id);
+	size -= t;
+	next += t;
+
+	mutex_unlock(&fsm->lock);
+
+	return PAGE_SIZE - size;
+}
+
+static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL);
+
+static struct attribute *fsl_otg_attrs[] = {
+	&dev_attr_fsl_usb2_otg_state.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(fsl_otg);
+
+/* Char driver interface to control some OTG input */
+
+/*
+ * Handle some ioctl command, such as get otg
+ * status and set host suspend
+ */
+static long fsl_otg_ioctl(struct file *file, unsigned int cmd,
+			  unsigned long arg)
+{
+	u32 retval = 0;
+
+	switch (cmd) {
+	case GET_OTG_STATUS:
+		retval = fsl_otg_dev->host_working;
+		break;
+
+	case SET_A_SUSPEND_REQ:
+		fsl_otg_dev->fsm.a_suspend_req_inf = arg;
+		break;
+
+	case SET_A_BUS_DROP:
+		fsl_otg_dev->fsm.a_bus_drop = arg;
+		break;
+
+	case SET_A_BUS_REQ:
+		fsl_otg_dev->fsm.a_bus_req = arg;
+		break;
+
+	case SET_B_BUS_REQ:
+		fsl_otg_dev->fsm.b_bus_req = arg;
+		break;
+
+	default:
+		break;
+	}
+
+	otg_statemachine(&fsl_otg_dev->fsm);
+
+	return retval;
+}
+
+static int fsl_otg_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int fsl_otg_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static const struct file_operations otg_fops = {
+	.owner = THIS_MODULE,
+	.llseek = NULL,
+	.read = NULL,
+	.write = NULL,
+	.unlocked_ioctl = fsl_otg_ioctl,
+	.open = fsl_otg_open,
+	.release = fsl_otg_release,
+};
+
+static int fsl_otg_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	if (!dev_get_platdata(&pdev->dev))
+		return -ENODEV;
+
+	/* configure the OTG */
+	ret = fsl_otg_conf(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't configure OTG module\n");
+		return ret;
+	}
+
+	/* start OTG */
+	ret = usb_otg_start(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't init FSL OTG device\n");
+		return ret;
+	}
+
+	ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register FSL OTG device\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static int fsl_otg_remove(struct platform_device *pdev)
+{
+	struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+	usb_remove_phy(&fsl_otg_dev->phy);
+	free_irq(fsl_otg_dev->irq, fsl_otg_dev);
+
+	iounmap((void *)usb_dr_regs);
+
+	fsl_otg_uninit_timers();
+	kfree(fsl_otg_dev->phy.otg);
+	kfree(fsl_otg_dev);
+
+	unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME);
+
+	if (pdata->exit)
+		pdata->exit(pdev);
+
+	return 0;
+}
+
+struct platform_driver fsl_otg_driver = {
+	.probe = fsl_otg_probe,
+	.remove = fsl_otg_remove,
+	.driver = {
+		.name = driver_name,
+		.owner = THIS_MODULE,
+		.dev_groups = fsl_otg_groups,
+	},
+};
+
+module_platform_driver(fsl_otg_driver);
+
+MODULE_DESCRIPTION(DRIVER_INFO);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/drivers/usb/phy/phy-fsl-usb.h b/marvell/linux/drivers/usb/phy/phy-fsl-usb.h
new file mode 100644
index 0000000..43d410f
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-fsl-usb.h
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. */
+
+#include <linux/usb/otg-fsm.h>
+#include <linux/usb/otg.h>
+#include <linux/ioctl.h>
+
+/* USB Command Register Bit Masks */
+#define USB_CMD_RUN_STOP		(0x1<<0)
+#define USB_CMD_CTRL_RESET		(0x1<<1)
+#define USB_CMD_PERIODIC_SCHEDULE_EN	(0x1<<4)
+#define USB_CMD_ASYNC_SCHEDULE_EN	(0x1<<5)
+#define USB_CMD_INT_AA_DOORBELL		(0x1<<6)
+#define USB_CMD_ASP			(0x3<<8)
+#define USB_CMD_ASYNC_SCH_PARK_EN	(0x1<<11)
+#define USB_CMD_SUTW			(0x1<<13)
+#define USB_CMD_ATDTW			(0x1<<14)
+#define USB_CMD_ITC			(0xFF<<16)
+
+/* bit 15,3,2 are frame list size */
+#define USB_CMD_FRAME_SIZE_1024		(0x0<<15 | 0x0<<2)
+#define USB_CMD_FRAME_SIZE_512		(0x0<<15 | 0x1<<2)
+#define USB_CMD_FRAME_SIZE_256		(0x0<<15 | 0x2<<2)
+#define USB_CMD_FRAME_SIZE_128		(0x0<<15 | 0x3<<2)
+#define USB_CMD_FRAME_SIZE_64		(0x1<<15 | 0x0<<2)
+#define USB_CMD_FRAME_SIZE_32		(0x1<<15 | 0x1<<2)
+#define USB_CMD_FRAME_SIZE_16		(0x1<<15 | 0x2<<2)
+#define USB_CMD_FRAME_SIZE_8		(0x1<<15 | 0x3<<2)
+
+/* bit 9-8 are async schedule park mode count */
+#define USB_CMD_ASP_00			(0x0<<8)
+#define USB_CMD_ASP_01			(0x1<<8)
+#define USB_CMD_ASP_10			(0x2<<8)
+#define USB_CMD_ASP_11			(0x3<<8)
+#define USB_CMD_ASP_BIT_POS		(8)
+
+/* bit 23-16 are interrupt threshold control */
+#define USB_CMD_ITC_NO_THRESHOLD	(0x00<<16)
+#define USB_CMD_ITC_1_MICRO_FRM		(0x01<<16)
+#define USB_CMD_ITC_2_MICRO_FRM		(0x02<<16)
+#define USB_CMD_ITC_4_MICRO_FRM		(0x04<<16)
+#define USB_CMD_ITC_8_MICRO_FRM		(0x08<<16)
+#define USB_CMD_ITC_16_MICRO_FRM	(0x10<<16)
+#define USB_CMD_ITC_32_MICRO_FRM	(0x20<<16)
+#define USB_CMD_ITC_64_MICRO_FRM	(0x40<<16)
+#define USB_CMD_ITC_BIT_POS		(16)
+
+/* USB Status Register Bit Masks */
+#define USB_STS_INT			(0x1<<0)
+#define USB_STS_ERR			(0x1<<1)
+#define USB_STS_PORT_CHANGE		(0x1<<2)
+#define USB_STS_FRM_LST_ROLL		(0x1<<3)
+#define USB_STS_SYS_ERR			(0x1<<4)
+#define USB_STS_IAA			(0x1<<5)
+#define USB_STS_RESET_RECEIVED		(0x1<<6)
+#define USB_STS_SOF			(0x1<<7)
+#define USB_STS_DCSUSPEND		(0x1<<8)
+#define USB_STS_HC_HALTED		(0x1<<12)
+#define USB_STS_RCL			(0x1<<13)
+#define USB_STS_PERIODIC_SCHEDULE	(0x1<<14)
+#define USB_STS_ASYNC_SCHEDULE		(0x1<<15)
+
+/* USB Interrupt Enable Register Bit Masks */
+#define USB_INTR_INT_EN			(0x1<<0)
+#define USB_INTR_ERR_INT_EN		(0x1<<1)
+#define USB_INTR_PC_DETECT_EN		(0x1<<2)
+#define USB_INTR_FRM_LST_ROLL_EN	(0x1<<3)
+#define USB_INTR_SYS_ERR_EN		(0x1<<4)
+#define USB_INTR_ASYN_ADV_EN		(0x1<<5)
+#define USB_INTR_RESET_EN		(0x1<<6)
+#define USB_INTR_SOF_EN			(0x1<<7)
+#define USB_INTR_DEVICE_SUSPEND		(0x1<<8)
+
+/* Device Address bit masks */
+#define USB_DEVICE_ADDRESS_MASK		(0x7F<<25)
+#define USB_DEVICE_ADDRESS_BIT_POS	(25)
+/* PORTSC  Register Bit Masks,Only one PORT in OTG mode*/
+#define PORTSC_CURRENT_CONNECT_STATUS	(0x1<<0)
+#define PORTSC_CONNECT_STATUS_CHANGE	(0x1<<1)
+#define PORTSC_PORT_ENABLE		(0x1<<2)
+#define PORTSC_PORT_EN_DIS_CHANGE	(0x1<<3)
+#define PORTSC_OVER_CURRENT_ACT		(0x1<<4)
+#define PORTSC_OVER_CUURENT_CHG		(0x1<<5)
+#define PORTSC_PORT_FORCE_RESUME	(0x1<<6)
+#define PORTSC_PORT_SUSPEND		(0x1<<7)
+#define PORTSC_PORT_RESET		(0x1<<8)
+#define PORTSC_LINE_STATUS_BITS		(0x3<<10)
+#define PORTSC_PORT_POWER		(0x1<<12)
+#define PORTSC_PORT_INDICTOR_CTRL	(0x3<<14)
+#define PORTSC_PORT_TEST_CTRL		(0xF<<16)
+#define PORTSC_WAKE_ON_CONNECT_EN	(0x1<<20)
+#define PORTSC_WAKE_ON_CONNECT_DIS	(0x1<<21)
+#define PORTSC_WAKE_ON_OVER_CURRENT	(0x1<<22)
+#define PORTSC_PHY_LOW_POWER_SPD	(0x1<<23)
+#define PORTSC_PORT_FORCE_FULL_SPEED	(0x1<<24)
+#define PORTSC_PORT_SPEED_MASK		(0x3<<26)
+#define PORTSC_TRANSCEIVER_WIDTH	(0x1<<28)
+#define PORTSC_PHY_TYPE_SEL		(0x3<<30)
+/* bit 11-10 are line status */
+#define PORTSC_LINE_STATUS_SE0		(0x0<<10)
+#define PORTSC_LINE_STATUS_JSTATE	(0x1<<10)
+#define PORTSC_LINE_STATUS_KSTATE	(0x2<<10)
+#define PORTSC_LINE_STATUS_UNDEF	(0x3<<10)
+#define PORTSC_LINE_STATUS_BIT_POS	(10)
+
+/* bit 15-14 are port indicator control */
+#define PORTSC_PIC_OFF			(0x0<<14)
+#define PORTSC_PIC_AMBER		(0x1<<14)
+#define PORTSC_PIC_GREEN		(0x2<<14)
+#define PORTSC_PIC_UNDEF		(0x3<<14)
+#define PORTSC_PIC_BIT_POS		(14)
+
+/* bit 19-16 are port test control */
+#define PORTSC_PTC_DISABLE		(0x0<<16)
+#define PORTSC_PTC_JSTATE		(0x1<<16)
+#define PORTSC_PTC_KSTATE		(0x2<<16)
+#define PORTSC_PTC_SEQNAK		(0x3<<16)
+#define PORTSC_PTC_PACKET		(0x4<<16)
+#define PORTSC_PTC_FORCE_EN		(0x5<<16)
+#define PORTSC_PTC_BIT_POS		(16)
+
+/* bit 27-26 are port speed */
+#define PORTSC_PORT_SPEED_FULL		(0x0<<26)
+#define PORTSC_PORT_SPEED_LOW		(0x1<<26)
+#define PORTSC_PORT_SPEED_HIGH		(0x2<<26)
+#define PORTSC_PORT_SPEED_UNDEF		(0x3<<26)
+#define PORTSC_SPEED_BIT_POS		(26)
+
+/* bit 28 is parallel transceiver width for UTMI interface */
+#define PORTSC_PTW			(0x1<<28)
+#define PORTSC_PTW_8BIT			(0x0<<28)
+#define PORTSC_PTW_16BIT		(0x1<<28)
+
+/* bit 31-30 are port transceiver select */
+#define PORTSC_PTS_UTMI			(0x0<<30)
+#define PORTSC_PTS_ULPI			(0x2<<30)
+#define PORTSC_PTS_FSLS_SERIAL		(0x3<<30)
+#define PORTSC_PTS_BIT_POS		(30)
+
+#define PORTSC_W1C_BITS			\
+	(PORTSC_CONNECT_STATUS_CHANGE |	\
+	 PORTSC_PORT_EN_DIS_CHANGE    |	\
+	 PORTSC_OVER_CUURENT_CHG)
+
+/* OTG Status Control Register Bit Masks */
+#define OTGSC_CTRL_VBUS_DISCHARGE	(0x1<<0)
+#define OTGSC_CTRL_VBUS_CHARGE		(0x1<<1)
+#define OTGSC_CTRL_OTG_TERMINATION	(0x1<<3)
+#define OTGSC_CTRL_DATA_PULSING		(0x1<<4)
+#define OTGSC_CTRL_ID_PULL_EN		(0x1<<5)
+#define OTGSC_HA_DATA_PULSE		(0x1<<6)
+#define OTGSC_HA_BA			(0x1<<7)
+#define OTGSC_STS_USB_ID		(0x1<<8)
+#define OTGSC_STS_A_VBUS_VALID		(0x1<<9)
+#define OTGSC_STS_A_SESSION_VALID	(0x1<<10)
+#define OTGSC_STS_B_SESSION_VALID	(0x1<<11)
+#define OTGSC_STS_B_SESSION_END		(0x1<<12)
+#define OTGSC_STS_1MS_TOGGLE		(0x1<<13)
+#define OTGSC_STS_DATA_PULSING		(0x1<<14)
+#define OTGSC_INTSTS_USB_ID		(0x1<<16)
+#define OTGSC_INTSTS_A_VBUS_VALID	(0x1<<17)
+#define OTGSC_INTSTS_A_SESSION_VALID	(0x1<<18)
+#define OTGSC_INTSTS_B_SESSION_VALID	(0x1<<19)
+#define OTGSC_INTSTS_B_SESSION_END	(0x1<<20)
+#define OTGSC_INTSTS_1MS		(0x1<<21)
+#define OTGSC_INTSTS_DATA_PULSING	(0x1<<22)
+#define OTGSC_INTR_USB_ID_EN		(0x1<<24)
+#define OTGSC_INTR_A_VBUS_VALID_EN	(0x1<<25)
+#define OTGSC_INTR_A_SESSION_VALID_EN	(0x1<<26)
+#define OTGSC_INTR_B_SESSION_VALID_EN	(0x1<<27)
+#define OTGSC_INTR_B_SESSION_END_EN	(0x1<<28)
+#define OTGSC_INTR_1MS_TIMER_EN		(0x1<<29)
+#define OTGSC_INTR_DATA_PULSING_EN	(0x1<<30)
+#define OTGSC_INTSTS_MASK		(0x00ff0000)
+
+/* USB MODE Register Bit Masks */
+#define  USB_MODE_CTRL_MODE_IDLE	(0x0<<0)
+#define  USB_MODE_CTRL_MODE_DEVICE	(0x2<<0)
+#define  USB_MODE_CTRL_MODE_HOST	(0x3<<0)
+#define  USB_MODE_CTRL_MODE_RSV		(0x1<<0)
+#define  USB_MODE_SETUP_LOCK_OFF	(0x1<<3)
+#define  USB_MODE_STREAM_DISABLE	(0x1<<4)
+#define  USB_MODE_ES			(0x1<<2) /* Endian Select */
+
+/* control Register Bit Masks */
+#define  USB_CTRL_IOENB			(0x1<<2)
+#define  USB_CTRL_ULPI_INT0EN		(0x1<<0)
+
+/* BCSR5 */
+#define BCSR5_INT_USB			(0x02)
+
+/* USB module clk cfg */
+#define SCCR_OFFS			(0xA08)
+#define SCCR_USB_CLK_DISABLE		(0x00000000)	/* USB clk disable */
+#define SCCR_USB_MPHCM_11		(0x00c00000)
+#define SCCR_USB_MPHCM_01		(0x00400000)
+#define SCCR_USB_MPHCM_10		(0x00800000)
+#define SCCR_USB_DRCM_11		(0x00300000)
+#define SCCR_USB_DRCM_01		(0x00100000)
+#define SCCR_USB_DRCM_10		(0x00200000)
+
+#define SICRL_OFFS			(0x114)
+#define SICRL_USB0			(0x40000000)
+#define SICRL_USB1			(0x20000000)
+
+#define SICRH_OFFS			(0x118)
+#define SICRH_USB_UTMI			(0x00020000)
+
+/* OTG interrupt enable bit masks */
+#define  OTGSC_INTERRUPT_ENABLE_BITS_MASK  \
+	(OTGSC_INTR_USB_ID_EN            | \
+	OTGSC_INTR_1MS_TIMER_EN		 | \
+	OTGSC_INTR_A_VBUS_VALID_EN       | \
+	OTGSC_INTR_A_SESSION_VALID_EN    | \
+	OTGSC_INTR_B_SESSION_VALID_EN    | \
+	OTGSC_INTR_B_SESSION_END_EN      | \
+	OTGSC_INTR_DATA_PULSING_EN)
+
+/* OTG interrupt status bit masks */
+#define  OTGSC_INTERRUPT_STATUS_BITS_MASK  \
+	(OTGSC_INTSTS_USB_ID          |    \
+	OTGSC_INTR_1MS_TIMER_EN       |    \
+	OTGSC_INTSTS_A_VBUS_VALID     |    \
+	OTGSC_INTSTS_A_SESSION_VALID  |    \
+	OTGSC_INTSTS_B_SESSION_VALID  |    \
+	OTGSC_INTSTS_B_SESSION_END    |    \
+	OTGSC_INTSTS_DATA_PULSING)
+
+/*
+ *  A-DEVICE timing  constants
+ */
+
+/* Wait for VBUS Rise  */
+#define TA_WAIT_VRISE	(100)	/* a_wait_vrise 100 ms, section: 6.6.5.1 */
+
+/* Wait for B-Connect */
+#define TA_WAIT_BCON	(10000)  /* a_wait_bcon > 1 sec, section: 6.6.5.2
+				  * This is only used to get out of
+				  * OTG_STATE_A_WAIT_BCON state if there was
+				  * no connection for these many milliseconds
+				  */
+
+/* A-Idle to B-Disconnect */
+/* It is necessary for this timer to be more than 750 ms because of a bug in OPT
+ * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated
+ * in the test description
+ */
+#define TA_AIDL_BDIS	(5000)	/* a_suspend minimum 200 ms, section: 6.6.5.3 */
+
+/* B-Idle to A-Disconnect */
+#define TA_BIDL_ADIS	(12)	/* 3 to 200 ms */
+
+/* B-device timing constants */
+
+
+/* Data-Line Pulse Time*/
+#define TB_DATA_PLS	(10)	/* b_srp_init,continue 5~10ms, section:5.3.3 */
+#define TB_DATA_PLS_MIN	(5)	/* minimum 5 ms */
+#define TB_DATA_PLS_MAX	(10)	/* maximum 10 ms */
+
+/* SRP Initiate Time  */
+#define TB_SRP_INIT	(100)	/* b_srp_init,maximum 100 ms, section:5.3.8 */
+
+/* SRP Fail Time  */
+#define TB_SRP_FAIL	(7000)	/* b_srp_init,Fail time 5~30s, section:6.8.2.2*/
+
+/* SRP result wait time */
+#define TB_SRP_WAIT	(60)
+
+/* VBus time */
+#define TB_VBUS_PLS	(30)	/* time to keep vbus pulsing asserted */
+
+/* Discharge time */
+/* This time should be less than 10ms. It varies from system to system. */
+#define TB_VBUS_DSCHRG	(8)
+
+/* A-SE0 to B-Reset  */
+#define TB_ASE0_BRST	(20)	/* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */
+
+/* A bus suspend timer before we can switch to b_wait_aconn */
+#define TB_A_SUSPEND	(7)
+#define TB_BUS_RESUME	(12)
+
+/* SE0 Time Before SRP */
+#define TB_SE0_SRP	(2)	/* b_idle,minimum 2 ms, section:5.3.2 */
+
+#define SET_OTG_STATE(phy, newstate)	((phy)->otg->state = newstate)
+
+struct usb_dr_mmap {
+	/* Capability register */
+	u8 res1[256];
+	u16 caplength;		/* Capability Register Length */
+	u16 hciversion;		/* Host Controller Interface Version */
+	u32 hcsparams;		/* Host Controller Structual Parameters */
+	u32 hccparams;		/* Host Controller Capability Parameters */
+	u8 res2[20];
+	u32 dciversion;		/* Device Controller Interface Version */
+	u32 dccparams;		/* Device Controller Capability Parameters */
+	u8 res3[24];
+	/* Operation register */
+	u32 usbcmd;		/* USB Command Register */
+	u32 usbsts;		/* USB Status Register */
+	u32 usbintr;		/* USB Interrupt Enable Register */
+	u32 frindex;		/* Frame Index Register */
+	u8 res4[4];
+	u32 deviceaddr;		/* Device Address */
+	u32 endpointlistaddr;	/* Endpoint List Address Register */
+	u8 res5[4];
+	u32 burstsize;		/* Master Interface Data Burst Size Register */
+	u32 txttfilltuning;	/* Transmit FIFO Tuning Controls Register */
+	u8 res6[8];
+	u32 ulpiview;		/* ULPI register access */
+	u8 res7[12];
+	u32 configflag;		/* Configure Flag Register */
+	u32 portsc;		/* Port 1 Status and Control Register */
+	u8 res8[28];
+	u32 otgsc;		/* On-The-Go Status and Control */
+	u32 usbmode;		/* USB Mode Register */
+	u32 endptsetupstat;	/* Endpoint Setup Status Register */
+	u32 endpointprime;	/* Endpoint Initialization Register */
+	u32 endptflush;		/* Endpoint Flush Register */
+	u32 endptstatus;	/* Endpoint Status Register */
+	u32 endptcomplete;	/* Endpoint Complete Register */
+	u32 endptctrl[6];	/* Endpoint Control Registers */
+	u8 res9[552];
+	u32 snoop1;
+	u32 snoop2;
+	u32 age_cnt_thresh;	/* Age Count Threshold Register */
+	u32 pri_ctrl;		/* Priority Control Register */
+	u32 si_ctrl;		/* System Interface Control Register */
+	u8 res10[236];
+	u32 control;		/* General Purpose Control Register */
+};
+
+struct fsl_otg_timer {
+	unsigned long expires;	/* Number of count increase to timeout */
+	unsigned long count;	/* Tick counter */
+	void (*function)(unsigned long);	/* Timeout function */
+	unsigned long data;	/* Data passed to function */
+	struct list_head list;
+};
+
+inline struct fsl_otg_timer *otg_timer_initializer
+(void (*function)(unsigned long), unsigned long expires, unsigned long data)
+{
+	struct fsl_otg_timer *timer;
+
+	timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL);
+	if (!timer)
+		return NULL;
+	timer->function = function;
+	timer->expires = expires;
+	timer->data = data;
+	return timer;
+}
+
+struct fsl_otg {
+	struct usb_phy phy;
+	struct otg_fsm fsm;
+	struct usb_dr_mmap *dr_mem_map;
+	struct delayed_work otg_event;
+
+	/* used for usb host */
+	struct work_struct work_wq;
+	u8	host_working;
+
+	int irq;
+};
+
+struct fsl_otg_config {
+	u8 otg_port;
+};
+
+/* For SRP and HNP handle */
+#define FSL_OTG_MAJOR		240
+#define FSL_OTG_NAME		"fsl-usb2-otg"
+/* Command to OTG driver ioctl */
+#define OTG_IOCTL_MAGIC		FSL_OTG_MAJOR
+/* if otg work as host, it should return 1, otherwise return 0 */
+#define GET_OTG_STATUS		_IOR(OTG_IOCTL_MAGIC, 1, int)
+#define SET_A_SUSPEND_REQ	_IOW(OTG_IOCTL_MAGIC, 2, int)
+#define SET_A_BUS_DROP		_IOW(OTG_IOCTL_MAGIC, 3, int)
+#define SET_A_BUS_REQ		_IOW(OTG_IOCTL_MAGIC, 4, int)
+#define SET_B_BUS_REQ		_IOW(OTG_IOCTL_MAGIC, 5, int)
+#define GET_A_SUSPEND_REQ	_IOR(OTG_IOCTL_MAGIC, 6, int)
+#define GET_A_BUS_DROP		_IOR(OTG_IOCTL_MAGIC, 7, int)
+#define GET_A_BUS_REQ		_IOR(OTG_IOCTL_MAGIC, 8, int)
+#define GET_B_BUS_REQ		_IOR(OTG_IOCTL_MAGIC, 9, int)
+
+void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer);
+void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer);
+void fsl_otg_pulse_vbus(void);
diff --git a/marvell/linux/drivers/usb/phy/phy-generic.c b/marvell/linux/drivers/usb/phy/phy-generic.c
new file mode 100644
index 0000000..a53b89b
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-generic.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NOP USB transceiver for all USB transceiver which are either built-in
+ * into USB IP or which are mostly autonomous.
+ *
+ * Copyright (C) 2009 Texas Instruments Inc
+ * Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
+ *
+ * Current status:
+ *	This provides a "nop" transceiver for PHYs which are
+ *	autonomous such as isp1504, isp1707, etc.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/usb_phy_generic.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include "phy-generic.h"
+
+#define VBUS_IRQ_FLAGS \
+	(IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \
+		IRQF_ONESHOT)
+
+struct platform_device *usb_phy_generic_register(void)
+{
+	return platform_device_register_simple("usb_phy_generic",
+			PLATFORM_DEVID_AUTO, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(usb_phy_generic_register);
+
+void usb_phy_generic_unregister(struct platform_device *pdev)
+{
+	platform_device_unregister(pdev);
+}
+EXPORT_SYMBOL_GPL(usb_phy_generic_unregister);
+
+static int nop_set_suspend(struct usb_phy *x, int suspend)
+{
+	struct usb_phy_generic *nop = dev_get_drvdata(x->dev);
+
+	if (!IS_ERR(nop->clk)) {
+		if (suspend)
+			clk_disable_unprepare(nop->clk);
+		else
+			clk_prepare_enable(nop->clk);
+	}
+
+	return 0;
+}
+
+static void nop_reset(struct usb_phy_generic *nop)
+{
+	if (!nop->gpiod_reset)
+		return;
+
+	gpiod_set_value_cansleep(nop->gpiod_reset, 1);
+	usleep_range(10000, 20000);
+	gpiod_set_value_cansleep(nop->gpiod_reset, 0);
+}
+
+/* interface to regulator framework */
+static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
+{
+	struct regulator *vbus_draw = nop->vbus_draw;
+	int enabled;
+	int ret;
+
+	if (!vbus_draw)
+		return;
+
+	enabled = nop->vbus_draw_enabled;
+	if (mA) {
+		regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
+		if (!enabled) {
+			ret = regulator_enable(vbus_draw);
+			if (ret < 0)
+				return;
+			nop->vbus_draw_enabled = 1;
+		}
+	} else {
+		if (enabled) {
+			ret = regulator_disable(vbus_draw);
+			if (ret < 0)
+				return;
+			nop->vbus_draw_enabled = 0;
+		}
+	}
+	nop->mA = mA;
+}
+
+
+static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
+{
+	struct usb_phy_generic *nop = data;
+	struct usb_otg *otg = nop->phy.otg;
+	int vbus, status;
+
+	vbus = gpiod_get_value(nop->gpiod_vbus);
+	if ((vbus ^ nop->vbus) == 0)
+		return IRQ_HANDLED;
+	nop->vbus = vbus;
+
+	if (vbus) {
+		status = USB_EVENT_VBUS;
+		otg->state = OTG_STATE_B_PERIPHERAL;
+		nop->phy.last_event = status;
+
+		/* drawing a "unit load" is *always* OK, except for OTG */
+		nop_set_vbus_draw(nop, 100);
+
+		atomic_notifier_call_chain(&nop->phy.notifier, status,
+					   otg->gadget);
+	} else {
+		nop_set_vbus_draw(nop, 0);
+
+		status = USB_EVENT_NONE;
+		otg->state = OTG_STATE_B_IDLE;
+		nop->phy.last_event = status;
+
+		atomic_notifier_call_chain(&nop->phy.notifier, status,
+					   otg->gadget);
+	}
+	return IRQ_HANDLED;
+}
+
+int usb_gen_phy_init(struct usb_phy *phy)
+{
+	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
+	int ret;
+
+	if (!IS_ERR(nop->vcc)) {
+		if (regulator_enable(nop->vcc))
+			dev_err(phy->dev, "Failed to enable power\n");
+	}
+
+	if (!IS_ERR(nop->clk)) {
+		ret = clk_prepare_enable(nop->clk);
+		if (ret)
+			return ret;
+	}
+
+	nop_reset(nop);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gen_phy_init);
+
+void usb_gen_phy_shutdown(struct usb_phy *phy)
+{
+	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
+
+	gpiod_set_value_cansleep(nop->gpiod_reset, 1);
+
+	if (!IS_ERR(nop->clk))
+		clk_disable_unprepare(nop->clk);
+
+	if (!IS_ERR(nop->vcc)) {
+		if (regulator_disable(nop->vcc))
+			dev_err(phy->dev, "Failed to disable power\n");
+	}
+}
+EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown);
+
+static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
+{
+	if (!otg)
+		return -ENODEV;
+
+	if (!gadget) {
+		otg->gadget = NULL;
+		return -ENODEV;
+	}
+
+	otg->gadget = gadget;
+	if (otg->state == OTG_STATE_B_PERIPHERAL)
+		atomic_notifier_call_chain(&otg->usb_phy->notifier,
+					   USB_EVENT_VBUS, otg->gadget);
+	else
+		otg->state = OTG_STATE_B_IDLE;
+	return 0;
+}
+
+static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	if (!otg)
+		return -ENODEV;
+
+	if (!host) {
+		otg->host = NULL;
+		return -ENODEV;
+	}
+
+	otg->host = host;
+	return 0;
+}
+
+int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
+		struct usb_phy_generic_platform_data *pdata)
+{
+	enum usb_phy_type type = USB_PHY_TYPE_USB2;
+	int err = 0;
+
+	u32 clk_rate = 0;
+	bool needs_vcc = false, needs_clk = false;
+
+	if (dev->of_node) {
+		struct device_node *node = dev->of_node;
+
+		if (of_property_read_u32(node, "clock-frequency", &clk_rate))
+			clk_rate = 0;
+
+		needs_vcc = of_property_read_bool(node, "vcc-supply");
+		needs_clk = of_property_read_bool(node, "clocks");
+		nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
+							   GPIOD_ASIS);
+		err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
+		if (!err) {
+			nop->gpiod_vbus = devm_gpiod_get_optional(dev,
+							 "vbus-detect",
+							 GPIOD_ASIS);
+			err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
+		}
+	} else if (pdata) {
+		type = pdata->type;
+		clk_rate = pdata->clk_rate;
+		needs_vcc = pdata->needs_vcc;
+		if (gpio_is_valid(pdata->gpio_reset)) {
+			err = devm_gpio_request_one(dev, pdata->gpio_reset,
+						    GPIOF_ACTIVE_LOW,
+						    dev_name(dev));
+			if (!err)
+				nop->gpiod_reset =
+					gpio_to_desc(pdata->gpio_reset);
+		}
+		nop->gpiod_vbus = pdata->gpiod_vbus;
+	}
+
+	if (err == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	if (err) {
+		dev_err(dev, "Error requesting RESET or VBUS GPIO\n");
+		return err;
+	}
+	if (nop->gpiod_reset)
+		gpiod_direction_output(nop->gpiod_reset, 1);
+
+	nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
+			GFP_KERNEL);
+	if (!nop->phy.otg)
+		return -ENOMEM;
+
+	nop->clk = devm_clk_get(dev, "main_clk");
+	if (IS_ERR(nop->clk)) {
+		dev_dbg(dev, "Can't get phy clock: %ld\n",
+					PTR_ERR(nop->clk));
+		if (needs_clk)
+			return PTR_ERR(nop->clk);
+	}
+
+	if (!IS_ERR(nop->clk) && clk_rate) {
+		err = clk_set_rate(nop->clk, clk_rate);
+		if (err) {
+			dev_err(dev, "Error setting clock rate\n");
+			return err;
+		}
+	}
+
+	nop->vcc = devm_regulator_get(dev, "vcc");
+	if (IS_ERR(nop->vcc)) {
+		dev_dbg(dev, "Error getting vcc regulator: %ld\n",
+					PTR_ERR(nop->vcc));
+		if (needs_vcc)
+			return -EPROBE_DEFER;
+	}
+
+	nop->dev		= dev;
+	nop->phy.dev		= nop->dev;
+	nop->phy.label		= "nop-xceiv";
+	nop->phy.set_suspend	= nop_set_suspend;
+	nop->phy.type		= type;
+
+	nop->phy.otg->state		= OTG_STATE_UNDEFINED;
+	nop->phy.otg->usb_phy		= &nop->phy;
+	nop->phy.otg->set_host		= nop_set_host;
+	nop->phy.otg->set_peripheral	= nop_set_peripheral;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
+
+static int usb_phy_generic_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct usb_phy_generic	*nop;
+	int err;
+
+	nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
+	if (!nop)
+		return -ENOMEM;
+
+	err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
+	if (err)
+		return err;
+	if (nop->gpiod_vbus) {
+		err = devm_request_threaded_irq(&pdev->dev,
+						gpiod_to_irq(nop->gpiod_vbus),
+						NULL, nop_gpio_vbus_thread,
+						VBUS_IRQ_FLAGS, "vbus_detect",
+						nop);
+		if (err) {
+			dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
+				gpiod_to_irq(nop->gpiod_vbus), err);
+			return err;
+		}
+		nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ?
+			OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE;
+	}
+
+	nop->phy.init		= usb_gen_phy_init;
+	nop->phy.shutdown	= usb_gen_phy_shutdown;
+
+	err = usb_add_phy_dev(&nop->phy);
+	if (err) {
+		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
+			err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, nop);
+
+	return 0;
+}
+
+static int usb_phy_generic_remove(struct platform_device *pdev)
+{
+	struct usb_phy_generic *nop = platform_get_drvdata(pdev);
+
+	usb_remove_phy(&nop->phy);
+
+	return 0;
+}
+
+static const struct of_device_id nop_xceiv_dt_ids[] = {
+	{ .compatible = "usb-nop-xceiv" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);
+
+static struct platform_driver usb_phy_generic_driver = {
+	.probe		= usb_phy_generic_probe,
+	.remove		= usb_phy_generic_remove,
+	.driver		= {
+		.name	= "usb_phy_generic",
+		.of_match_table = nop_xceiv_dt_ids,
+	},
+};
+
+static int __init usb_phy_generic_init(void)
+{
+	return platform_driver_register(&usb_phy_generic_driver);
+}
+subsys_initcall(usb_phy_generic_init);
+
+static void __exit usb_phy_generic_exit(void)
+{
+	platform_driver_unregister(&usb_phy_generic_driver);
+}
+module_exit(usb_phy_generic_exit);
+
+MODULE_ALIAS("platform:usb_phy_generic");
+MODULE_AUTHOR("Texas Instruments Inc");
+MODULE_DESCRIPTION("NOP USB Transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/drivers/usb/phy/phy-generic.h b/marvell/linux/drivers/usb/phy/phy-generic.h
new file mode 100644
index 0000000..9728962
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-generic.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _PHY_GENERIC_H_
+#define _PHY_GENERIC_H_
+
+#include <linux/usb/usb_phy_generic.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+struct usb_phy_generic {
+	struct usb_phy phy;
+	struct device *dev;
+	struct clk *clk;
+	struct regulator *vcc;
+	struct gpio_desc *gpiod_reset;
+	struct gpio_desc *gpiod_vbus;
+	struct regulator *vbus_draw;
+	bool vbus_draw_enabled;
+	unsigned long mA;
+	unsigned int vbus;
+};
+
+int usb_gen_phy_init(struct usb_phy *phy);
+void usb_gen_phy_shutdown(struct usb_phy *phy);
+
+int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
+		struct usb_phy_generic_platform_data *pdata);
+
+#endif
diff --git a/marvell/linux/drivers/usb/phy/phy-gpio-vbus-usb.c b/marvell/linux/drivers/usb/phy/phy-gpio-vbus-usb.c
new file mode 100644
index 0000000..553e257
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices
+ *
+ * Copyright (c) 2008 Philipp Zabel <philipp.zabel@gmail.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#include <linux/regulator/consumer.h>
+
+#include <linux/usb/gadget.h>
+#include <linux/usb/gpio_vbus.h>
+#include <linux/usb/otg.h>
+
+
+/*
+ * A simple GPIO VBUS sensing driver for B peripheral only devices
+ * with internal transceivers. It can control a D+ pullup GPIO and
+ * a regulator to limit the current drawn from VBUS.
+ *
+ * Needs to be loaded before the UDC driver that will use it.
+ */
+struct gpio_vbus_data {
+	struct usb_phy		phy;
+	struct device          *dev;
+	struct regulator       *vbus_draw;
+	int			vbus_draw_enabled;
+	unsigned		mA;
+	struct delayed_work	work;
+	int			vbus;
+	int			irq;
+};
+
+
+/*
+ * This driver relies on "both edges" triggering.  VBUS has 100 msec to
+ * stabilize, so the peripheral controller driver may need to cope with
+ * some bouncing due to current surges (e.g. charging local capacitance)
+ * and contact chatter.
+ *
+ * REVISIT in desperate straits, toggling between rising and falling
+ * edges might be workable.
+ */
+#define VBUS_IRQ_FLAGS \
+	(IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
+
+
+/* interface to regulator framework */
+static void set_vbus_draw(struct gpio_vbus_data *gpio_vbus, unsigned mA)
+{
+	struct regulator *vbus_draw = gpio_vbus->vbus_draw;
+	int enabled;
+	int ret;
+
+	if (!vbus_draw)
+		return;
+
+	enabled = gpio_vbus->vbus_draw_enabled;
+	if (mA) {
+		regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
+		if (!enabled) {
+			ret = regulator_enable(vbus_draw);
+			if (ret < 0)
+				return;
+			gpio_vbus->vbus_draw_enabled = 1;
+		}
+	} else {
+		if (enabled) {
+			ret = regulator_disable(vbus_draw);
+			if (ret < 0)
+				return;
+			gpio_vbus->vbus_draw_enabled = 0;
+		}
+	}
+	gpio_vbus->mA = mA;
+}
+
+static int is_vbus_powered(struct gpio_vbus_mach_info *pdata)
+{
+	int vbus;
+
+	vbus = gpio_get_value(pdata->gpio_vbus);
+	if (pdata->gpio_vbus_inverted)
+		vbus = !vbus;
+
+	return vbus;
+}
+
+static void gpio_vbus_work(struct work_struct *work)
+{
+	struct gpio_vbus_data *gpio_vbus =
+		container_of(work, struct gpio_vbus_data, work.work);
+	struct gpio_vbus_mach_info *pdata = dev_get_platdata(gpio_vbus->dev);
+	int gpio, status, vbus;
+
+	if (!gpio_vbus->phy.otg->gadget)
+		return;
+
+	vbus = is_vbus_powered(pdata);
+	if ((vbus ^ gpio_vbus->vbus) == 0)
+		return;
+	gpio_vbus->vbus = vbus;
+
+	/* Peripheral controllers which manage the pullup themselves won't have
+	 * gpio_pullup configured here.  If it's configured here, we'll do what
+	 * isp1301_omap::b_peripheral() does and enable the pullup here... although
+	 * that may complicate usb_gadget_{,dis}connect() support.
+	 */
+	gpio = pdata->gpio_pullup;
+
+	if (vbus) {
+		status = USB_EVENT_VBUS;
+		gpio_vbus->phy.otg->state = OTG_STATE_B_PERIPHERAL;
+		gpio_vbus->phy.last_event = status;
+		usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget);
+
+		/* drawing a "unit load" is *always* OK, except for OTG */
+		set_vbus_draw(gpio_vbus, 100);
+
+		/* optionally enable D+ pullup */
+		if (gpio_is_valid(gpio))
+			gpio_set_value(gpio, !pdata->gpio_pullup_inverted);
+
+		atomic_notifier_call_chain(&gpio_vbus->phy.notifier,
+					   status, gpio_vbus->phy.otg->gadget);
+		usb_phy_set_event(&gpio_vbus->phy, USB_EVENT_ENUMERATED);
+	} else {
+		/* optionally disable D+ pullup */
+		if (gpio_is_valid(gpio))
+			gpio_set_value(gpio, pdata->gpio_pullup_inverted);
+
+		set_vbus_draw(gpio_vbus, 0);
+
+		usb_gadget_vbus_disconnect(gpio_vbus->phy.otg->gadget);
+		status = USB_EVENT_NONE;
+		gpio_vbus->phy.otg->state = OTG_STATE_B_IDLE;
+		gpio_vbus->phy.last_event = status;
+
+		atomic_notifier_call_chain(&gpio_vbus->phy.notifier,
+					   status, gpio_vbus->phy.otg->gadget);
+		usb_phy_set_event(&gpio_vbus->phy, USB_EVENT_NONE);
+	}
+}
+
+/* VBUS change IRQ handler */
+static irqreturn_t gpio_vbus_irq(int irq, void *data)
+{
+	struct platform_device *pdev = data;
+	struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev);
+	struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
+	struct usb_otg *otg = gpio_vbus->phy.otg;
+
+	dev_dbg(&pdev->dev, "VBUS %s (gadget: %s)\n",
+		is_vbus_powered(pdata) ? "supplied" : "inactive",
+		otg->gadget ? otg->gadget->name : "none");
+
+	if (otg->gadget)
+		schedule_delayed_work(&gpio_vbus->work, msecs_to_jiffies(100));
+
+	return IRQ_HANDLED;
+}
+
+/* OTG transceiver interface */
+
+/* bind/unbind the peripheral controller */
+static int gpio_vbus_set_peripheral(struct usb_otg *otg,
+					struct usb_gadget *gadget)
+{
+	struct gpio_vbus_data *gpio_vbus;
+	struct gpio_vbus_mach_info *pdata;
+	struct platform_device *pdev;
+	int gpio;
+
+	gpio_vbus = container_of(otg->usb_phy, struct gpio_vbus_data, phy);
+	pdev = to_platform_device(gpio_vbus->dev);
+	pdata = dev_get_platdata(gpio_vbus->dev);
+	gpio = pdata->gpio_pullup;
+
+	if (!gadget) {
+		dev_dbg(&pdev->dev, "unregistering gadget '%s'\n",
+			otg->gadget->name);
+
+		/* optionally disable D+ pullup */
+		if (gpio_is_valid(gpio))
+			gpio_set_value(gpio, pdata->gpio_pullup_inverted);
+
+		set_vbus_draw(gpio_vbus, 0);
+
+		usb_gadget_vbus_disconnect(otg->gadget);
+		otg->state = OTG_STATE_UNDEFINED;
+
+		otg->gadget = NULL;
+		return 0;
+	}
+
+	otg->gadget = gadget;
+	dev_dbg(&pdev->dev, "registered gadget '%s'\n", gadget->name);
+
+	/* initialize connection state */
+	gpio_vbus->vbus = 0; /* start with disconnected */
+	gpio_vbus_irq(gpio_vbus->irq, pdev);
+	return 0;
+}
+
+/* effective for B devices, ignored for A-peripheral */
+static int gpio_vbus_set_power(struct usb_phy *phy, unsigned mA)
+{
+	struct gpio_vbus_data *gpio_vbus;
+
+	gpio_vbus = container_of(phy, struct gpio_vbus_data, phy);
+
+	if (phy->otg->state == OTG_STATE_B_PERIPHERAL)
+		set_vbus_draw(gpio_vbus, mA);
+	return 0;
+}
+
+/* for non-OTG B devices: set/clear transceiver suspend mode */
+static int gpio_vbus_set_suspend(struct usb_phy *phy, int suspend)
+{
+	struct gpio_vbus_data *gpio_vbus;
+
+	gpio_vbus = container_of(phy, struct gpio_vbus_data, phy);
+
+	/* draw max 0 mA from vbus in suspend mode; or the previously
+	 * recorded amount of current if not suspended
+	 *
+	 * NOTE: high powered configs (mA > 100) may draw up to 2.5 mA
+	 * if they're wake-enabled ... we don't handle that yet.
+	 */
+	return gpio_vbus_set_power(phy, suspend ? 0 : gpio_vbus->mA);
+}
+
+/* platform driver interface */
+
+static int gpio_vbus_probe(struct platform_device *pdev)
+{
+	struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev);
+	struct gpio_vbus_data *gpio_vbus;
+	struct resource *res;
+	int err, gpio, irq;
+	unsigned long irqflags;
+
+	if (!pdata || !gpio_is_valid(pdata->gpio_vbus))
+		return -EINVAL;
+	gpio = pdata->gpio_vbus;
+
+	gpio_vbus = devm_kzalloc(&pdev->dev, sizeof(struct gpio_vbus_data),
+				 GFP_KERNEL);
+	if (!gpio_vbus)
+		return -ENOMEM;
+
+	gpio_vbus->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+					  GFP_KERNEL);
+	if (!gpio_vbus->phy.otg)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, gpio_vbus);
+	gpio_vbus->dev = &pdev->dev;
+	gpio_vbus->phy.label = "gpio-vbus";
+	gpio_vbus->phy.dev = gpio_vbus->dev;
+	gpio_vbus->phy.set_power = gpio_vbus_set_power;
+	gpio_vbus->phy.set_suspend = gpio_vbus_set_suspend;
+
+	gpio_vbus->phy.otg->state = OTG_STATE_UNDEFINED;
+	gpio_vbus->phy.otg->usb_phy = &gpio_vbus->phy;
+	gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral;
+
+	err = devm_gpio_request(&pdev->dev, gpio, "vbus_detect");
+	if (err) {
+		dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n",
+			gpio, err);
+		return err;
+	}
+	gpio_direction_input(gpio);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res) {
+		irq = res->start;
+		irqflags = (res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED;
+	} else {
+		irq = gpio_to_irq(gpio);
+		irqflags = VBUS_IRQ_FLAGS;
+	}
+
+	gpio_vbus->irq = irq;
+
+	/* if data line pullup is in use, initialize it to "not pulling up" */
+	gpio = pdata->gpio_pullup;
+	if (gpio_is_valid(gpio)) {
+		err = devm_gpio_request(&pdev->dev, gpio, "udc_pullup");
+		if (err) {
+			dev_err(&pdev->dev,
+				"can't request pullup gpio %d, err: %d\n",
+				gpio, err);
+			return err;
+		}
+		gpio_direction_output(gpio, pdata->gpio_pullup_inverted);
+	}
+
+	err = devm_request_irq(&pdev->dev, irq, gpio_vbus_irq, irqflags,
+			       "vbus_detect", pdev);
+	if (err) {
+		dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
+			irq, err);
+		return err;
+	}
+
+	INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work);
+
+	gpio_vbus->vbus_draw = devm_regulator_get(&pdev->dev, "vbus_draw");
+	if (IS_ERR(gpio_vbus->vbus_draw)) {
+		dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n",
+			PTR_ERR(gpio_vbus->vbus_draw));
+		gpio_vbus->vbus_draw = NULL;
+	}
+
+	/* only active when a gadget is registered */
+	err = usb_add_phy(&gpio_vbus->phy, USB_PHY_TYPE_USB2);
+	if (err) {
+		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
+			err);
+		return err;
+	}
+
+	device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+	return 0;
+}
+
+static int gpio_vbus_remove(struct platform_device *pdev)
+{
+	struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	cancel_delayed_work_sync(&gpio_vbus->work);
+
+	usb_remove_phy(&gpio_vbus->phy);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpio_vbus_pm_suspend(struct device *dev)
+{
+	struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(gpio_vbus->irq);
+
+	return 0;
+}
+
+static int gpio_vbus_pm_resume(struct device *dev)
+{
+	struct gpio_vbus_data *gpio_vbus = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(gpio_vbus->irq);
+
+	return 0;
+}
+
+static const struct dev_pm_ops gpio_vbus_dev_pm_ops = {
+	.suspend	= gpio_vbus_pm_suspend,
+	.resume		= gpio_vbus_pm_resume,
+};
+#endif
+
+MODULE_ALIAS("platform:gpio-vbus");
+
+static struct platform_driver gpio_vbus_driver = {
+	.driver = {
+		.name  = "gpio-vbus",
+#ifdef CONFIG_PM
+		.pm = &gpio_vbus_dev_pm_ops,
+#endif
+	},
+	.probe		= gpio_vbus_probe,
+	.remove		= gpio_vbus_remove,
+};
+
+module_platform_driver(gpio_vbus_driver);
+
+MODULE_DESCRIPTION("simple GPIO controlled OTG transceiver driver");
+MODULE_AUTHOR("Philipp Zabel");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/drivers/usb/phy/phy-isp1301-omap.c b/marvell/linux/drivers/usb/phy/phy-isp1301-omap.c
new file mode 100644
index 0000000..7041ba0
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-isp1301-omap.c
@@ -0,0 +1,1636 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller
+ *
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <mach/mux.h>
+
+#include <mach/usb.h>
+
+#undef VERBOSE
+
+
+#define	DRIVER_VERSION	"24 August 2004"
+#define	DRIVER_NAME	(isp1301_driver.driver.name)
+
+MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver");
+MODULE_LICENSE("GPL");
+
+struct isp1301 {
+	struct usb_phy		phy;
+	struct i2c_client	*client;
+	void			(*i2c_release)(struct device *dev);
+
+	int			irq_type;
+
+	u32			last_otg_ctrl;
+	unsigned		working:1;
+
+	struct timer_list	timer;
+
+	/* use keventd context to change the state for us */
+	struct work_struct	work;
+
+	unsigned long		todo;
+#		define WORK_UPDATE_ISP	0	/* update ISP from OTG */
+#		define WORK_UPDATE_OTG	1	/* update OTG from ISP */
+#		define WORK_HOST_RESUME	4	/* resume host */
+#		define WORK_TIMER	6	/* timer fired */
+#		define WORK_STOP	7	/* don't resubmit */
+};
+
+
+/* bits in OTG_CTRL */
+
+#define	OTG_XCEIV_OUTPUTS \
+	(OTG_ASESSVLD|OTG_BSESSEND|OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID)
+#define	OTG_XCEIV_INPUTS \
+	(OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID)
+#define	OTG_CTRL_BITS \
+	(OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|OTG_B_HNPEN|OTG_BUSDROP)
+	/* and OTG_PULLUP is sometimes written */
+
+#define	OTG_CTRL_MASK	(OTG_DRIVER_SEL| \
+	OTG_XCEIV_OUTPUTS|OTG_XCEIV_INPUTS| \
+	OTG_CTRL_BITS)
+
+
+/*-------------------------------------------------------------------------*/
+
+/* board-specific PM hooks */
+
+#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3)
+
+#if IS_REACHABLE(CONFIG_TPS65010)
+
+#include <linux/mfd/tps65010.h>
+
+#else
+
+static inline int tps65010_set_vbus_draw(unsigned mA)
+{
+	pr_debug("tps65010: draw %d mA (STUB)\n", mA);
+	return 0;
+}
+
+#endif
+
+static void enable_vbus_draw(struct isp1301 *isp, unsigned mA)
+{
+	int status = tps65010_set_vbus_draw(mA);
+	if (status < 0)
+		pr_debug("  VBUS %d mA error %d\n", mA, status);
+}
+
+#else
+
+static void enable_vbus_draw(struct isp1301 *isp, unsigned mA)
+{
+	/* H4 controls this by DIP switch S2.4; no soft control.
+	 * ON means the charger is always enabled.  Leave it OFF
+	 * unless the OTG port is used only in B-peripheral mode.
+	 */
+}
+
+#endif
+
+static void enable_vbus_source(struct isp1301 *isp)
+{
+	/* this board won't supply more than 8mA vbus power.
+	 * some boards can switch a 100ma "unit load" (or more).
+	 */
+}
+
+
+/* products will deliver OTG messages with LEDs, GUI, etc */
+static inline void notresponding(struct isp1301 *isp)
+{
+	printk(KERN_NOTICE "OTG device not responding.\n");
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct i2c_driver isp1301_driver;
+
+/* smbus apis are used for portability */
+
+static inline u8
+isp1301_get_u8(struct isp1301 *isp, u8 reg)
+{
+	return i2c_smbus_read_byte_data(isp->client, reg + 0);
+}
+
+static inline int
+isp1301_get_u16(struct isp1301 *isp, u8 reg)
+{
+	return i2c_smbus_read_word_data(isp->client, reg);
+}
+
+static inline int
+isp1301_set_bits(struct isp1301 *isp, u8 reg, u8 bits)
+{
+	return i2c_smbus_write_byte_data(isp->client, reg + 0, bits);
+}
+
+static inline int
+isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits)
+{
+	return i2c_smbus_write_byte_data(isp->client, reg + 1, bits);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* identification */
+#define	ISP1301_VENDOR_ID		0x00	/* u16 read */
+#define	ISP1301_PRODUCT_ID		0x02	/* u16 read */
+#define	ISP1301_BCD_DEVICE		0x14	/* u16 read */
+
+#define	I2C_VENDOR_ID_PHILIPS		0x04cc
+#define	I2C_PRODUCT_ID_PHILIPS_1301	0x1301
+
+/* operational registers */
+#define	ISP1301_MODE_CONTROL_1		0x04	/* u8 read, set, +1 clear */
+#	define	MC1_SPEED		(1 << 0)
+#	define	MC1_SUSPEND		(1 << 1)
+#	define	MC1_DAT_SE0		(1 << 2)
+#	define	MC1_TRANSPARENT		(1 << 3)
+#	define	MC1_BDIS_ACON_EN	(1 << 4)
+#	define	MC1_OE_INT_EN		(1 << 5)
+#	define	MC1_UART_EN		(1 << 6)
+#	define	MC1_MASK		0x7f
+#define	ISP1301_MODE_CONTROL_2		0x12	/* u8 read, set, +1 clear */
+#	define	MC2_GLOBAL_PWR_DN	(1 << 0)
+#	define	MC2_SPD_SUSP_CTRL	(1 << 1)
+#	define	MC2_BI_DI		(1 << 2)
+#	define	MC2_TRANSP_BDIR0	(1 << 3)
+#	define	MC2_TRANSP_BDIR1	(1 << 4)
+#	define	MC2_AUDIO_EN		(1 << 5)
+#	define	MC2_PSW_EN		(1 << 6)
+#	define	MC2_EN2V7		(1 << 7)
+#define	ISP1301_OTG_CONTROL_1		0x06	/* u8 read, set, +1 clear */
+#	define	OTG1_DP_PULLUP		(1 << 0)
+#	define	OTG1_DM_PULLUP		(1 << 1)
+#	define	OTG1_DP_PULLDOWN	(1 << 2)
+#	define	OTG1_DM_PULLDOWN	(1 << 3)
+#	define	OTG1_ID_PULLDOWN	(1 << 4)
+#	define	OTG1_VBUS_DRV		(1 << 5)
+#	define	OTG1_VBUS_DISCHRG	(1 << 6)
+#	define	OTG1_VBUS_CHRG		(1 << 7)
+#define	ISP1301_OTG_STATUS		0x10	/* u8 readonly */
+#	define	OTG_B_SESS_END		(1 << 6)
+#	define	OTG_B_SESS_VLD		(1 << 7)
+
+#define	ISP1301_INTERRUPT_SOURCE	0x08	/* u8 read */
+#define	ISP1301_INTERRUPT_LATCH		0x0A	/* u8 read, set, +1 clear */
+
+#define	ISP1301_INTERRUPT_FALLING	0x0C	/* u8 read, set, +1 clear */
+#define	ISP1301_INTERRUPT_RISING	0x0E	/* u8 read, set, +1 clear */
+
+/* same bitfields in all interrupt registers */
+#	define	INTR_VBUS_VLD		(1 << 0)
+#	define	INTR_SESS_VLD		(1 << 1)
+#	define	INTR_DP_HI		(1 << 2)
+#	define	INTR_ID_GND		(1 << 3)
+#	define	INTR_DM_HI		(1 << 4)
+#	define	INTR_ID_FLOAT		(1 << 5)
+#	define	INTR_BDIS_ACON		(1 << 6)
+#	define	INTR_CR_INT		(1 << 7)
+
+/*-------------------------------------------------------------------------*/
+
+static inline const char *state_name(struct isp1301 *isp)
+{
+	return usb_otg_state_string(isp->phy.otg->state);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* NOTE:  some of this ISP1301 setup is specific to H2 boards;
+ * not everything is guarded by board-specific checks, or even using
+ * omap_usb_config data to deduce MC1_DAT_SE0 and MC2_BI_DI.
+ *
+ * ALSO:  this currently doesn't use ISP1301 low-power modes
+ * while OTG is running.
+ */
+
+static void power_down(struct isp1301 *isp)
+{
+	isp->phy.otg->state = OTG_STATE_UNDEFINED;
+
+	// isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
+	isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND);
+
+	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_ID_PULLDOWN);
+	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+}
+
+static void __maybe_unused power_up(struct isp1301 *isp)
+{
+	// isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
+	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND);
+
+	/* do this only when cpu is driving transceiver,
+	 * so host won't see a low speed device...
+	 */
+	isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+}
+
+#define	NO_HOST_SUSPEND
+
+static int host_suspend(struct isp1301 *isp)
+{
+#ifdef	NO_HOST_SUSPEND
+	return 0;
+#else
+	struct device	*dev;
+
+	if (!isp->phy.otg->host)
+		return -ENODEV;
+
+	/* Currently ASSUMES only the OTG port matters;
+	 * other ports could be active...
+	 */
+	dev = isp->phy.otg->host->controller;
+	return dev->driver->suspend(dev, 3, 0);
+#endif
+}
+
+static int host_resume(struct isp1301 *isp)
+{
+#ifdef	NO_HOST_SUSPEND
+	return 0;
+#else
+	struct device	*dev;
+
+	if (!isp->phy.otg->host)
+		return -ENODEV;
+
+	dev = isp->phy.otg->host->controller;
+	return dev->driver->resume(dev, 0);
+#endif
+}
+
+static int gadget_suspend(struct isp1301 *isp)
+{
+	isp->phy.otg->gadget->b_hnp_enable = 0;
+	isp->phy.otg->gadget->a_hnp_support = 0;
+	isp->phy.otg->gadget->a_alt_hnp_support = 0;
+	return usb_gadget_vbus_disconnect(isp->phy.otg->gadget);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define	TIMER_MINUTES	10
+#define	TIMER_JIFFIES	(TIMER_MINUTES * 60 * HZ)
+
+/* Almost all our I2C messaging comes from a work queue's task context.
+ * NOTE: guaranteeing certain response times might mean we shouldn't
+ * share keventd's work queue; a realtime task might be safest.
+ */
+static void isp1301_defer_work(struct isp1301 *isp, int work)
+{
+	int status;
+
+	if (isp && !test_and_set_bit(work, &isp->todo)) {
+		(void) get_device(&isp->client->dev);
+		status = schedule_work(&isp->work);
+		if (!status && !isp->working)
+			dev_vdbg(&isp->client->dev,
+				"work item %d may be lost\n", work);
+	}
+}
+
+/* called from irq handlers */
+static void a_idle(struct isp1301 *isp, const char *tag)
+{
+	u32 l;
+
+	if (isp->phy.otg->state == OTG_STATE_A_IDLE)
+		return;
+
+	isp->phy.otg->default_a = 1;
+	if (isp->phy.otg->host) {
+		isp->phy.otg->host->is_b_host = 0;
+		host_suspend(isp);
+	}
+	if (isp->phy.otg->gadget) {
+		isp->phy.otg->gadget->is_a_peripheral = 1;
+		gadget_suspend(isp);
+	}
+	isp->phy.otg->state = OTG_STATE_A_IDLE;
+	l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+	omap_writel(l, OTG_CTRL);
+	isp->last_otg_ctrl = l;
+	pr_debug("  --> %s/%s\n", state_name(isp), tag);
+}
+
+/* called from irq handlers */
+static void b_idle(struct isp1301 *isp, const char *tag)
+{
+	u32 l;
+
+	if (isp->phy.otg->state == OTG_STATE_B_IDLE)
+		return;
+
+	isp->phy.otg->default_a = 0;
+	if (isp->phy.otg->host) {
+		isp->phy.otg->host->is_b_host = 1;
+		host_suspend(isp);
+	}
+	if (isp->phy.otg->gadget) {
+		isp->phy.otg->gadget->is_a_peripheral = 0;
+		gadget_suspend(isp);
+	}
+	isp->phy.otg->state = OTG_STATE_B_IDLE;
+	l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+	omap_writel(l, OTG_CTRL);
+	isp->last_otg_ctrl = l;
+	pr_debug("  --> %s/%s\n", state_name(isp), tag);
+}
+
+static void
+dump_regs(struct isp1301 *isp, const char *label)
+{
+	u8	ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1);
+	u8	status = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+	u8	src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE);
+
+	pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n",
+		omap_readl(OTG_CTRL), label, state_name(isp),
+		ctrl, status, src);
+	/* mode control and irq enables don't change much */
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_USB_OTG
+
+/*
+ * The OMAP OTG controller handles most of the OTG state transitions.
+ *
+ * We translate isp1301 outputs (mostly voltage comparator status) into
+ * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state
+ * flags into isp1301 inputs ... and infer state transitions.
+ */
+
+#ifdef	VERBOSE
+
+static void check_state(struct isp1301 *isp, const char *tag)
+{
+	enum usb_otg_state	state = OTG_STATE_UNDEFINED;
+	u8			fsm = omap_readw(OTG_TEST) & 0x0ff;
+	unsigned		extra = 0;
+
+	switch (fsm) {
+
+	/* default-b */
+	case 0x0:
+		state = OTG_STATE_B_IDLE;
+		break;
+	case 0x3:
+	case 0x7:
+		extra = 1;
+	case 0x1:
+		state = OTG_STATE_B_PERIPHERAL;
+		break;
+	case 0x11:
+		state = OTG_STATE_B_SRP_INIT;
+		break;
+
+	/* extra dual-role default-b states */
+	case 0x12:
+	case 0x13:
+	case 0x16:
+		extra = 1;
+	case 0x17:
+		state = OTG_STATE_B_WAIT_ACON;
+		break;
+	case 0x34:
+		state = OTG_STATE_B_HOST;
+		break;
+
+	/* default-a */
+	case 0x36:
+		state = OTG_STATE_A_IDLE;
+		break;
+	case 0x3c:
+		state = OTG_STATE_A_WAIT_VFALL;
+		break;
+	case 0x7d:
+		state = OTG_STATE_A_VBUS_ERR;
+		break;
+	case 0x9e:
+	case 0x9f:
+		extra = 1;
+	case 0x89:
+		state = OTG_STATE_A_PERIPHERAL;
+		break;
+	case 0xb7:
+		state = OTG_STATE_A_WAIT_VRISE;
+		break;
+	case 0xb8:
+		state = OTG_STATE_A_WAIT_BCON;
+		break;
+	case 0xb9:
+		state = OTG_STATE_A_HOST;
+		break;
+	case 0xba:
+		state = OTG_STATE_A_SUSPEND;
+		break;
+	default:
+		break;
+	}
+	if (isp->phy.otg->state == state && !extra)
+		return;
+	pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag,
+		usb_otg_state_string(state), fsm, state_name(isp),
+		omap_readl(OTG_CTRL));
+}
+
+#else
+
+static inline void check_state(struct isp1301 *isp, const char *tag) { }
+
+#endif
+
+/* outputs from ISP1301_INTERRUPT_SOURCE */
+static void update_otg1(struct isp1301 *isp, u8 int_src)
+{
+	u32	otg_ctrl;
+
+	otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+	otg_ctrl &= ~OTG_XCEIV_INPUTS;
+	otg_ctrl &= ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD);
+
+	if (int_src & INTR_SESS_VLD)
+		otg_ctrl |= OTG_ASESSVLD;
+	else if (isp->phy.otg->state == OTG_STATE_A_WAIT_VFALL) {
+		a_idle(isp, "vfall");
+		otg_ctrl &= ~OTG_CTRL_BITS;
+	}
+	if (int_src & INTR_VBUS_VLD)
+		otg_ctrl |= OTG_VBUSVLD;
+	if (int_src & INTR_ID_GND) {		/* default-A */
+		if (isp->phy.otg->state == OTG_STATE_B_IDLE
+				|| isp->phy.otg->state
+					== OTG_STATE_UNDEFINED) {
+			a_idle(isp, "init");
+			return;
+		}
+	} else {				/* default-B */
+		otg_ctrl |= OTG_ID;
+		if (isp->phy.otg->state == OTG_STATE_A_IDLE
+			|| isp->phy.otg->state == OTG_STATE_UNDEFINED) {
+			b_idle(isp, "init");
+			return;
+		}
+	}
+	omap_writel(otg_ctrl, OTG_CTRL);
+}
+
+/* outputs from ISP1301_OTG_STATUS */
+static void update_otg2(struct isp1301 *isp, u8 otg_status)
+{
+	u32	otg_ctrl;
+
+	otg_ctrl = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+	otg_ctrl &= ~OTG_XCEIV_INPUTS;
+	otg_ctrl &= ~(OTG_BSESSVLD | OTG_BSESSEND);
+	if (otg_status & OTG_B_SESS_VLD)
+		otg_ctrl |= OTG_BSESSVLD;
+	else if (otg_status & OTG_B_SESS_END)
+		otg_ctrl |= OTG_BSESSEND;
+	omap_writel(otg_ctrl, OTG_CTRL);
+}
+
+/* inputs going to ISP1301 */
+static void otg_update_isp(struct isp1301 *isp)
+{
+	u32	otg_ctrl, otg_change;
+	u8	set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP;
+
+	otg_ctrl = omap_readl(OTG_CTRL);
+	otg_change = otg_ctrl ^ isp->last_otg_ctrl;
+	isp->last_otg_ctrl = otg_ctrl;
+	otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS;
+
+	switch (isp->phy.otg->state) {
+	case OTG_STATE_B_IDLE:
+	case OTG_STATE_B_PERIPHERAL:
+	case OTG_STATE_B_SRP_INIT:
+		if (!(otg_ctrl & OTG_PULLUP)) {
+			// if (otg_ctrl & OTG_B_HNPEN) {
+			if (isp->phy.otg->gadget->b_hnp_enable) {
+				isp->phy.otg->state = OTG_STATE_B_WAIT_ACON;
+				pr_debug("  --> b_wait_acon\n");
+			}
+			goto pulldown;
+		}
+pullup:
+		set |= OTG1_DP_PULLUP;
+		clr |= OTG1_DP_PULLDOWN;
+		break;
+	case OTG_STATE_A_SUSPEND:
+	case OTG_STATE_A_PERIPHERAL:
+		if (otg_ctrl & OTG_PULLUP)
+			goto pullup;
+		/* FALLTHROUGH */
+	// case OTG_STATE_B_WAIT_ACON:
+	default:
+pulldown:
+		set |= OTG1_DP_PULLDOWN;
+		clr |= OTG1_DP_PULLUP;
+		break;
+	}
+
+#	define toggle(OTG,ISP) do { \
+		if (otg_ctrl & OTG) set |= ISP; \
+		else clr |= ISP; \
+		} while (0)
+
+	if (!(isp->phy.otg->host))
+		otg_ctrl &= ~OTG_DRV_VBUS;
+
+	switch (isp->phy.otg->state) {
+	case OTG_STATE_A_SUSPEND:
+		if (otg_ctrl & OTG_DRV_VBUS) {
+			set |= OTG1_VBUS_DRV;
+			break;
+		}
+		/* HNP failed for some reason (A_AIDL_BDIS timeout) */
+		notresponding(isp);
+
+		/* FALLTHROUGH */
+	case OTG_STATE_A_VBUS_ERR:
+		isp->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
+		pr_debug("  --> a_wait_vfall\n");
+		/* FALLTHROUGH */
+	case OTG_STATE_A_WAIT_VFALL:
+		/* FIXME usbcore thinks port power is still on ... */
+		clr |= OTG1_VBUS_DRV;
+		break;
+	case OTG_STATE_A_IDLE:
+		if (otg_ctrl & OTG_DRV_VBUS) {
+			isp->phy.otg->state = OTG_STATE_A_WAIT_VRISE;
+			pr_debug("  --> a_wait_vrise\n");
+		}
+		/* FALLTHROUGH */
+	default:
+		toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV);
+	}
+
+	toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG);
+	toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG);
+
+#	undef toggle
+
+	isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set);
+	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr);
+
+	/* HNP switch to host or peripheral; and SRP */
+	if (otg_change & OTG_PULLUP) {
+		u32 l;
+
+		switch (isp->phy.otg->state) {
+		case OTG_STATE_B_IDLE:
+			if (clr & OTG1_DP_PULLUP)
+				break;
+			isp->phy.otg->state = OTG_STATE_B_PERIPHERAL;
+			pr_debug("  --> b_peripheral\n");
+			break;
+		case OTG_STATE_A_SUSPEND:
+			if (clr & OTG1_DP_PULLUP)
+				break;
+			isp->phy.otg->state = OTG_STATE_A_PERIPHERAL;
+			pr_debug("  --> a_peripheral\n");
+			break;
+		default:
+			break;
+		}
+		l = omap_readl(OTG_CTRL);
+		l |= OTG_PULLUP;
+		omap_writel(l, OTG_CTRL);
+	}
+
+	check_state(isp, __func__);
+	dump_regs(isp, "otg->isp1301");
+}
+
+static irqreturn_t omap_otg_irq(int irq, void *_isp)
+{
+	u16		otg_irq = omap_readw(OTG_IRQ_SRC);
+	u32		otg_ctrl;
+	int		ret = IRQ_NONE;
+	struct isp1301	*isp = _isp;
+	struct usb_otg	*otg = isp->phy.otg;
+
+	/* update ISP1301 transceiver from OTG controller */
+	if (otg_irq & OPRT_CHG) {
+		omap_writew(OPRT_CHG, OTG_IRQ_SRC);
+		isp1301_defer_work(isp, WORK_UPDATE_ISP);
+		ret = IRQ_HANDLED;
+
+	/* SRP to become b_peripheral failed */
+	} else if (otg_irq & B_SRP_TMROUT) {
+		pr_debug("otg: B_SRP_TIMEOUT, %06x\n", omap_readl(OTG_CTRL));
+		notresponding(isp);
+
+		/* gadget drivers that care should monitor all kinds of
+		 * remote wakeup (SRP, normal) using their own timer
+		 * to give "check cable and A-device" messages.
+		 */
+		if (isp->phy.otg->state == OTG_STATE_B_SRP_INIT)
+			b_idle(isp, "srp_timeout");
+
+		omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* HNP to become b_host failed */
+	} else if (otg_irq & B_HNP_FAIL) {
+		pr_debug("otg: %s B_HNP_FAIL, %06x\n",
+				state_name(isp), omap_readl(OTG_CTRL));
+		notresponding(isp);
+
+		otg_ctrl = omap_readl(OTG_CTRL);
+		otg_ctrl |= OTG_BUSDROP;
+		otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+		omap_writel(otg_ctrl, OTG_CTRL);
+
+		/* subset of b_peripheral()... */
+		isp->phy.otg->state = OTG_STATE_B_PERIPHERAL;
+		pr_debug("  --> b_peripheral\n");
+
+		omap_writew(B_HNP_FAIL, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* detect SRP from B-device ... */
+	} else if (otg_irq & A_SRP_DETECT) {
+		pr_debug("otg: %s SRP_DETECT, %06x\n",
+				state_name(isp), omap_readl(OTG_CTRL));
+
+		isp1301_defer_work(isp, WORK_UPDATE_OTG);
+		switch (isp->phy.otg->state) {
+		case OTG_STATE_A_IDLE:
+			if (!otg->host)
+				break;
+			isp1301_defer_work(isp, WORK_HOST_RESUME);
+			otg_ctrl = omap_readl(OTG_CTRL);
+			otg_ctrl |= OTG_A_BUSREQ;
+			otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ)
+					& ~OTG_XCEIV_INPUTS
+					& OTG_CTRL_MASK;
+			omap_writel(otg_ctrl, OTG_CTRL);
+			break;
+		default:
+			break;
+		}
+
+		omap_writew(A_SRP_DETECT, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* timer expired:  T(a_wait_bcon) and maybe T(a_wait_vrise)
+	 * we don't track them separately
+	 */
+	} else if (otg_irq & A_REQ_TMROUT) {
+		otg_ctrl = omap_readl(OTG_CTRL);
+		pr_info("otg: BCON_TMOUT from %s, %06x\n",
+				state_name(isp), otg_ctrl);
+		notresponding(isp);
+
+		otg_ctrl |= OTG_BUSDROP;
+		otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+		omap_writel(otg_ctrl, OTG_CTRL);
+		isp->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
+
+		omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* A-supplied voltage fell too low; overcurrent */
+	} else if (otg_irq & A_VBUS_ERR) {
+		otg_ctrl = omap_readl(OTG_CTRL);
+		printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n",
+			state_name(isp), otg_irq, otg_ctrl);
+
+		otg_ctrl |= OTG_BUSDROP;
+		otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+		omap_writel(otg_ctrl, OTG_CTRL);
+		isp->phy.otg->state = OTG_STATE_A_VBUS_ERR;
+
+		omap_writew(A_VBUS_ERR, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+	/* switch driver; the transceiver code activates it,
+	 * ungating the udc clock or resuming OHCI.
+	 */
+	} else if (otg_irq & DRIVER_SWITCH) {
+		int	kick = 0;
+
+		otg_ctrl = omap_readl(OTG_CTRL);
+		printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n",
+				state_name(isp),
+				(otg_ctrl & OTG_DRIVER_SEL)
+					? "gadget" : "host",
+				otg_ctrl);
+		isp1301_defer_work(isp, WORK_UPDATE_ISP);
+
+		/* role is peripheral */
+		if (otg_ctrl & OTG_DRIVER_SEL) {
+			switch (isp->phy.otg->state) {
+			case OTG_STATE_A_IDLE:
+				b_idle(isp, __func__);
+				break;
+			default:
+				break;
+			}
+			isp1301_defer_work(isp, WORK_UPDATE_ISP);
+
+		/* role is host */
+		} else {
+			if (!(otg_ctrl & OTG_ID)) {
+				otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
+				omap_writel(otg_ctrl | OTG_A_BUSREQ, OTG_CTRL);
+			}
+
+			if (otg->host) {
+				switch (isp->phy.otg->state) {
+				case OTG_STATE_B_WAIT_ACON:
+					isp->phy.otg->state = OTG_STATE_B_HOST;
+					pr_debug("  --> b_host\n");
+					kick = 1;
+					break;
+				case OTG_STATE_A_WAIT_BCON:
+					isp->phy.otg->state = OTG_STATE_A_HOST;
+					pr_debug("  --> a_host\n");
+					break;
+				case OTG_STATE_A_PERIPHERAL:
+					isp->phy.otg->state = OTG_STATE_A_WAIT_BCON;
+					pr_debug("  --> a_wait_bcon\n");
+					break;
+				default:
+					break;
+				}
+				isp1301_defer_work(isp, WORK_HOST_RESUME);
+			}
+		}
+
+		omap_writew(DRIVER_SWITCH, OTG_IRQ_SRC);
+		ret = IRQ_HANDLED;
+
+		if (kick)
+			usb_bus_start_enum(otg->host, otg->host->otg_port);
+	}
+
+	check_state(isp, __func__);
+	return ret;
+}
+
+static struct platform_device *otg_dev;
+
+static int isp1301_otg_init(struct isp1301 *isp)
+{
+	u32 l;
+
+	if (!otg_dev)
+		return -ENODEV;
+
+	dump_regs(isp, __func__);
+	/* some of these values are board-specific... */
+	l = omap_readl(OTG_SYSCON_2);
+	l |= OTG_EN
+		/* for B-device: */
+		| SRP_GPDATA		/* 9msec Bdev D+ pulse */
+		| SRP_GPDVBUS		/* discharge after VBUS pulse */
+		// | (3 << 24)		/* 2msec VBUS pulse */
+		/* for A-device: */
+		| (0 << 20)		/* 200ms nominal A_WAIT_VRISE timer */
+		| SRP_DPW		/* detect 167+ns SRP pulses */
+		| SRP_DATA | SRP_VBUS	/* accept both kinds of SRP pulse */
+		;
+	omap_writel(l, OTG_SYSCON_2);
+
+	update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE));
+	update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS));
+
+	check_state(isp, __func__);
+	pr_debug("otg: %s, %s %06x\n",
+			state_name(isp), __func__, omap_readl(OTG_CTRL));
+
+	omap_writew(DRIVER_SWITCH | OPRT_CHG
+			| B_SRP_TMROUT | B_HNP_FAIL
+			| A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT, OTG_IRQ_EN);
+
+	l = omap_readl(OTG_SYSCON_2);
+	l |= OTG_EN;
+	omap_writel(l, OTG_SYSCON_2);
+
+	return 0;
+}
+
+static int otg_probe(struct platform_device *dev)
+{
+	// struct omap_usb_config *config = dev->platform_data;
+
+	otg_dev = dev;
+	return 0;
+}
+
+static int otg_remove(struct platform_device *dev)
+{
+	otg_dev = NULL;
+	return 0;
+}
+
+static struct platform_driver omap_otg_driver = {
+	.probe		= otg_probe,
+	.remove		= otg_remove,
+	.driver		= {
+		.name	= "omap_otg",
+	},
+};
+
+static int otg_bind(struct isp1301 *isp)
+{
+	int	status;
+
+	if (otg_dev)
+		return -EBUSY;
+
+	status = platform_driver_register(&omap_otg_driver);
+	if (status < 0)
+		return status;
+
+	if (otg_dev)
+		status = request_irq(otg_dev->resource[1].start, omap_otg_irq,
+				0, DRIVER_NAME, isp);
+	else
+		status = -ENODEV;
+
+	if (status < 0)
+		platform_driver_unregister(&omap_otg_driver);
+	return status;
+}
+
+static void otg_unbind(struct isp1301 *isp)
+{
+	if (!otg_dev)
+		return;
+	free_irq(otg_dev->resource[1].start, isp);
+}
+
+#else
+
+/* OTG controller isn't clocked */
+
+#endif	/* CONFIG_USB_OTG */
+
+/*-------------------------------------------------------------------------*/
+
+static void b_peripheral(struct isp1301 *isp)
+{
+	u32 l;
+
+	l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+	omap_writel(l, OTG_CTRL);
+
+	usb_gadget_vbus_connect(isp->phy.otg->gadget);
+
+#ifdef	CONFIG_USB_OTG
+	enable_vbus_draw(isp, 8);
+	otg_update_isp(isp);
+#else
+	enable_vbus_draw(isp, 100);
+	/* UDC driver just set OTG_BSESSVLD */
+	isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP);
+	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN);
+	isp->phy.otg->state = OTG_STATE_B_PERIPHERAL;
+	pr_debug("  --> b_peripheral\n");
+	dump_regs(isp, "2periph");
+#endif
+}
+
+static void isp_update_otg(struct isp1301 *isp, u8 stat)
+{
+	struct usb_otg		*otg = isp->phy.otg;
+	u8			isp_stat, isp_bstat;
+	enum usb_otg_state	state = isp->phy.otg->state;
+
+	if (stat & INTR_BDIS_ACON)
+		pr_debug("OTG:  BDIS_ACON, %s\n", state_name(isp));
+
+	/* start certain state transitions right away */
+	isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE);
+	if (isp_stat & INTR_ID_GND) {
+		if (otg->default_a) {
+			switch (state) {
+			case OTG_STATE_B_IDLE:
+				a_idle(isp, "idle");
+				/* FALLTHROUGH */
+			case OTG_STATE_A_IDLE:
+				enable_vbus_source(isp);
+				/* FALLTHROUGH */
+			case OTG_STATE_A_WAIT_VRISE:
+				/* we skip over OTG_STATE_A_WAIT_BCON, since
+				 * the HC will transition to A_HOST (or
+				 * A_SUSPEND!) without our noticing except
+				 * when HNP is used.
+				 */
+				if (isp_stat & INTR_VBUS_VLD)
+					isp->phy.otg->state = OTG_STATE_A_HOST;
+				break;
+			case OTG_STATE_A_WAIT_VFALL:
+				if (!(isp_stat & INTR_SESS_VLD))
+					a_idle(isp, "vfell");
+				break;
+			default:
+				if (!(isp_stat & INTR_VBUS_VLD))
+					isp->phy.otg->state = OTG_STATE_A_VBUS_ERR;
+				break;
+			}
+			isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+		} else {
+			switch (state) {
+			case OTG_STATE_B_PERIPHERAL:
+			case OTG_STATE_B_HOST:
+			case OTG_STATE_B_WAIT_ACON:
+				usb_gadget_vbus_disconnect(otg->gadget);
+				break;
+			default:
+				break;
+			}
+			if (state != OTG_STATE_A_IDLE)
+				a_idle(isp, "id");
+			if (otg->host && state == OTG_STATE_A_IDLE)
+				isp1301_defer_work(isp, WORK_HOST_RESUME);
+			isp_bstat = 0;
+		}
+	} else {
+		u32 l;
+
+		/* if user unplugged mini-A end of cable,
+		 * don't bypass A_WAIT_VFALL.
+		 */
+		if (otg->default_a) {
+			switch (state) {
+			default:
+				isp->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
+				break;
+			case OTG_STATE_A_WAIT_VFALL:
+				state = OTG_STATE_A_IDLE;
+				/* hub_wq may take a while to notice and
+				 * handle this disconnect, so don't go
+				 * to B_IDLE quite yet.
+				 */
+				break;
+			case OTG_STATE_A_IDLE:
+				host_suspend(isp);
+				isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1,
+						MC1_BDIS_ACON_EN);
+				isp->phy.otg->state = OTG_STATE_B_IDLE;
+				l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+				l &= ~OTG_CTRL_BITS;
+				omap_writel(l, OTG_CTRL);
+				break;
+			case OTG_STATE_B_IDLE:
+				break;
+			}
+		}
+		isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
+
+		switch (isp->phy.otg->state) {
+		case OTG_STATE_B_PERIPHERAL:
+		case OTG_STATE_B_WAIT_ACON:
+		case OTG_STATE_B_HOST:
+			if (likely(isp_bstat & OTG_B_SESS_VLD))
+				break;
+			enable_vbus_draw(isp, 0);
+#ifndef	CONFIG_USB_OTG
+			/* UDC driver will clear OTG_BSESSVLD */
+			isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1,
+						OTG1_DP_PULLDOWN);
+			isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1,
+						OTG1_DP_PULLUP);
+			dump_regs(isp, __func__);
+#endif
+			/* FALLTHROUGH */
+		case OTG_STATE_B_SRP_INIT:
+			b_idle(isp, __func__);
+			l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
+			omap_writel(l, OTG_CTRL);
+			/* FALLTHROUGH */
+		case OTG_STATE_B_IDLE:
+			if (otg->gadget && (isp_bstat & OTG_B_SESS_VLD)) {
+#ifdef	CONFIG_USB_OTG
+				update_otg1(isp, isp_stat);
+				update_otg2(isp, isp_bstat);
+#endif
+				b_peripheral(isp);
+			} else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD)))
+				isp_bstat |= OTG_B_SESS_END;
+			break;
+		case OTG_STATE_A_WAIT_VFALL:
+			break;
+		default:
+			pr_debug("otg: unsupported b-device %s\n",
+				state_name(isp));
+			break;
+		}
+	}
+
+	if (state != isp->phy.otg->state)
+		pr_debug("  isp, %s -> %s\n",
+				usb_otg_state_string(state), state_name(isp));
+
+#ifdef	CONFIG_USB_OTG
+	/* update the OTG controller state to match the isp1301; may
+	 * trigger OPRT_CHG irqs for changes going to the isp1301.
+	 */
+	update_otg1(isp, isp_stat);
+	update_otg2(isp, isp_bstat);
+	check_state(isp, __func__);
+#endif
+
+	dump_regs(isp, "isp1301->otg");
+}
+
+/*-------------------------------------------------------------------------*/
+
+static u8 isp1301_clear_latch(struct isp1301 *isp)
+{
+	u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH);
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch);
+	return latch;
+}
+
+static void
+isp1301_work(struct work_struct *work)
+{
+	struct isp1301	*isp = container_of(work, struct isp1301, work);
+	int		stop;
+
+	/* implicit lock:  we're the only task using this device */
+	isp->working = 1;
+	do {
+		stop = test_bit(WORK_STOP, &isp->todo);
+
+#ifdef	CONFIG_USB_OTG
+		/* transfer state from otg engine to isp1301 */
+		if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) {
+			otg_update_isp(isp);
+			put_device(&isp->client->dev);
+		}
+#endif
+		/* transfer state from isp1301 to otg engine */
+		if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) {
+			u8		stat = isp1301_clear_latch(isp);
+
+			isp_update_otg(isp, stat);
+			put_device(&isp->client->dev);
+		}
+
+		if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) {
+			u32	otg_ctrl;
+
+			/*
+			 * skip A_WAIT_VRISE; hc transitions invisibly
+			 * skip A_WAIT_BCON; same.
+			 */
+			switch (isp->phy.otg->state) {
+			case OTG_STATE_A_WAIT_BCON:
+			case OTG_STATE_A_WAIT_VRISE:
+				isp->phy.otg->state = OTG_STATE_A_HOST;
+				pr_debug("  --> a_host\n");
+				otg_ctrl = omap_readl(OTG_CTRL);
+				otg_ctrl |= OTG_A_BUSREQ;
+				otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ)
+						& OTG_CTRL_MASK;
+				omap_writel(otg_ctrl, OTG_CTRL);
+				break;
+			case OTG_STATE_B_WAIT_ACON:
+				isp->phy.otg->state = OTG_STATE_B_HOST;
+				pr_debug("  --> b_host (acon)\n");
+				break;
+			case OTG_STATE_B_HOST:
+			case OTG_STATE_B_IDLE:
+			case OTG_STATE_A_IDLE:
+				break;
+			default:
+				pr_debug("  host resume in %s\n",
+						state_name(isp));
+			}
+			host_resume(isp);
+			// mdelay(10);
+			put_device(&isp->client->dev);
+		}
+
+		if (test_and_clear_bit(WORK_TIMER, &isp->todo)) {
+#ifdef	VERBOSE
+			dump_regs(isp, "timer");
+			if (!stop)
+				mod_timer(&isp->timer, jiffies + TIMER_JIFFIES);
+#endif
+			put_device(&isp->client->dev);
+		}
+
+		if (isp->todo)
+			dev_vdbg(&isp->client->dev,
+				"work done, todo = 0x%lx\n",
+				isp->todo);
+		if (stop) {
+			dev_dbg(&isp->client->dev, "stop\n");
+			break;
+		}
+	} while (isp->todo);
+	isp->working = 0;
+}
+
+static irqreturn_t isp1301_irq(int irq, void *isp)
+{
+	isp1301_defer_work(isp, WORK_UPDATE_OTG);
+	return IRQ_HANDLED;
+}
+
+static void isp1301_timer(struct timer_list *t)
+{
+	struct isp1301 *isp = from_timer(isp, t, timer);
+
+	isp1301_defer_work(isp, WORK_TIMER);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void isp1301_release(struct device *dev)
+{
+	struct isp1301	*isp;
+
+	isp = dev_get_drvdata(dev);
+
+	/* FIXME -- not with a "new style" driver, it doesn't!! */
+
+	/* ugly -- i2c hijacks our memory hook to wait_for_completion() */
+	if (isp->i2c_release)
+		isp->i2c_release(dev);
+	kfree(isp->phy.otg);
+	kfree (isp);
+}
+
+static struct isp1301 *the_transceiver;
+
+static int isp1301_remove(struct i2c_client *i2c)
+{
+	struct isp1301	*isp;
+
+	isp = i2c_get_clientdata(i2c);
+
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0);
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0);
+	free_irq(i2c->irq, isp);
+#ifdef	CONFIG_USB_OTG
+	otg_unbind(isp);
+#endif
+	if (machine_is_omap_h2())
+		gpio_free(2);
+
+	set_bit(WORK_STOP, &isp->todo);
+	del_timer_sync(&isp->timer);
+	flush_work(&isp->work);
+
+	put_device(&i2c->dev);
+	the_transceiver = NULL;
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* NOTE:  three modes are possible here, only one of which
+ * will be standards-conformant on any given system:
+ *
+ *  - OTG mode (dual-role), required if there's a Mini-AB connector
+ *  - HOST mode, for when there's one or more A (host) connectors
+ *  - DEVICE mode, for when there's a B/Mini-B (device) connector
+ *
+ * As a rule, you won't have an isp1301 chip unless it's there to
+ * support the OTG mode.  Other modes help testing USB controllers
+ * in isolation from (full) OTG support, or maybe so later board
+ * revisions can help to support those feature.
+ */
+
+#ifdef	CONFIG_USB_OTG
+
+static int isp1301_otg_enable(struct isp1301 *isp)
+{
+	power_up(isp);
+	isp1301_otg_init(isp);
+
+	/* NOTE:  since we don't change this, this provides
+	 * a few more interrupts than are strictly needed.
+	 */
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+		INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+		INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
+
+	dev_info(&isp->client->dev, "ready for dual-role USB ...\n");
+
+	return 0;
+}
+
+#endif
+
+/* add or disable the host device+driver */
+static int
+isp1301_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct isp1301	*isp = container_of(otg->usb_phy, struct isp1301, phy);
+
+	if (isp != the_transceiver)
+		return -ENODEV;
+
+	if (!host) {
+		omap_writew(0, OTG_IRQ_EN);
+		power_down(isp);
+		otg->host = NULL;
+		return 0;
+	}
+
+#ifdef	CONFIG_USB_OTG
+	otg->host = host;
+	dev_dbg(&isp->client->dev, "registered host\n");
+	host_suspend(isp);
+	if (otg->gadget)
+		return isp1301_otg_enable(isp);
+	return 0;
+
+#elif !IS_ENABLED(CONFIG_USB_OMAP)
+	// FIXME update its refcount
+	otg->host = host;
+
+	power_up(isp);
+
+	if (machine_is_omap_h2())
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+
+	dev_info(&isp->client->dev, "A-Host sessions ok\n");
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+		INTR_ID_GND);
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+		INTR_ID_GND);
+
+	/* If this has a Mini-AB connector, this mode is highly
+	 * nonstandard ... but can be handy for testing, especially with
+	 * the Mini-A end of an OTG cable.  (Or something nonstandard
+	 * like MiniB-to-StandardB, maybe built with a gender mender.)
+	 */
+	isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV);
+
+	dump_regs(isp, __func__);
+
+	return 0;
+
+#else
+	dev_dbg(&isp->client->dev, "host sessions not allowed\n");
+	return -EINVAL;
+#endif
+
+}
+
+static int
+isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
+{
+	struct isp1301	*isp = container_of(otg->usb_phy, struct isp1301, phy);
+
+	if (isp != the_transceiver)
+		return -ENODEV;
+
+	if (!gadget) {
+		omap_writew(0, OTG_IRQ_EN);
+		if (!otg->default_a)
+			enable_vbus_draw(isp, 0);
+		usb_gadget_vbus_disconnect(otg->gadget);
+		otg->gadget = NULL;
+		power_down(isp);
+		return 0;
+	}
+
+#ifdef	CONFIG_USB_OTG
+	otg->gadget = gadget;
+	dev_dbg(&isp->client->dev, "registered gadget\n");
+	/* gadget driver may be suspended until vbus_connect () */
+	if (otg->host)
+		return isp1301_otg_enable(isp);
+	return 0;
+
+#elif	!defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE)
+	otg->gadget = gadget;
+	// FIXME update its refcount
+
+	{
+		u32 l;
+
+		l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
+		l &= ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS);
+		l |= OTG_ID;
+		omap_writel(l, OTG_CTRL);
+	}
+
+	power_up(isp);
+	isp->phy.otg->state = OTG_STATE_B_IDLE;
+
+	if (machine_is_omap_h2() || machine_is_omap_h3())
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
+
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
+		INTR_SESS_VLD);
+	isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
+		INTR_VBUS_VLD);
+	dev_info(&isp->client->dev, "B-Peripheral sessions ok\n");
+	dump_regs(isp, __func__);
+
+	/* If this has a Mini-AB connector, this mode is highly
+	 * nonstandard ... but can be handy for testing, so long
+	 * as you don't plug a Mini-A cable into the jack.
+	 */
+	if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD)
+		b_peripheral(isp);
+
+	return 0;
+
+#else
+	dev_dbg(&isp->client->dev, "peripheral sessions not allowed\n");
+	return -EINVAL;
+#endif
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int
+isp1301_set_power(struct usb_phy *dev, unsigned mA)
+{
+	if (!the_transceiver)
+		return -ENODEV;
+	if (dev->otg->state == OTG_STATE_B_PERIPHERAL)
+		enable_vbus_draw(the_transceiver, mA);
+	return 0;
+}
+
+static int
+isp1301_start_srp(struct usb_otg *otg)
+{
+	struct isp1301	*isp = container_of(otg->usb_phy, struct isp1301, phy);
+	u32		otg_ctrl;
+
+	if (isp != the_transceiver || isp->phy.otg->state != OTG_STATE_B_IDLE)
+		return -ENODEV;
+
+	otg_ctrl = omap_readl(OTG_CTRL);
+	if (!(otg_ctrl & OTG_BSESSEND))
+		return -EINVAL;
+
+	otg_ctrl |= OTG_B_BUSREQ;
+	otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK;
+	omap_writel(otg_ctrl, OTG_CTRL);
+	isp->phy.otg->state = OTG_STATE_B_SRP_INIT;
+
+	pr_debug("otg: SRP, %s ... %06x\n", state_name(isp),
+			omap_readl(OTG_CTRL));
+#ifdef	CONFIG_USB_OTG
+	check_state(isp, __func__);
+#endif
+	return 0;
+}
+
+static int
+isp1301_start_hnp(struct usb_otg *otg)
+{
+#ifdef	CONFIG_USB_OTG
+	struct isp1301	*isp = container_of(otg->usb_phy, struct isp1301, phy);
+	u32 l;
+
+	if (isp != the_transceiver)
+		return -ENODEV;
+	if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable))
+		return -ENOTCONN;
+	if (!otg->default_a && (otg->gadget == NULL
+			|| !otg->gadget->b_hnp_enable))
+		return -ENOTCONN;
+
+	/* We want hardware to manage most HNP protocol timings.
+	 * So do this part as early as possible...
+	 */
+	switch (isp->phy.otg->state) {
+	case OTG_STATE_B_HOST:
+		isp->phy.otg->state = OTG_STATE_B_PERIPHERAL;
+		/* caller will suspend next */
+		break;
+	case OTG_STATE_A_HOST:
+#if 0
+		/* autoconnect mode avoids irq latency bugs */
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1,
+				MC1_BDIS_ACON_EN);
+#endif
+		/* caller must suspend then clear A_BUSREQ */
+		usb_gadget_vbus_connect(otg->gadget);
+		l = omap_readl(OTG_CTRL);
+		l |= OTG_A_SETB_HNPEN;
+		omap_writel(l, OTG_CTRL);
+
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		/* initiated by B-Host suspend */
+		break;
+	default:
+		return -EILSEQ;
+	}
+	pr_debug("otg: HNP %s, %06x ...\n",
+		state_name(isp), omap_readl(OTG_CTRL));
+	check_state(isp, __func__);
+	return 0;
+#else
+	/* srp-only */
+	return -EINVAL;
+#endif
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
+{
+	int			status;
+	struct isp1301		*isp;
+
+	if (the_transceiver)
+		return 0;
+
+	isp = kzalloc(sizeof *isp, GFP_KERNEL);
+	if (!isp)
+		return 0;
+
+	isp->phy.otg = kzalloc(sizeof *isp->phy.otg, GFP_KERNEL);
+	if (!isp->phy.otg) {
+		kfree(isp);
+		return 0;
+	}
+
+	INIT_WORK(&isp->work, isp1301_work);
+	timer_setup(&isp->timer, isp1301_timer, 0);
+
+	i2c_set_clientdata(i2c, isp);
+	isp->client = i2c;
+
+	/* verify the chip (shouldn't be necessary) */
+	status = isp1301_get_u16(isp, ISP1301_VENDOR_ID);
+	if (status != I2C_VENDOR_ID_PHILIPS) {
+		dev_dbg(&i2c->dev, "not philips id: %d\n", status);
+		goto fail;
+	}
+	status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID);
+	if (status != I2C_PRODUCT_ID_PHILIPS_1301) {
+		dev_dbg(&i2c->dev, "not isp1301, %d\n", status);
+		goto fail;
+	}
+	isp->i2c_release = i2c->dev.release;
+	i2c->dev.release = isp1301_release;
+
+	/* initial development used chiprev 2.00 */
+	status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE);
+	dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n",
+		status >> 8, status & 0xff);
+
+	/* make like power-on reset */
+	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK);
+
+	isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI);
+	isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI);
+
+	isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1,
+				OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN);
+	isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1,
+				~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN));
+
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0);
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0);
+	isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0);
+
+#ifdef	CONFIG_USB_OTG
+	status = otg_bind(isp);
+	if (status < 0) {
+		dev_dbg(&i2c->dev, "can't bind OTG\n");
+		goto fail;
+	}
+#endif
+
+	if (machine_is_omap_h2()) {
+		/* full speed signaling by default */
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1,
+			MC1_SPEED);
+		isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2,
+			MC2_SPD_SUSP_CTRL);
+
+		/* IRQ wired at M14 */
+		omap_cfg_reg(M14_1510_GPIO2);
+		if (gpio_request(2, "isp1301") == 0)
+			gpio_direction_input(2);
+		isp->irq_type = IRQF_TRIGGER_FALLING;
+	}
+
+	status = request_irq(i2c->irq, isp1301_irq,
+			isp->irq_type, DRIVER_NAME, isp);
+	if (status < 0) {
+		dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n",
+				i2c->irq, status);
+		goto fail;
+	}
+
+	isp->phy.dev = &i2c->dev;
+	isp->phy.label = DRIVER_NAME;
+	isp->phy.set_power = isp1301_set_power,
+
+	isp->phy.otg->usb_phy = &isp->phy;
+	isp->phy.otg->set_host = isp1301_set_host,
+	isp->phy.otg->set_peripheral = isp1301_set_peripheral,
+	isp->phy.otg->start_srp = isp1301_start_srp,
+	isp->phy.otg->start_hnp = isp1301_start_hnp,
+
+	enable_vbus_draw(isp, 0);
+	power_down(isp);
+	the_transceiver = isp;
+
+#ifdef	CONFIG_USB_OTG
+	update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE));
+	update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS));
+#endif
+
+	dump_regs(isp, __func__);
+
+#ifdef	VERBOSE
+	mod_timer(&isp->timer, jiffies + TIMER_JIFFIES);
+	dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES);
+#endif
+
+	status = usb_add_phy(&isp->phy, USB_PHY_TYPE_USB2);
+	if (status < 0)
+		dev_err(&i2c->dev, "can't register transceiver, %d\n",
+			status);
+
+	return 0;
+
+fail:
+	kfree(isp->phy.otg);
+	kfree(isp);
+	return -ENODEV;
+}
+
+static const struct i2c_device_id isp1301_id[] = {
+	{ "isp1301_omap", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, isp1301_id);
+
+static struct i2c_driver isp1301_driver = {
+	.driver = {
+		.name	= "isp1301_omap",
+	},
+	.probe		= isp1301_probe,
+	.remove		= isp1301_remove,
+	.id_table	= isp1301_id,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init isp_init(void)
+{
+	return i2c_add_driver(&isp1301_driver);
+}
+subsys_initcall(isp_init);
+
+static void __exit isp_exit(void)
+{
+	if (the_transceiver)
+		usb_remove_phy(&the_transceiver->phy);
+	i2c_del_driver(&isp1301_driver);
+}
+module_exit(isp_exit);
+
diff --git a/marvell/linux/drivers/usb/phy/phy-isp1301.c b/marvell/linux/drivers/usb/phy/phy-isp1301.c
new file mode 100644
index 0000000..6cf6fbd
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-isp1301.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * NXP ISP1301 USB transceiver driver
+ *
+ * Copyright (C) 2012 Roland Stigge
+ *
+ * Author: Roland Stigge <stigge@antcom.de>
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/isp1301.h>
+
+#define DRV_NAME		"isp1301"
+
+struct isp1301 {
+	struct usb_phy		phy;
+	struct mutex		mutex;
+
+	struct i2c_client	*client;
+};
+
+#define phy_to_isp(p)		(container_of((p), struct isp1301, phy))
+
+static const struct i2c_device_id isp1301_id[] = {
+	{ "isp1301", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, isp1301_id);
+
+static const struct of_device_id isp1301_of_match[] = {
+	{.compatible = "nxp,isp1301" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, isp1301_of_match);
+
+static struct i2c_client *isp1301_i2c_client;
+
+static int __isp1301_write(struct isp1301 *isp, u8 reg, u8 value, u8 clear)
+{
+	return i2c_smbus_write_byte_data(isp->client, reg | clear, value);
+}
+
+static int isp1301_write(struct isp1301 *isp, u8 reg, u8 value)
+{
+	return __isp1301_write(isp, reg, value, 0);
+}
+
+static int isp1301_clear(struct isp1301 *isp, u8 reg, u8 value)
+{
+	return __isp1301_write(isp, reg, value, ISP1301_I2C_REG_CLEAR_ADDR);
+}
+
+static int isp1301_phy_init(struct usb_phy *phy)
+{
+	struct isp1301 *isp = phy_to_isp(phy);
+
+	/* Disable transparent UART mode first */
+	isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_UART_EN);
+	isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_1, ~MC1_SPEED_REG);
+	isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_SPEED_REG);
+	isp1301_clear(isp, ISP1301_I2C_MODE_CONTROL_2, ~0);
+	isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_2, (MC2_BI_DI | MC2_PSW_EN
+				| MC2_SPD_SUSP_CTRL));
+
+	isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, ~0);
+	isp1301_write(isp, ISP1301_I2C_MODE_CONTROL_1, MC1_DAT_SE0);
+	isp1301_write(isp, ISP1301_I2C_OTG_CONTROL_1, (OTG1_DM_PULLDOWN
+				| OTG1_DP_PULLDOWN));
+	isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, (OTG1_DM_PULLUP
+				| OTG1_DP_PULLUP));
+
+	/* mask all interrupts */
+	isp1301_clear(isp, ISP1301_I2C_INTERRUPT_LATCH, ~0);
+	isp1301_clear(isp, ISP1301_I2C_INTERRUPT_FALLING, ~0);
+	isp1301_clear(isp, ISP1301_I2C_INTERRUPT_RISING, ~0);
+
+	return 0;
+}
+
+static int isp1301_phy_set_vbus(struct usb_phy *phy, int on)
+{
+	struct isp1301 *isp = phy_to_isp(phy);
+
+	if (on)
+		isp1301_write(isp, ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV);
+	else
+		isp1301_clear(isp, ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV);
+
+	return 0;
+}
+
+static int isp1301_probe(struct i2c_client *client,
+			 const struct i2c_device_id *i2c_id)
+{
+	struct isp1301 *isp;
+	struct usb_phy *phy;
+
+	isp = devm_kzalloc(&client->dev, sizeof(*isp), GFP_KERNEL);
+	if (!isp)
+		return -ENOMEM;
+
+	isp->client = client;
+	mutex_init(&isp->mutex);
+
+	phy = &isp->phy;
+	phy->dev = &client->dev;
+	phy->label = DRV_NAME;
+	phy->init = isp1301_phy_init;
+	phy->set_vbus = isp1301_phy_set_vbus;
+	phy->type = USB_PHY_TYPE_USB2;
+
+	i2c_set_clientdata(client, isp);
+	usb_add_phy_dev(phy);
+
+	isp1301_i2c_client = client;
+
+	return 0;
+}
+
+static int isp1301_remove(struct i2c_client *client)
+{
+	struct isp1301 *isp = i2c_get_clientdata(client);
+
+	usb_remove_phy(&isp->phy);
+	isp1301_i2c_client = NULL;
+
+	return 0;
+}
+
+static struct i2c_driver isp1301_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.of_match_table = isp1301_of_match,
+	},
+	.probe = isp1301_probe,
+	.remove = isp1301_remove,
+	.id_table = isp1301_id,
+};
+
+module_i2c_driver(isp1301_driver);
+
+static int match(struct device *dev, const void *data)
+{
+	const struct device_node *node = (const struct device_node *)data;
+	return (dev->of_node == node) &&
+		(dev->driver == &isp1301_driver.driver);
+}
+
+struct i2c_client *isp1301_get_client(struct device_node *node)
+{
+	if (node) { /* reference of ISP1301 I2C node via DT */
+		struct device *dev = bus_find_device(&i2c_bus_type, NULL,
+						     node, match);
+		if (!dev)
+			return NULL;
+		return to_i2c_client(dev);
+	} else { /* non-DT: only one ISP1301 chip supported */
+		return isp1301_i2c_client;
+	}
+}
+EXPORT_SYMBOL_GPL(isp1301_get_client);
+
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("NXP ISP1301 USB transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/drivers/usb/phy/phy-keystone.c b/marvell/linux/drivers/usb/phy/phy-keystone.c
new file mode 100644
index 0000000..1987126
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-keystone.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * phy-keystone - USB PHY, talking to dwc3 controller in Keystone.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: WingMan Kwok <w-kwok2@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/usb_phy_generic.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "phy-generic.h"
+
+/* USB PHY control register offsets */
+#define USB_PHY_CTL_UTMI		0x0000
+#define USB_PHY_CTL_PIPE		0x0004
+#define USB_PHY_CTL_PARAM_1		0x0008
+#define USB_PHY_CTL_PARAM_2		0x000c
+#define USB_PHY_CTL_CLOCK		0x0010
+#define USB_PHY_CTL_PLL			0x0014
+
+#define PHY_REF_SSP_EN			BIT(29)
+
+struct keystone_usbphy {
+	struct usb_phy_generic	usb_phy_gen;
+	void __iomem			*phy_ctrl;
+};
+
+static inline u32 keystone_usbphy_readl(void __iomem *base, u32 offset)
+{
+	return readl(base + offset);
+}
+
+static inline void keystone_usbphy_writel(void __iomem *base,
+					  u32 offset, u32 value)
+{
+	writel(value, base + offset);
+}
+
+static int keystone_usbphy_init(struct usb_phy *phy)
+{
+	struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev);
+	u32 val;
+
+	val  = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK);
+	keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK,
+				val | PHY_REF_SSP_EN);
+	return 0;
+}
+
+static void keystone_usbphy_shutdown(struct usb_phy *phy)
+{
+	struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev);
+	u32 val;
+
+	val  = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK);
+	keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK,
+				val &= ~PHY_REF_SSP_EN);
+}
+
+static int keystone_usbphy_probe(struct platform_device *pdev)
+{
+	struct device		*dev = &pdev->dev;
+	struct keystone_usbphy	*k_phy;
+	struct resource		*res;
+	int ret;
+
+	k_phy = devm_kzalloc(dev, sizeof(*k_phy), GFP_KERNEL);
+	if (!k_phy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	k_phy->phy_ctrl = devm_ioremap_resource(dev, res);
+	if (IS_ERR(k_phy->phy_ctrl))
+		return PTR_ERR(k_phy->phy_ctrl);
+
+	ret = usb_phy_gen_create_phy(dev, &k_phy->usb_phy_gen, NULL);
+	if (ret)
+		return ret;
+
+	k_phy->usb_phy_gen.phy.init = keystone_usbphy_init;
+	k_phy->usb_phy_gen.phy.shutdown = keystone_usbphy_shutdown;
+
+	platform_set_drvdata(pdev, k_phy);
+
+	return usb_add_phy_dev(&k_phy->usb_phy_gen.phy);
+}
+
+static int keystone_usbphy_remove(struct platform_device *pdev)
+{
+	struct keystone_usbphy *k_phy = platform_get_drvdata(pdev);
+
+	usb_remove_phy(&k_phy->usb_phy_gen.phy);
+
+	return 0;
+}
+
+static const struct of_device_id keystone_usbphy_ids[] = {
+	{ .compatible = "ti,keystone-usbphy" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, keystone_usbphy_ids);
+
+static struct platform_driver keystone_usbphy_driver = {
+	.probe          = keystone_usbphy_probe,
+	.remove         = keystone_usbphy_remove,
+	.driver         = {
+		.name   = "keystone-usbphy",
+		.of_match_table = keystone_usbphy_ids,
+	},
+};
+
+module_platform_driver(keystone_usbphy_driver);
+
+MODULE_ALIAS("platform:keystone-usbphy");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("Keystone USB phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/marvell/linux/drivers/usb/phy/phy-mv-connector.c b/marvell/linux/drivers/usb/phy/phy-mv-connector.c
new file mode 100644
index 0000000..fdfe720
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-mv-connector.c
@@ -0,0 +1,82 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/platform_data/mv_usb.h>
+
+struct pxa_usb_extern_dev {
+	unsigned int id;
+	struct pxa_usb_extern_ops ops;
+	struct atomic_notifier_head *head;
+};
+
+static struct pxa_usb_extern_dev pxa_usb[PXA_USB_DEV_MAX];
+
+struct pxa_usb_extern_ops *pxa_usb_get_extern_ops(unsigned int id)
+{
+	if (id >= PXA_USB_DEV_MAX)
+		return NULL;
+
+	return &pxa_usb[id].ops;
+}
+
+int pxa_usb_register_notifier(unsigned int id, struct notifier_block *nb)
+{
+	struct pxa_usb_extern_dev *dev;
+	int ret;
+
+	if (id >= PXA_USB_DEV_MAX)
+		return -ENODEV;
+
+	dev = &pxa_usb[id];
+	if (dev->head == NULL) {
+		dev->head = kzalloc(sizeof(*dev->head), GFP_KERNEL);
+		if (dev->head == NULL)
+			return -ENOMEM;
+		ATOMIC_INIT_NOTIFIER_HEAD(dev->head);
+	}
+
+	ret = atomic_notifier_chain_register(dev->head, nb);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int pxa_usb_unregister_notifier(unsigned int id, struct notifier_block *nb)
+{
+	struct pxa_usb_extern_dev *dev;
+	int ret;
+
+	if (id >= PXA_USB_DEV_MAX)
+		return -ENODEV;
+
+	dev = &pxa_usb[id];
+	if (dev->head == NULL)
+		return -EINVAL;
+
+	ret = atomic_notifier_chain_unregister(dev->head, nb);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int pxa_usb_notify(unsigned int id, unsigned long val, void *v)
+{
+	struct pxa_usb_extern_dev *dev;
+	int ret;
+
+	if (id >= PXA_USB_DEV_MAX)
+		return -ENODEV;
+
+	dev = &pxa_usb[id];
+	if (dev->head == NULL)
+		return -EINVAL;
+
+	ret = atomic_notifier_call_chain(dev->head, val, v);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/marvell/linux/drivers/usb/phy/phy-mv-usb.c b/marvell/linux/drivers/usb/phy/phy-mv-usb.c
new file mode 100644
index 0000000..d094df0
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-mv-usb.c
@@ -0,0 +1,1727 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ * Author: Chao Xie <chao.xie@marvell.com>
+ *	   Neil Zhang <zhangwm@marvell.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/mv_usb2_phy.h>
+#include <linux/platform_data/mv_usb.h>
+#include <linux/pm_qos.h>
+#include <soc/asr/regs-addr.h>
+#include <linux/gpio.h>
+#include <linux/cputype.h>
+#include <linux/edge_wakeup_mmp.h>
+
+#include "phy-mv-usb.h"
+
+#ifndef CONFIG_CPU_ASR18XX
+#define CONFIG_CPU_ASR18XX 1
+#endif
+
+#define	DRIVER_DESC	"Marvell USB OTG transceiver driver"
+#define	DRIVER_VERSION	"Jan 20, 2010"
+#define MAX_RETRY_TIMES 60
+#define RETRY_SLEEP_MS  1000
+#define ENNUM		-1
+
+#define APMU_SD_ROT_WAKE_CLR 0x7C
+#if defined(CONFIG_CPU_ASR18XX)
+#define USB_OTG_ID_WAKEUP_EN (1<<22)
+#define USB_OTG_ID_WAKEUP_CLR (1<<23)
+#define USB_LINE_STAT0_WAKEUP_EN (1<<9)
+#define USB_LINE_STAT1_WAKEUP_EN (1<<10)
+#define USB_VBUS_WAKE_CLEAR	 (1<<4)
+#define USB_ID_WAKE_CLEAR	 (1<<7)
+#else
+#define USB_OTG_ID_WAKEUP_EN (1<<8)
+#define USB_OTG_ID_WAKEUP_CLR (1<<18)
+#define USB_ID_WAKE_CLEAR	 (1<<7)
+#endif
+#define MV_HSIC_WAKEUP_EN_BIT	(0x1 << 24)
+
+#define ASR_OTGSC_ID_RISE_INT_EN	(0x1 << 26)
+#define ASR_OTGSC_ID_FALL_INT_EN	(0x1 << 27)
+
+#define ASR_OTGSC_ID_RISE_INT	(0x1 << 26)
+#define ASR_OTGSC_ID_FALL_INT	(0x1 << 27)
+#define ASR_OTGSC_STS_USB_ID	(0x1 << 21)
+
+#define U2x_CAPREGS_OFFSET       0x100
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+static const char driver_name[] = "mv-otg";
+
+static int otg_force_host_mode;
+static int otg_force_dev_mode;
+static bool usb_host_vbus_on;
+
+static char *state_string[] = {
+	"undefined",
+	"b_idle",
+	"b_srp_init",
+	"b_peripheral",
+	"b_wait_acon",
+	"b_host",
+	"a_idle",
+	"a_wait_vrise",
+	"a_wait_bcon",
+	"a_host",
+	"a_suspend",
+	"a_peripheral",
+	"a_wait_vfall",
+	"a_vbus_err"
+};
+static struct mv_otg *the_controller;
+
+#if defined(CONFIG_CPU_ASR18XX) && defined(CONFIG_USB_MVC2)
+static int mv_otg_read_id_val(unsigned int *level)
+{
+	struct mv_otg *mvotg = the_controller;
+	struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
+	*level = otg_ctrl->id;
+
+	return 0;
+}
+
+static int mv_otg_set_mvc2_connect_state(struct usb_otg *otg,
+				bool state)
+{
+	struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy);
+	mvotg->mvc2_state = state;
+
+	return 0;
+}
+#endif
+
+static int mv_otg_set_pm_event(struct usb_otg *otg,
+				bool on)
+{
+	struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy);
+
+	if (on) {
+		pr_info("%s: acquire pm lock\n", __func__);
+		/* set constraint before turn on vbus */
+		__pm_stay_awake(mvotg->pdev->dev.power.wakeup);
+		pm_qos_update_request(&mvotg->qos_idle, mvotg->lpm_qos);
+	} else {
+		pr_info("%s: release pm lock\n", __func__);
+		__pm_relax(mvotg->pdev->dev.power.wakeup);
+		pm_qos_update_request(&mvotg->qos_idle,
+			PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
+	}
+
+	return 0;
+}
+
+/* need to write APMU register to enable USBID wakeup/irq */
+static void mv_otg_usbid_wakeup_en(int en)
+{
+	u32 tmp32;
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	tmp32 = __raw_readl(apmu_base + APMU_SD_ROT_WAKE_CLR);
+	if (en)
+		tmp32 |= USB_OTG_ID_WAKEUP_EN;
+	else
+		tmp32 &= ~USB_OTG_ID_WAKEUP_EN;
+	__raw_writel(tmp32, apmu_base + APMU_SD_ROT_WAKE_CLR);
+
+}
+
+/* need to write APMU register to clear USBID/linestat wakeup/irq */
+static void mv_otg_usbid_linestat_wakeup_clear(void)
+{
+	u32 tmp32;
+	int timeout = 10000;
+
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	tmp32 = __raw_readl(apmu_base + APMU_SD_ROT_WAKE_CLR);
+
+	/* to clear APMU USBID wakeup/irq, first wirte 1, then write 0 */
+	tmp32 |= (USB_OTG_ID_WAKEUP_CLR | USB_ID_WAKE_CLEAR
+		  | USB_VBUS_WAKE_CLEAR);
+	tmp32 &= ~MV_HSIC_WAKEUP_EN_BIT;
+	__raw_writel(tmp32, apmu_base + APMU_SD_ROT_WAKE_CLR);
+	while ((__raw_readl(apmu_base + APMU_SD_ROT_WAKE_CLR)) &
+		USB_OTG_ID_WAKEUP_CLR) {
+		timeout--;
+		if (timeout == 0) {
+			WARN(1, "usb_otg wakeup clear timeout");
+			break;
+		}
+	}
+}
+
+#ifdef CONFIG_USB_MV_HSIC_UDC
+static void mv_clear_hsic_wakeup_en(void)
+{
+	u32 tmp32;
+
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	tmp32 = __raw_readl(apmu_base + APMU_SD_ROT_WAKE_CLR);
+	if (!(tmp32 & (0x1 << 24)))
+		return;
+	tmp32 &= ~(0x1 << 24);
+	__raw_writel(tmp32, apmu_base + APMU_SD_ROT_WAKE_CLR);
+}
+#endif
+
+bool is_otg_host_vbus_on(void)
+{
+	return usb_host_vbus_on;
+}
+
+static int mv_otg_set_vbus(struct usb_otg *otg, bool on)
+{
+	struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy);
+	int ret;
+
+	usb_host_vbus_on = on;
+	if (mvotg->gpio_num == ENNUM)
+		ret = pxa_usb_extern_call(mvotg->pdata->id, vbus, set_vbus, on);
+	else
+		ret = gpio_direction_output(mvotg->gpio_num , on);
+
+	return ret;
+}
+
+static int mv_otg_set_host(struct usb_otg *otg,
+			   struct usb_bus *host)
+{
+	otg->host = host;
+
+	return 0;
+}
+
+static int mv_otg_set_peripheral(struct usb_otg *otg,
+				 struct usb_gadget *gadget)
+{
+	otg->gadget = gadget;
+
+	return 0;
+}
+
+static void mv_otg_run_state_machine(struct mv_otg *mvotg,
+				     unsigned long delay)
+{
+	dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n");
+	if (!mvotg->qwork)
+		return;
+
+	queue_delayed_work(mvotg->qwork, &mvotg->work, delay);
+}
+#if defined(CONFIG_CPU_ASR18XX)
+static void mv_otg_timer_await_vrise(struct timer_list *timer)
+{
+	struct mv_otg *mvotg = the_controller;
+
+	mvotg->otg_ctrl.a_wait_vrise_timeout = 1;
+
+	dev_info(&mvotg->pdev->dev, "A Device wait Vrise Timeout!\n");
+
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 0);
+		spin_unlock(&mvotg->wq_lock);
+	}
+}
+#endif
+
+
+static void mv_otg_timer_await_bcon(struct timer_list *timer)
+{
+	struct mv_otg *mvotg = the_controller;
+
+	mvotg->otg_ctrl.a_wait_bcon_timeout = 1;
+
+	dev_info(&mvotg->pdev->dev, "B Device No Response!\n");
+
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 0);
+		spin_unlock(&mvotg->wq_lock);
+	}
+}
+
+static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
+{
+	struct timer_list *timer;
+
+	if (id >= OTG_TIMER_NUM)
+		return -EINVAL;
+
+	timer = &mvotg->otg_ctrl.timer[id];
+
+	if (timer_pending(timer))
+		del_timer(timer);
+
+	return 0;
+}
+
+static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id,
+			    unsigned long interval,
+			    void (*callback) (struct timer_list *timer))
+{
+	struct timer_list *timer;
+
+	if (id >= OTG_TIMER_NUM)
+		return -EINVAL;
+
+	timer = &mvotg->otg_ctrl.timer[id];
+	if (timer_pending(timer)) {
+		dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id);
+		return -EBUSY;
+	}
+
+	timer_setup(timer, callback, 0);
+	timer->expires = jiffies + interval;
+	add_timer(timer);
+
+	return 0;
+}
+
+static int mv_otg_reset(struct mv_otg *mvotg)
+{
+	unsigned int loops;
+	u32 tmp;
+
+	/* Stop the controller */
+	tmp = readl(&mvotg->op_regs->usbcmd);
+	tmp &= ~USBCMD_RUN_STOP;
+	writel(tmp, &mvotg->op_regs->usbcmd);
+
+	/* Reset the controller to get default values */
+	writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd);
+
+	loops = 500;
+	while (readl(&mvotg->op_regs->usbcmd) & USBCMD_CTRL_RESET) {
+		if (loops == 0) {
+			dev_err(&mvotg->pdev->dev,
+				"Wait for RESET completed TIMEOUT\n");
+			return -ETIMEDOUT;
+		}
+		loops--;
+		udelay(20);
+	}
+
+	writel(0x0, &mvotg->op_regs->usbintr);
+	tmp = readl(&mvotg->op_regs->usbsts);
+	writel(tmp, &mvotg->op_regs->usbsts);
+
+	return 0;
+}
+
+static void mv_otg_init_irq(struct mv_otg *mvotg)
+{
+	u32 otgsc;
+
+	mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID
+	    | OTGSC_INTR_A_VBUS_VALID;
+	mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID
+	    | OTGSC_INTSTS_A_VBUS_VALID;
+
+	if ((mvotg->pdata->extern_attr & MV_USB_HAS_VBUS_DETECTION) == 0) {
+		mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID
+		    | OTGSC_INTR_B_SESSION_END;
+		mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID
+		    | OTGSC_INTSTS_B_SESSION_END;
+	}
+
+	if ((mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION) == 0) {
+		mvotg->irq_en |= OTGSC_INTR_USB_ID;
+		mvotg->irq_status |= OTGSC_INTSTS_USB_ID;
+	}
+
+	otgsc = readl(&mvotg->op_regs->otgsc);
+	otgsc |= mvotg->irq_en;
+	writel(otgsc, &mvotg->op_regs->otgsc);
+}
+
+static void asr_gpio_otg_init_irq(struct mv_otg *mvotg)
+{
+	u32 otgsc;
+
+	mvotg->irq_en = ASR_OTGSC_ID_RISE_INT_EN
+				| ASR_OTGSC_ID_FALL_INT_EN;
+	mvotg->irq_status = ASR_OTGSC_ID_RISE_INT
+				| ASR_OTGSC_ID_FALL_INT;
+
+	otgsc = readl(&mvotg->op_regs->usbintr);
+	otgsc &= ~mvotg->irq_en;
+	writel(otgsc, &mvotg->op_regs->usbintr);
+}
+
+static void asr_otg_init_irq(struct mv_otg *mvotg)
+{
+	u32 otgsc;
+
+	if (mvotg->gpio_usbid >= 0) {
+		asr_gpio_otg_init_irq(mvotg);
+		return;
+	}
+
+	mvotg->irq_en = 0x0;
+
+	if ((mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION) == 0) {
+		mvotg->irq_en = ASR_OTGSC_ID_RISE_INT_EN
+				| ASR_OTGSC_ID_FALL_INT_EN;
+		mvotg->irq_status = ASR_OTGSC_ID_RISE_INT
+				| ASR_OTGSC_ID_FALL_INT;
+	}
+
+	otgsc = readl(&mvotg->op_regs->usbintr);
+	otgsc |= mvotg->irq_en;
+	writel(otgsc, &mvotg->op_regs->usbintr);
+}
+
+static void mv_otg_start_host(struct mv_otg *mvotg, int on)
+{
+#ifdef CONFIG_USB
+	struct usb_otg *otg = mvotg->phy.otg;
+	struct usb_hcd *hcd;
+
+	if (!otg->host) {
+		int retry = 0;
+		while (retry < MAX_RETRY_TIMES) {
+			retry++;
+			msleep(RETRY_SLEEP_MS);
+			if (otg->host)
+				break;
+		}
+
+		if (!otg->host) {
+			dev_err(mvotg->phy.dev, "otg->host is not set!\n");
+			return;
+		}
+	}
+
+	dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop");
+
+	hcd = bus_to_hcd(otg->host);
+
+	if (on) {
+		mvotg->is_hostmode = 1;
+		/* set constraint before turn on vbus */
+		if (mvotg->disable_host_wakelock)
+			pm_wakeup_event(&mvotg->pdev->dev, 5000);
+		else
+			pm_stay_awake(&mvotg->pdev->dev);
+		pm_qos_update_request(&mvotg->qos_idle, mvotg->lpm_qos);
+		usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
+	} else {
+		mvotg->is_hostmode = 0;
+		usb_remove_hcd(hcd);
+		pm_qos_update_request(&mvotg->qos_idle,
+			PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
+		pm_relax(&mvotg->pdev->dev);
+	}
+#endif /* CONFIG_USB */
+}
+
+static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
+{
+	struct usb_otg *otg = mvotg->phy.otg;
+	if (!otg->gadget) {
+		int retry = 0;
+		while (retry < MAX_RETRY_TIMES) {
+			retry++;
+			msleep(RETRY_SLEEP_MS);
+			if (otg->gadget)
+				break;
+#if defined(CONFIG_CPU_ASR18XX) && defined(CONFIG_USB_MVC2)
+			if (mvotg->mvc2_state == 1)
+				return;
+#endif
+		}
+
+		if (!otg->gadget) {
+			dev_err(mvotg->phy.dev, "otg->gadget is not set!\n");
+			return;
+		}
+	}
+
+	dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off");
+
+	if (on) {
+		/* set constraint before turn on vbus */
+		pm_stay_awake(&mvotg->pdev->dev);
+		pm_qos_update_request(&mvotg->qos_idle, mvotg->lpm_qos);
+		usb_gadget_vbus_connect(otg->gadget);
+	} else {
+		usb_gadget_vbus_disconnect(otg->gadget);
+		pm_qos_update_request(&mvotg->qos_idle,
+			PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
+		pm_relax(&mvotg->pdev->dev);
+	}
+}
+
+static void otg_clock_enable(struct mv_otg *mvotg)
+{
+	clk_enable(mvotg->clk);
+}
+
+static void otg_clock_disable(struct mv_otg *mvotg)
+{
+	clk_disable(mvotg->clk);
+}
+
+static int mv_otg_enable_internal(struct mv_otg *mvotg)
+{
+	int retval = 0;
+
+	if (mvotg->active)
+		return 0;
+
+	dev_dbg(&mvotg->pdev->dev, "otg enabled\n");
+
+	otg_clock_enable(mvotg);
+	retval = usb_phy_init(mvotg->outer_phy);
+	if (retval) {
+		dev_err(&mvotg->pdev->dev,
+			"failed to initialize phy %d\n", retval);
+		otg_clock_disable(mvotg);
+		return retval;
+	}
+
+	mvotg->active = 1;
+
+	return 0;
+
+}
+
+static int mv_otg_enable(struct mv_otg *mvotg)
+{
+	if (mvotg->clock_gating)
+		return mv_otg_enable_internal(mvotg);
+
+	return 0;
+}
+
+static void mv_otg_disable_internal(struct mv_otg *mvotg)
+{
+
+	if (mvotg->active) {
+		dev_dbg(&mvotg->pdev->dev, "otg disabled\n");
+		usb_phy_shutdown(mvotg->outer_phy);
+		otg_clock_disable(mvotg);
+		mvotg->active = 0;
+	}
+}
+
+static void mv_otg_disable(struct mv_otg *mvotg)
+{
+	if (mvotg->clock_gating)
+		mv_otg_disable_internal(mvotg);
+}
+
+static void mv_otg_update_inputs(struct mv_otg *mvotg)
+{
+	struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
+	u32 otgsc;
+	int ret;
+
+	otgsc = readl(&mvotg->op_regs->otgsc);
+
+	if (mvotg->pdata->extern_attr & MV_USB_HAS_VBUS_DETECTION) {
+		unsigned int vbus;
+		ret = pxa_usb_extern_call(mvotg->pdata->id, vbus, get_vbus, &vbus);
+		if (ret)
+			vbus = usb_phy_get_vbus(mvotg->outer_phy);
+		if (vbus == VBUS_HIGH) {
+			otg_ctrl->b_sess_vld = 1;
+			otg_ctrl->b_sess_end = 0;
+		} else {
+			otg_ctrl->b_sess_vld = 0;
+			otg_ctrl->b_sess_end = 1;
+		}
+	} else {
+		otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID);
+		otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END);
+	}
+
+	if (mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION) {
+		unsigned int id;
+		pxa_usb_extern_call(mvotg->pdata->id, idpin, get_idpin, &id);
+		otg_ctrl->id = !!id;
+	} else {
+		otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID);
+	}
+
+	if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
+		otg_ctrl->a_bus_req = 1;
+
+	if (otg_force_host_mode) {
+		otg_ctrl->id = 0;
+		otg_ctrl->a_bus_req = 1;
+	}
+
+	otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID);
+	otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID);
+
+	dev_dbg(&mvotg->pdev->dev, "%s: ", __func__);
+	dev_info(&mvotg->pdev->dev, "id %d, otgsc: 0x%x\n", otg_ctrl->id, otgsc);
+	dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld);
+	dev_info(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end);
+	dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld);
+	dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld);
+
+}
+
+static void asr_otg_update_inputs(struct mv_otg *mvotg)
+{
+	struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
+	u32 otgsc;
+	int ret;
+
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	otgsc = readl(apmu_base + APMU_SD_ROT_WAKE_CLR);
+
+	if (mvotg->pdata->extern_attr & MV_USB_HAS_VBUS_DETECTION) {
+		unsigned int vbus;
+		ret = pxa_usb_extern_call(mvotg->pdata->id, vbus, get_vbus, &vbus);
+		if (ret)
+			vbus = usb_phy_get_vbus(mvotg->outer_phy);
+		if (vbus == VBUS_HIGH) {
+			otg_ctrl->a_sess_vld = 1;
+			otg_ctrl->a_vbus_vld = 1;
+			otg_ctrl->b_sess_vld = 1;
+			otg_ctrl->b_sess_end = 0;
+		} else {
+			otg_ctrl->a_sess_vld = 0;
+			otg_ctrl->a_vbus_vld = 0;
+			otg_ctrl->b_sess_vld = 0;
+			otg_ctrl->b_sess_end = 1;
+		}
+	}
+
+	if (mvotg->gpio_usbid >= 0) {
+		otg_ctrl->id = !!gpio_get_value(mvotg->gpio_usbid);
+	} else {
+		if (mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION) {
+			unsigned int id;
+			pxa_usb_extern_call(mvotg->pdata->id, idpin, get_idpin, &id);
+			otg_ctrl->id = !!id;
+		} else {
+			otg_ctrl->id = !!(otgsc & ASR_OTGSC_STS_USB_ID);
+		}
+	}
+
+	if (otg_force_dev_mode) {
+		pr_info("otg force dev mode\n");
+		otg_ctrl->id = 1;
+	}
+
+	if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id)
+		otg_ctrl->a_bus_req = 1;
+
+	if (otg_force_host_mode) {
+		otg_ctrl->id = 0;
+		otg_ctrl->a_bus_req = 1;
+	}
+
+	dev_dbg(&mvotg->pdev->dev, "%s: ", __func__);
+	dev_info(&mvotg->pdev->dev, "id %d otgsc: 0x%x\n", otg_ctrl->id, otgsc);
+	dev_info(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld);
+	dev_info(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end);
+	dev_info(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld);
+	dev_info(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld);
+}
+
+static void mv_otg_update_state(struct mv_otg *mvotg)
+{
+	struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
+	struct usb_phy *phy = &mvotg->phy;
+	int old_state = phy->state;
+
+	switch (old_state) {
+	case OTG_STATE_UNDEFINED:
+		phy->state = OTG_STATE_B_IDLE;
+		/* FALL THROUGH */
+	case OTG_STATE_B_IDLE:
+		if (otg_ctrl->id == 0)
+			phy->state = OTG_STATE_A_IDLE;
+		else if (otg_ctrl->b_sess_vld)
+			phy->state = OTG_STATE_B_PERIPHERAL;
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0)
+			phy->state = OTG_STATE_B_IDLE;
+		break;
+	case OTG_STATE_A_IDLE:
+		if (otg_ctrl->id)
+			phy->state = OTG_STATE_B_IDLE;
+		else if (!(otg_ctrl->a_bus_drop) &&
+			 (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det))
+			phy->state = OTG_STATE_A_WAIT_VRISE;
+		break;
+	case OTG_STATE_A_WAIT_VRISE:
+		#if defined(CONFIG_CPU_ASR18XX)
+		if (otg_ctrl->id || otg_ctrl->a_wait_vrise_timeout
+			|| otg_ctrl->a_vbus_vld) {
+			mv_otg_cancel_timer(mvotg, A_WAIT_VRISE_TIMER);
+			mvotg->otg_ctrl.a_wait_vrise_timeout = 0;
+			phy->state = OTG_STATE_A_WAIT_BCON;
+		}
+		#else
+		if (otg_ctrl->a_vbus_vld)
+			phy->state = OTG_STATE_A_WAIT_BCON;
+		#endif
+		break;
+	case OTG_STATE_A_WAIT_BCON:
+		if (otg_ctrl->id || otg_ctrl->a_bus_drop
+		    || otg_ctrl->a_wait_bcon_timeout) {
+			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+			phy->state = OTG_STATE_A_WAIT_VFALL;
+			otg_ctrl->a_bus_req = 0;
+		} else if (!otg_ctrl->a_vbus_vld) {
+			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+			phy->state = OTG_STATE_A_VBUS_ERR;
+		} else if (otg_ctrl->b_conn) {
+			mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
+			mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
+			phy->state = OTG_STATE_A_HOST;
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		if (otg_ctrl->id || !otg_ctrl->b_conn
+		    || otg_ctrl->a_bus_drop)
+			phy->state = OTG_STATE_A_WAIT_BCON;
+		else if (!otg_ctrl->a_vbus_vld)
+			phy->state = OTG_STATE_A_VBUS_ERR;
+		break;
+	case OTG_STATE_A_WAIT_VFALL:
+		if (otg_ctrl->id
+		    || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld)
+		    || otg_ctrl->a_bus_req)
+			phy->state = OTG_STATE_A_IDLE;
+		break;
+	case OTG_STATE_A_VBUS_ERR:
+		if (otg_ctrl->id || otg_ctrl->a_clr_err
+		    || otg_ctrl->a_bus_drop) {
+			otg_ctrl->a_clr_err = 0;
+			phy->state = OTG_STATE_A_WAIT_VFALL;
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+static void mv_otg_work(struct work_struct *work)
+{
+	struct mv_otg *mvotg;
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+	int old_state;
+
+	mvotg = container_of(to_delayed_work(work), struct mv_otg, work);
+	/*
+	* the otg may be closed in the process of the usb plug-out, when usb
+	* is plug in and exec this work, we still need to enable the otg again
+	*/
+	if (!mvotg->active) {
+		mv_otg_enable(mvotg);
+		mv_otg_init_irq(mvotg);
+	}
+
+run:
+	/* work queue is single thread, or we need spin_lock to protect */
+	phy = &mvotg->phy;
+	otg = phy->otg;
+	old_state = phy->state;
+
+	if (!mvotg->active)
+		return;
+
+	mv_otg_update_inputs(mvotg);
+	mv_otg_update_state(mvotg);
+
+
+	if (old_state != phy->state) {
+		dev_dbg(&mvotg->pdev->dev, "change from state %s to %s\n",
+			 state_string[old_state],
+			 state_string[phy->state]);
+
+		switch (phy->state) {
+		case OTG_STATE_B_IDLE:
+			otg->default_a = 0;
+			if (old_state == OTG_STATE_B_PERIPHERAL ||
+				old_state == OTG_STATE_UNDEFINED)
+				mv_otg_start_periphrals(mvotg, 0);
+			mv_otg_reset(mvotg);
+			mv_otg_disable(mvotg);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			mv_otg_enable(mvotg);
+			mv_otg_start_periphrals(mvotg, 1);
+			break;
+		case OTG_STATE_A_IDLE:
+			otg->default_a = 1;
+			mv_otg_enable(mvotg);
+			if (old_state == OTG_STATE_A_WAIT_VFALL)
+				mv_otg_start_host(mvotg, 0);
+			mv_otg_reset(mvotg);
+			break;
+		case OTG_STATE_A_WAIT_VRISE:
+			mv_otg_set_vbus(otg, 1);
+	#if defined(CONFIG_CPU_ASR18XX)
+			mv_otg_set_timer(mvotg, A_WAIT_VRISE_TIMER,
+					 T_A_WAIT_VRISE,
+					 mv_otg_timer_await_vrise);
+	#endif
+			break;
+		case OTG_STATE_A_WAIT_BCON:
+			if (old_state != OTG_STATE_A_HOST)
+				mv_otg_start_host(mvotg, 1);
+			mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
+					 T_A_WAIT_BCON,
+					 mv_otg_timer_await_bcon);
+			/*
+			 * Now, we directly enter A_HOST. So set b_conn = 1
+			 * here. In fact, it need host driver to notify us.
+			 */
+			mvotg->otg_ctrl.b_conn = 1;
+			break;
+		case OTG_STATE_A_HOST:
+			break;
+		case OTG_STATE_A_WAIT_VFALL:
+			/*
+			 * Now, we has exited A_HOST. So set b_conn = 0
+			 * here. In fact, it need host driver to notify us.
+			 */
+			mvotg->otg_ctrl.b_conn = 0;
+			mv_otg_set_vbus(otg, 0);
+			break;
+		case OTG_STATE_A_VBUS_ERR:
+			break;
+		default:
+			break;
+		}
+		goto run;
+	}
+}
+
+static void asr_otg_work(struct work_struct *work)
+{
+	struct mv_otg *mvotg;
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+	int old_state;
+
+	mvotg = container_of(to_delayed_work(work), struct mv_otg, work);
+	/*
+	* the otg may be closed in the process of the usb plug-out, when usb
+	* is plug in and exec this work, we still need to enable the otg again
+	*/
+	if (!mvotg->active) {
+		mv_otg_enable(mvotg);
+		asr_otg_init_irq(mvotg);
+	}
+
+run:
+	/* work queue is single thread, or we need spin_lock to protect */
+	phy = &mvotg->phy;
+	otg = phy->otg;
+	old_state = phy->state;
+
+	if (!mvotg->active)
+		return;
+
+	asr_otg_update_inputs(mvotg);
+	mv_otg_update_state(mvotg);
+
+
+	if (old_state != phy->state) {
+		dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
+			 state_string[old_state],
+			 state_string[phy->state]);
+
+		switch (phy->state) {
+		case OTG_STATE_B_IDLE:
+			otg->default_a = 0;
+			if (old_state == OTG_STATE_B_PERIPHERAL ||
+				old_state == OTG_STATE_UNDEFINED)
+				mv_otg_start_periphrals(mvotg, 0);
+			mv_otg_reset(mvotg);
+			mv_otg_disable(mvotg);
+			break;
+		case OTG_STATE_B_PERIPHERAL:
+			mv_otg_enable(mvotg);
+			mv_otg_start_periphrals(mvotg, 1);
+			break;
+		case OTG_STATE_A_IDLE:
+			otg->default_a = 1;
+			mv_otg_enable(mvotg);
+			if (old_state == OTG_STATE_A_WAIT_VFALL)
+				mv_otg_start_host(mvotg, 0);
+			mv_otg_reset(mvotg);
+			break;
+		case OTG_STATE_A_WAIT_VRISE:
+			mv_otg_set_vbus(otg, 1);
+			msleep(10);
+	#if defined(CONFIG_CPU_ASR18XX)
+			mv_otg_set_timer(mvotg, A_WAIT_VRISE_TIMER,
+					 T_A_WAIT_VRISE,
+					 mv_otg_timer_await_vrise);
+	#endif
+			break;
+		case OTG_STATE_A_WAIT_BCON:
+			if (old_state != OTG_STATE_A_HOST)
+				mv_otg_start_host(mvotg, 1);
+			mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER,
+					 T_A_WAIT_BCON,
+					 mv_otg_timer_await_bcon);
+			/*
+			 * Now, we directly enter A_HOST. So set b_conn = 1
+			 * here. In fact, it need host driver to notify us.
+			 */
+			mvotg->otg_ctrl.b_conn = 1;
+			break;
+		case OTG_STATE_A_HOST:
+			break;
+		case OTG_STATE_A_WAIT_VFALL:
+			/*
+			 * Now, we has exited A_HOST. So set b_conn = 0
+			 * here. In fact, it need host driver to notify us.
+			 */
+			mvotg->otg_ctrl.b_conn = 0;
+			mv_otg_set_vbus(otg, 0);
+			msleep(10);
+			break;
+		case OTG_STATE_A_VBUS_ERR:
+			break;
+		default:
+			break;
+		}
+		goto run;
+	} else if ((phy->state == OTG_STATE_B_IDLE) && mvotg->active) {
+		mv_otg_disable(mvotg);
+		dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
+			 state_string[phy->state],
+			 state_string[phy->state]);	
+	}
+}
+
+static irqreturn_t mv_otg_irq(int irq, void *dev)
+{
+	struct mv_otg *mvotg = dev;
+	u32 otgsc;
+
+	/* if otg clock is not enabled, otgsc read out will be 0 */
+	if (!mvotg->active)
+		mv_otg_enable(mvotg);
+
+	otgsc = readl(&mvotg->op_regs->otgsc);
+	writel(otgsc | mvotg->irq_en, &mvotg->op_regs->otgsc);
+
+	if (!(mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION)
+		|| mvotg->support_wkresume)
+		mv_otg_usbid_linestat_wakeup_clear();
+
+#ifdef CONFIG_USB_MV_HSIC_UDC
+	/* disable hsic wakeup en bit to avoid irq storm */
+	mv_clear_hsic_wakeup_en();
+#endif
+
+	if (!(mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION)) {
+		if (mvotg->otg_ctrl.id != (!!(otgsc & OTGSC_STS_USB_ID))) {
+			mv_otg_run_state_machine(mvotg, 0);
+			return IRQ_HANDLED;
+		}
+	}
+
+	/*
+	 * if we have vbus, then the vbus detection for B-device
+	 * will be done by mv_otg_inputs_irq().
+	 */
+	if (mvotg->pdata->extern_attr & MV_USB_HAS_VBUS_DETECTION)
+		if ((otgsc & OTGSC_STS_USB_ID) &&
+			!(otgsc & OTGSC_INTSTS_USB_ID))
+			return IRQ_NONE;
+
+	if ((otgsc & mvotg->irq_status) == 0)
+		return IRQ_NONE;
+	mv_otg_run_state_machine(mvotg, 0);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t asr_gpio_otg_irq(int irq, void *dev)
+{
+	struct mv_otg *mvotg = dev;
+	u32 otgsc;
+
+	if (!mvotg->active)
+		mv_otg_enable(mvotg);
+
+	msleep(5);
+	otgsc = !!gpio_get_value(mvotg->gpio_usbid);
+	pr_info("gpio-usbid: %d, mvotg->otg_ctrl.id: %d\n",
+		otgsc, mvotg->otg_ctrl.id);
+
+	if (!(mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION)
+		|| mvotg->support_wkresume)
+		mv_otg_usbid_linestat_wakeup_clear();
+
+	if (mvotg->otg_ctrl.id != otgsc) {
+		mv_otg_run_state_machine(mvotg, 0);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t asr_otg_irq(int irq, void *dev)
+{
+	struct mv_otg *mvotg = dev;
+	u32 otgsc;
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+
+	/* if otg clock is not enabled, otgsc read out will be 0 */
+	if (!mvotg->active)
+		mv_otg_enable(mvotg);
+
+	otgsc = readl(&mvotg->op_regs->usbsts);
+	writel((otgsc & mvotg->irq_en), &mvotg->op_regs->usbsts);
+	otgsc = readl(apmu_base + APMU_SD_ROT_WAKE_CLR);
+
+	if (!(mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION)
+		|| mvotg->support_wkresume)
+		mv_otg_usbid_linestat_wakeup_clear();
+
+	if (!(mvotg->pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION)) {
+		if (mvotg->otg_ctrl.id != (!!(otgsc & ASR_OTGSC_STS_USB_ID))) {
+			mv_otg_run_state_machine(mvotg, 0);
+			return IRQ_HANDLED;
+		}
+	}
+
+	/*
+	 * if we have vbus, then the vbus detection for B-device
+	 * will be done by mv_otg_inputs_irq().
+	 */
+	if (mvotg->pdata->extern_attr & MV_USB_HAS_VBUS_DETECTION)
+		if (!(otgsc & ASR_OTGSC_STS_USB_ID))
+			return IRQ_NONE;
+
+	if ((otgsc & mvotg->irq_status) == 0)
+		return IRQ_NONE;
+	mv_otg_run_state_machine(mvotg, 0);
+
+	return IRQ_HANDLED;
+}
+
+static int mv_otg_notifier_callback(struct notifier_block *nb,
+                                unsigned long val, void *v)
+{
+	struct mv_otg *mvotg = container_of(nb, struct mv_otg, notifier);
+	/* The clock may disabled at this time */
+	if (!mvotg->active) {
+		mv_otg_enable(mvotg);
+		mv_otg_init_irq(mvotg);
+	}
+	mv_otg_run_state_machine(mvotg, 0);
+
+	return 0;
+}
+
+static int asr_otg_notifier_callback(struct notifier_block *nb,
+                                unsigned long val, void *v)
+{
+	struct mv_otg *mvotg = container_of(nb, struct mv_otg, notifier);
+	/* The clock may disabled at this time */
+	if (!mvotg->active) {
+		mv_otg_enable(mvotg);
+		asr_otg_init_irq(mvotg);
+	}
+	mv_otg_run_state_machine(mvotg, 0);
+
+	return 0;
+}
+
+static ssize_t
+get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	return scnprintf(buf, PAGE_SIZE, "%d\n",
+			 mvotg->otg_ctrl.a_bus_req);
+}
+
+static ssize_t
+set_a_bus_req(struct device *dev, struct device_attribute *attr,
+	      const char *buf, size_t count)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+
+	if (count > 2)
+		return -1;
+
+	/* We will use this interface to change to A device */
+	if (mvotg->phy.state != OTG_STATE_B_IDLE
+	    && mvotg->phy.state != OTG_STATE_A_IDLE)
+		return -1;
+
+	/* The clock may disabled and we need to set irq for ID detected */
+	mv_otg_enable(mvotg);
+	if (cpu_is_asr1803())
+		asr_otg_init_irq(mvotg);
+	else
+		mv_otg_init_irq(mvotg);
+
+	if (buf[0] == '1') {
+		mvotg->otg_ctrl.a_bus_req = 1;
+		mvotg->otg_ctrl.a_bus_drop = 0;
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: a_bus_req = 1\n");
+
+		if (spin_trylock(&mvotg->wq_lock)) {
+			mv_otg_run_state_machine(mvotg, 0);
+			spin_unlock(&mvotg->wq_lock);
+		}
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req,
+		   set_a_bus_req);
+
+static ssize_t
+set_a_clr_err(struct device *dev, struct device_attribute *attr,
+	      const char *buf, size_t count)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	if (!mvotg->phy.otg->default_a)
+		return -1;
+
+	if (count > 2)
+		return -1;
+
+	if (buf[0] == '1') {
+		mvotg->otg_ctrl.a_clr_err = 1;
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: a_clr_err = 1\n");
+	}
+
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 0);
+		spin_unlock(&mvotg->wq_lock);
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err);
+
+static ssize_t
+get_a_bus_drop(struct device *dev, struct device_attribute *attr,
+	       char *buf)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	return scnprintf(buf, PAGE_SIZE, "%d\n",
+			 mvotg->otg_ctrl.a_bus_drop);
+}
+
+static ssize_t
+set_a_bus_drop(struct device *dev, struct device_attribute *attr,
+	       const char *buf, size_t count)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	if (!mvotg->phy.otg->default_a)
+		return -1;
+
+	if (count > 2)
+		return -1;
+
+	if (buf[0] == '0') {
+		mvotg->otg_ctrl.a_bus_drop = 0;
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: a_bus_drop = 0\n");
+	} else if (buf[0] == '1') {
+		mvotg->otg_ctrl.a_bus_drop = 1;
+		mvotg->otg_ctrl.a_bus_req = 0;
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: a_bus_drop = 1\n");
+		dev_dbg(&mvotg->pdev->dev,
+			"User request: and a_bus_req = 0\n");
+	}
+
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 0);
+		spin_unlock(&mvotg->wq_lock);
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR,
+		   get_a_bus_drop, set_a_bus_drop);
+
+static ssize_t
+get_otg_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	char *state = otg_force_host_mode ? "host" : "client";
+	return sprintf(buf, "OTG mode: %s\n", state);
+}
+
+static ssize_t
+set_otg_mode(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	char *usage = "Usage: $echo host/client to switch otg mode";
+	char buff[16], *b;
+
+	strncpy(buff, buf, sizeof(buff));
+	b = strim(buff);
+	pr_info("OTG state is %s\n", state_string[mvotg->phy.state]);
+	if (!strcmp(b, "host")) {
+		if (mvotg->phy.state == OTG_STATE_B_PERIPHERAL) {
+			pr_err("Failed to swich mode, pls don't connect to PC!\n");
+			return count;
+		}
+		otg_force_host_mode = 1;
+	} else if (!strcmp(b, "client")) {
+		otg_force_host_mode = 0;
+	} else {
+		pr_err("%s\n", usage);
+		return count;
+	}
+	mv_otg_run_state_machine(mvotg, 0);
+
+	return count;
+}
+static DEVICE_ATTR(otg_mode, S_IRUGO | S_IWUSR, get_otg_mode, set_otg_mode);
+
+static ssize_t
+get_host_wakelock(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	return sprintf(buf, "disable_host_wakelock: %d\n", mvotg->disable_host_wakelock);
+}
+
+static ssize_t
+set_host_wakelock(struct device *dev, struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct mv_otg *mvotg = dev_get_drvdata(dev);
+	char buff[16], *b;
+
+	strncpy(buff, buf, sizeof(buff));
+	b = strim(buff);
+	if (!strncmp(b, "0", 1)) {
+		pm_relax(&mvotg->pdev->dev);
+		pr_err("set otg wakelock disabled\n");
+		mvotg->disable_host_wakelock = 1;
+	} else {
+		pm_stay_awake(&mvotg->pdev->dev);
+		pr_err("set otg wakelock enabled\n");
+		mvotg->disable_host_wakelock = 0;
+	}
+
+	return count;
+}
+static DEVICE_ATTR(host_wakelock, S_IRUGO | S_IWUSR, get_host_wakelock, set_host_wakelock);
+
+static struct attribute *inputs_attrs[] = {
+	&dev_attr_a_bus_req.attr,
+	&dev_attr_a_clr_err.attr,
+	&dev_attr_a_bus_drop.attr,
+	&dev_attr_otg_mode.attr,
+	&dev_attr_host_wakelock.attr,
+	NULL,
+};
+
+static struct attribute_group inputs_attr_group = {
+	.name = "inputs",
+	.attrs = inputs_attrs,
+};
+
+int mv_otg_remove(struct platform_device *pdev)
+{
+	struct mv_otg *mvotg = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+
+	sysfs_remove_group(&mvotg->pdev->dev.kobj, &inputs_attr_group);
+
+	if (mvotg->qwork) {
+		flush_workqueue(mvotg->qwork);
+		destroy_workqueue(mvotg->qwork);
+	}
+
+	if (mvotg->pdata->extern_attr
+		& (MV_USB_HAS_VBUS_DETECTION | MV_USB_HAS_IDPIN_DETECTION))
+		pxa_usb_unregister_notifier(mvotg->pdata->id, &mvotg->notifier);
+
+	mv_otg_disable(mvotg);
+
+	clk_unprepare(mvotg->clk);
+
+	pm_qos_remove_request(&mvotg->qos_idle);
+
+	usb_remove_phy(&mvotg->phy);
+
+	the_controller = NULL;
+
+	return 0;
+}
+
+static void mv_otg_phy_bind_device(struct mv_otg *mvotg)
+{
+	const char *device_name;
+	struct device_node *np = (mvotg->phy.dev)->of_node;
+
+	if (!of_property_read_string(np, "marvell,udc-name", &device_name))
+		usb_bind_phy(device_name, MV_USB2_OTG_PHY_INDEX,
+						dev_name(mvotg->phy.dev));
+
+	if (!of_property_read_string(np, "marvell,ehci-name", &device_name))
+		usb_bind_phy(device_name, MV_USB2_OTG_PHY_INDEX,
+						dev_name(mvotg->phy.dev));
+}
+
+static void gpio_usbid_wakeup_fn(int gpio, void *data)
+{
+	struct mv_otg *mvotg = (struct mv_otg *)data;
+	pm_wakeup_event(&mvotg->pdev->dev, 5000);
+}
+
+static int init_gpio_usbid(struct platform_device *pdev,
+	struct mv_otg *mvotg)
+{
+	int ret = -1;
+
+	of_property_read_u32(pdev->dev.of_node,
+			"edge_wakeup_gpio", &mvotg->edge_gpio);
+	if (mvotg->edge_gpio >= 0) {
+		ret = request_mfp_edge_wakeup(mvotg->edge_gpio,
+					      gpio_usbid_wakeup_fn,
+					      (void *)mvotg, &pdev->dev);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to request edge wakeup.\n");
+			goto edge_wakeup;
+		}
+	} else {
+		dev_err(&pdev->dev, "error: no edge gpio for gpio usbid\n");
+	}
+
+	ret = gpio_request(mvotg->gpio_usbid, "gpio_usbid");
+
+	gpio_direction_input(mvotg->gpio_usbid);
+
+	mvotg->gpio_irq = gpio_to_irq(mvotg->gpio_usbid);
+	ret =
+	    request_threaded_irq(mvotg->gpio_irq, NULL, asr_gpio_otg_irq,
+			IRQF_SHARED | IRQF_TRIGGER_RISING |
+			IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "gpio_usbid",
+			mvotg);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "%s: request irq failed!\n",
+				 __func__);
+		goto free_gpio;
+	}
+
+	mv_otg_usbid_wakeup_en(0);
+	device_init_wakeup(&pdev->dev, 1);
+
+	ret = 0;
+	goto out;
+
+free_gpio:
+	gpio_free(mvotg->gpio_usbid);
+
+edge_wakeup:
+	if (mvotg->edge_gpio >= 0)
+		remove_mfp_edge_wakeup(mvotg->edge_gpio);
+out:
+	return ret;
+}
+
+static int mv_otg_probe(struct platform_device *pdev)
+{
+	struct mv_usb_platform_data *pdata = pdev->dev.platform_data;
+	struct mv_otg *mvotg;
+	struct usb_otg *otg;
+	struct resource *r;
+	int retval = 0;
+	struct device_node *np = pdev->dev.of_node;
+	const __be32 *prop;
+	unsigned int proplen;
+#if defined(CONFIG_CPU_ASR18XX)
+	struct device_node *mvc2_np;
+	int mvc2_phandle;
+#endif
+	if (cpu_is_asr1802s())
+		BUG_ON("1802s does't support OTG");
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "failed to get platform data\n");
+		return -ENODEV;
+	}
+
+	mvotg = devm_kzalloc(&pdev->dev, sizeof(*mvotg), GFP_KERNEL);
+	if (!mvotg) {
+		dev_err(&pdev->dev, "failed to allocate memory!\n");
+		return -ENOMEM;
+	}
+
+	the_controller = mvotg;
+
+	otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+	if (!otg)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, mvotg);
+
+	mvotg->pdev = pdev;
+	mvotg->pdata = pdata;
+
+	mvotg->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mvotg->clk))
+		return PTR_ERR(mvotg->clk);
+	clk_prepare(mvotg->clk);
+
+	mvotg->qwork = create_singlethread_workqueue("mv_otg_queue");
+	if (!mvotg->qwork) {
+		dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n");
+		return -ENOMEM;
+	}
+	if (cpu_is_asr1803())
+		INIT_DELAYED_WORK(&mvotg->work, asr_otg_work);
+	else
+		INIT_DELAYED_WORK(&mvotg->work, mv_otg_work);
+
+	/* OTG common part */
+	mvotg->pdev = pdev;
+	mvotg->phy.dev = &pdev->dev;
+	mvotg->phy.type = USB_PHY_TYPE_USB2;
+	mvotg->phy.otg = otg;
+	mvotg->phy.label = driver_name;
+	mvotg->phy.state = OTG_STATE_UNDEFINED;
+
+	otg->usb_phy = &mvotg->phy;
+	otg->set_host = mv_otg_set_host;
+	otg->set_peripheral = mv_otg_set_peripheral;
+	otg->set_vbus = mv_otg_set_vbus;
+#if defined(CONFIG_CPU_ASR18XX) && defined(CONFIG_USB_MVC2)
+	otg->set_mvc2_connect_state = mv_otg_set_mvc2_connect_state;
+#endif
+	otg->set_pm_event = mv_otg_set_pm_event;
+	mv_otg_phy_bind_device(mvotg);
+
+	r = platform_get_resource(mvotg->pdev,
+					 IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no I/O memory resource defined\n");
+		retval = -ENODEV;
+		goto err_destroy_workqueue;
+	}
+
+	mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+	if (mvotg->cap_regs == NULL) {
+		dev_err(&pdev->dev, "failed to map I/O memory\n");
+		retval = -EFAULT;
+		goto err_destroy_workqueue;
+	}
+	mvotg->cap_regs = mvotg->cap_regs + U2x_CAPREGS_OFFSET;
+
+	mvotg->outer_phy = devm_usb_get_phy_dev(&pdev->dev, MV_USB2_PHY_INDEX);
+	if (IS_ERR_OR_NULL(mvotg->outer_phy)) {
+		retval = PTR_ERR(mvotg->outer_phy);
+		if (retval != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "can not find outer phy\n");
+		goto err_destroy_workqueue;
+	}
+
+	/* we will acces controller register, so enable the udc controller */
+	retval = mv_otg_enable_internal(mvotg);
+	if (retval) {
+		dev_err(&pdev->dev, "mv otg enable error %d\n", retval);
+		goto err_destroy_workqueue;
+	}
+
+	mvotg->op_regs =
+		(struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs
+			+ (readl(mvotg->cap_regs) & CAPLENGTH_MASK));
+#if defined(CONFIG_CPU_ASR18XX)
+	if (of_property_read_bool(np , "otg,has-idpin-detection"))
+		pdata->extern_attr |= MV_USB_HAS_IDPIN_DETECTION;
+
+	if (of_property_read_bool(np , "otg,use-gpio-vbus")) {
+		if (of_property_read_u32(np , "gpio-num", &mvotg->gpio_num)) {
+			mvotg->gpio_num = ENNUM;
+			dev_info(&pdev->dev, "failed to find GPIO number in dts\n");
+		} else {
+			if (gpio_request(mvotg->gpio_num, "OTGVBUS")) {
+				dev_err(&pdev->dev , "OTG Request GPIO failed,gpio: %d\n" ,
+					mvotg->gpio_num);
+				mvotg->gpio_num = ENNUM;
+			} else
+				gpio_direction_output(mvotg->gpio_num , 0);
+		}
+
+	} else
+		mvotg->gpio_num = ENNUM;
+	mvotg->outer_phy->io_op_regs = mvotg->op_regs;
+
+	if (of_property_read_u32(np , "mvc2-usb3", &mvc2_phandle)) {
+		dev_err(&pdev->dev, "failed to find mvc2-usb3 in dt\n");
+		goto mvc2_disbaled;
+	}
+
+	mvc2_np = of_find_node_by_phandle(mvc2_phandle);
+	if (unlikely(IS_ERR(mvc2_np))) {
+		dev_err(&pdev->dev, "failed to find device_node for mvc2\n");
+		goto mvc2_disbaled;
+	}
+
+	if (of_device_is_available(mvc2_np)) {
+		/*do nothing,usb3 will handle it*/
+	} else {
+mvc2_disbaled:
+		pdata->extern_attr |= MV_USB_HAS_VBUS_DETECTION
+					| MV_USB_HAS_IDPIN_DETECTION;
+
+		if (of_property_read_bool(np , "otg,has-idpin-detection"))
+			pdata->extern_attr |= MV_USB_HAS_IDPIN_DETECTION;
+		else
+			pdata->extern_attr &= ~MV_USB_HAS_IDPIN_DETECTION;
+	}
+#endif
+
+	if (pdata->extern_attr
+		& (MV_USB_HAS_VBUS_DETECTION | MV_USB_HAS_IDPIN_DETECTION)) {
+		if (cpu_is_asr1803())
+			mvotg->notifier.notifier_call = asr_otg_notifier_callback;
+		else
+			mvotg->notifier.notifier_call = mv_otg_notifier_callback;
+		pxa_usb_register_notifier(mvotg->pdata->id, &mvotg->notifier);
+		if (pdata->extern_attr & MV_USB_HAS_VBUS_DETECTION) {
+			mvotg->clock_gating = 1;
+			pxa_usb_extern_call(mvotg->pdata->id, vbus, init);
+		}
+		if (pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION)
+			pxa_usb_extern_call(mvotg->pdata->id, idpin, init);
+	}
+
+	if (pdata->disable_otg_clock_gating)
+		mvotg->clock_gating = 0;
+#if defined(CONFIG_CPU_ASR18XX) && defined(CONFIG_USB_MVC2)
+	else
+		mvotg->clock_gating = 1;
+#endif
+
+#ifdef CONFIG_USB_TELEPHONY
+	pr_info("%s: force to disable clk gating as no vbus detection\n",
+		__func__);
+	mvotg->clock_gating = 0;
+#endif
+
+	mv_otg_reset(mvotg);
+	if (cpu_is_asr1803())
+		asr_otg_init_irq(mvotg);
+	else
+		mv_otg_init_irq(mvotg);
+	r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no IRQ resource defined\n");
+		retval = -ENODEV;
+		goto err_disable_clk;
+	}
+
+	mvotg->irq = r->start;
+	retval = usb_add_phy_dev(&mvotg->phy);
+	if (retval < 0) {
+		dev_err(&pdev->dev, "can't register transceiver, %d\n",
+			retval);
+		goto err_disable_clk;
+	}
+
+	prop = of_get_property(np, "lpm-qos", &proplen);
+	if (!prop) {
+		pr_err("lpm-qos config in DT for mv_otg is not defined\n");
+		goto err_disable_clk;
+	} else
+		mvotg->lpm_qos = be32_to_cpup(prop);
+
+	mvotg->qos_idle.name = mvotg->pdev->name;
+	pm_qos_add_request(&mvotg->qos_idle, PM_QOS_CPUIDLE_BLOCK,
+			PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
+
+	retval = sysfs_create_group(&pdev->dev.kobj, &inputs_attr_group);
+	if (retval < 0) {
+		dev_dbg(&pdev->dev,
+			"Can't register sysfs attr group: %d\n", retval);
+		goto err_remove_otg_phy;
+	}
+
+	spin_lock_init(&mvotg->wq_lock);
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 2 * HZ);
+		spin_unlock(&mvotg->wq_lock);
+	}
+
+	dev_info(&pdev->dev,
+		 "successful probe OTG device %s clock gating.\n",
+		 mvotg->clock_gating ? "with" : "without");
+
+	device_init_wakeup(&pdev->dev, 1);
+	if (!(pdata->extern_attr & MV_USB_HAS_IDPIN_DETECTION)) {
+		enable_irq_wake(mvotg->irq);
+		mv_otg_usbid_wakeup_en(1);
+	}
+
+	/* enable usb line stat wakeup */
+	prop = of_get_property(np, "support-wkresume", &proplen);
+	if (prop)
+		mvotg->support_wkresume = 1;
+	else
+		mvotg->support_wkresume = 0;
+
+#if defined(CONFIG_CPU_ASR18XX) && defined(CONFIG_USB_MVC2)
+	pxa_usb_set_extern_call(PXA_USB_DEV_MVC2, mvc2_idpin, get_idpin,
+				mv_otg_read_id_val);
+#endif
+
+	if (of_property_read_u32(np , "otg-force-dev-mode", &otg_force_dev_mode)) {
+		otg_force_dev_mode = 0;
+	} else {
+		dev_info(&pdev->dev, "otg force dev mode\n");
+		otg_force_dev_mode = 1;
+	}
+
+	if (of_property_read_u32(np , "gpio-usbid", &mvotg->gpio_usbid)) {
+		mvotg->gpio_usbid = -EINVAL;
+		dev_info(&pdev->dev, "No gpio usbid in dts\n");
+		if (cpu_is_asr1803()) {
+			if (devm_request_irq(&pdev->dev, mvotg->irq, asr_otg_irq, IRQF_SHARED,
+					driver_name, mvotg)) {
+				dev_err(&pdev->dev, "Request irq %d for OTG failed\n",
+					mvotg->irq);
+				mvotg->irq = 0;
+				retval = -ENODEV;
+				goto err_remove_otg_phy;
+			}
+		} else {
+			if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED,
+					driver_name, mvotg)) {
+				dev_err(&pdev->dev, "Request irq %d for OTG failed\n",
+					mvotg->irq);
+				mvotg->irq = 0;
+				retval = -ENODEV;
+				goto err_remove_otg_phy;
+			}
+		}
+	} else {
+		pr_info("gpio_usbid: %d\n", mvotg->gpio_usbid);
+		init_gpio_usbid(pdev, mvotg);
+	}
+	return 0;
+
+err_remove_otg_phy:
+	usb_remove_phy(&mvotg->phy);
+	pm_qos_remove_request(&mvotg->qos_idle);
+err_disable_clk:
+	mv_otg_disable_internal(mvotg);
+	if (pdata->extern_attr
+		& (MV_USB_HAS_VBUS_DETECTION | MV_USB_HAS_IDPIN_DETECTION))
+		pxa_usb_unregister_notifier(mvotg->pdata->id, &mvotg->notifier);
+	the_controller = NULL;
+err_destroy_workqueue:
+	flush_workqueue(mvotg->qwork);
+	destroy_workqueue(mvotg->qwork);
+
+	return retval;
+}
+
+#ifdef CONFIG_PM
+static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct mv_otg *mvotg = platform_get_drvdata(pdev);
+
+#ifndef CONFIG_USB_TELEPHONY
+	if (!mvotg->clock_gating)
+		mv_otg_disable_internal(mvotg);
+#endif
+	if (mvotg->is_hostmode && mvotg->disable_host_wakelock)
+		pm_qos_update_request(&mvotg->qos_idle,
+			PM_QOS_CPUIDLE_BLOCK_DEFAULT_VALUE);
+	else
+		mvotg->phy.state = OTG_STATE_UNDEFINED;
+
+	return 0;
+}
+
+static int mv_otg_resume(struct platform_device *pdev)
+{
+#ifdef CONFIG_USB_TELEPHONY
+	struct mv_otg *mvotg = platform_get_drvdata(pdev);
+	u32 otgsc;
+
+	mv_otg_enable_internal(mvotg);
+
+	otgsc = readl(&mvotg->op_regs->otgsc);
+	otgsc |= mvotg->irq_en;
+	writel(otgsc, &mvotg->op_regs->otgsc);
+
+	if (spin_trylock(&mvotg->wq_lock)) {
+		mv_otg_run_state_machine(mvotg, 0);
+		spin_unlock(&mvotg->wq_lock);
+	}
+#else
+	struct mv_otg *mvotg = platform_get_drvdata(pdev);
+
+	if (mvotg->is_hostmode && mvotg->disable_host_wakelock)
+		pm_qos_update_request(&mvotg->qos_idle, mvotg->lpm_qos);
+#endif
+
+	return 0;
+}
+#endif
+
+static const struct of_device_id mv_otg_dt_match[] = {
+	{ .compatible = "marvell,mv-otg" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mv_udc_dt_match);
+
+static struct platform_driver mv_otg_driver = {
+	.probe = mv_otg_probe,
+	.remove = __exit_p(mv_otg_remove),
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(mv_otg_dt_match),
+		   .name = driver_name,
+		   },
+#ifdef CONFIG_PM
+	.suspend = mv_otg_suspend,
+	.resume = mv_otg_resume,
+#endif
+};
+module_platform_driver(mv_otg_driver);
diff --git a/marvell/linux/drivers/usb/phy/phy-mv-usb.h b/marvell/linux/drivers/usb/phy/phy-mv-usb.h
new file mode 100644
index 0000000..5b9189d
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-mv-usb.h
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ */
+
+#ifndef	__MV_USB_OTG_CONTROLLER__
+#define	__MV_USB_OTG_CONTROLLER__
+
+#include <linux/types.h>
+
+/* Command Register Bit Masks */
+#define USBCMD_RUN_STOP			(0x00000001)
+#define USBCMD_CTRL_RESET		(0x00000002)
+
+/* PORTSCX  Register Bit Masks */
+#define PORTSCX_PHY_LOW_POWER_SPD		(0x00800000)
+
+/* otgsc Register Bit Masks */
+#define OTGSC_CTRL_VUSB_DISCHARGE		0x00000001
+#define OTGSC_CTRL_VUSB_CHARGE			0x00000002
+#define OTGSC_CTRL_OTG_TERM			0x00000008
+#define OTGSC_CTRL_DATA_PULSING			0x00000010
+#define OTGSC_STS_USB_ID			0x00000100
+#define OTGSC_STS_A_VBUS_VALID			0x00000200
+#define OTGSC_STS_A_SESSION_VALID		0x00000400
+#define OTGSC_STS_B_SESSION_VALID		0x00000800
+#define OTGSC_STS_B_SESSION_END			0x00001000
+#define OTGSC_STS_1MS_TOGGLE			0x00002000
+#define OTGSC_STS_DATA_PULSING			0x00004000
+#define OTGSC_INTSTS_USB_ID			0x00010000
+#define OTGSC_INTSTS_A_VBUS_VALID		0x00020000
+#define OTGSC_INTSTS_A_SESSION_VALID		0x00040000
+#define OTGSC_INTSTS_B_SESSION_VALID		0x00080000
+#define OTGSC_INTSTS_B_SESSION_END		0x00100000
+#define OTGSC_INTSTS_1MS			0x00200000
+#define OTGSC_INTSTS_DATA_PULSING		0x00400000
+#define OTGSC_INTR_USB_ID			0x01000000
+#define OTGSC_INTR_A_VBUS_VALID			0x02000000
+#define OTGSC_INTR_A_SESSION_VALID		0x04000000
+#define OTGSC_INTR_B_SESSION_VALID		0x08000000
+#define OTGSC_INTR_B_SESSION_END		0x10000000
+#define OTGSC_INTR_1MS_TIMER			0x20000000
+#define OTGSC_INTR_DATA_PULSING			0x40000000
+
+#define CAPLENGTH_MASK		(0xff)
+
+/* Timer's interval, unit 10ms */
+#define T_A_WAIT_VRISE		100
+#define T_A_WAIT_BCON		2000
+#define T_A_AIDL_BDIS		100
+#define T_A_BIDL_ADIS		20
+#define T_B_ASE0_BRST		400
+#define T_B_SE0_SRP		300
+#define T_B_SRP_FAIL		2000
+#define T_B_DATA_PLS		10
+#define T_B_SRP_INIT		100
+#define T_A_SRP_RSPNS		10
+#define T_A_DRV_RSM		5
+
+enum otg_function {
+	OTG_B_DEVICE = 0,
+	OTG_A_DEVICE
+};
+
+enum mv_otg_timer {
+	A_WAIT_BCON_TIMER = 0,
+#if defined(CONFIG_CPU_ASR18XX)
+	A_WAIT_VRISE_TIMER,
+#endif
+	OTG_TIMER_NUM
+};
+
+/* PXA OTG state machine */
+struct mv_otg_ctrl {
+	/* internal variables */
+	u8 a_set_b_hnp_en;	/* A-Device set b_hnp_en */
+	u8 b_srp_done;
+	u8 b_hnp_en;
+
+	/* OTG inputs */
+	u8 a_bus_drop;
+	u8 a_bus_req;
+	u8 a_clr_err;
+	u8 a_bus_resume;
+	u8 a_bus_suspend;
+	u8 a_conn;
+	u8 a_sess_vld;
+	u8 a_srp_det;
+	u8 a_vbus_vld;
+	u8 b_bus_req;		/* B-Device Require Bus */
+	u8 b_bus_resume;
+	u8 b_bus_suspend;
+	u8 b_conn;
+	u8 b_se0_srp;
+	u8 b_sess_end;
+	u8 b_sess_vld;
+	u8 id;
+	u8 a_suspend_req;
+
+	/*Timer event */
+	u8 a_aidl_bdis_timeout;
+	u8 b_ase0_brst_timeout;
+	u8 a_bidl_adis_timeout;
+	u8 a_wait_bcon_timeout;
+#if defined(CONFIG_CPU_ASR18XX)
+	u8 a_wait_vrise_timeout;
+#endif
+	struct timer_list timer[OTG_TIMER_NUM];
+};
+
+#define VUSBHS_MAX_PORTS	8
+
+struct mv_otg_regs {
+	u32 usbcmd;		/* Command register */
+	u32 usbsts;		/* Status register */
+	u32 usbintr;		/* Interrupt enable */
+	u32 frindex;		/* Frame index */
+	u32 reserved1[1];
+	u32 deviceaddr;		/* Device Address */
+	u32 eplistaddr;		/* Endpoint List Address */
+	u32 ttctrl;		/* HOST TT status and control */
+	u32 burstsize;		/* Programmable Burst Size */
+	u32 txfilltuning;	/* Host Transmit Pre-Buffer Packet Tuning */
+	u32 reserved[4];
+	u32 epnak;		/* Endpoint NAK */
+	u32 epnaken;		/* Endpoint NAK Enable */
+	u32 configflag;		/* Configured Flag register */
+	u32 portsc[VUSBHS_MAX_PORTS];	/* Port Status/Control x, x = 1..8 */
+	u32 otgsc;
+	u32 usbmode;		/* USB Host/Device mode */
+	u32 epsetupstat;	/* Endpoint Setup Status */
+	u32 epprime;		/* Endpoint Initialize */
+	u32 epflush;		/* Endpoint De-initialize */
+	u32 epstatus;		/* Endpoint Status */
+	u32 epcomplete;		/* Endpoint Interrupt On Complete */
+	u32 epctrlx[16];	/* Endpoint Control, where x = 0.. 15 */
+	u32 mcr;		/* Mux Control */
+	u32 isr;		/* Interrupt Status */
+	u32 ier;		/* Interrupt Enable */
+};
+
+struct pm_qos_request;
+
+struct mv_otg {
+	struct usb_phy phy;
+	struct usb_phy *outer_phy;
+	struct mv_otg_ctrl otg_ctrl;
+
+	/* base address */
+	void __iomem *phy_regs;
+	void __iomem *cap_regs;
+	struct mv_otg_regs __iomem *op_regs;
+
+	struct platform_device *pdev;
+	int irq;
+	u32 irq_status;
+	u32 irq_en;
+
+	struct delayed_work work;
+	struct workqueue_struct *qwork;
+
+	spinlock_t wq_lock;
+
+	struct mv_usb_platform_data *pdata;
+	struct notifier_block notifier;
+
+	struct pm_qos_request   qos_idle;
+	s32                     lpm_qos;
+
+	unsigned int active;
+	unsigned int clock_gating;
+	struct clk *clk;
+#if defined(CONFIG_CPU_ASR18XX) && defined(CONFIG_USB_MVC2)
+	bool mvc2_state;
+#endif
+	u32 support_wkresume:1;
+	u32 is_hostmode:1;
+	u32 disable_host_wakelock:1;
+	int gpio_num;
+
+	/* gpio-based usbid pin */
+	int gpio_usbid;
+	int edge_gpio;
+	int gpio_irq;
+};
+
+#endif
diff --git a/marvell/linux/drivers/usb/phy/phy-mv-usb2.c b/marvell/linux/drivers/usb/phy/phy-mv-usb2.c
new file mode 100644
index 0000000..a053968
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-mv-usb2.c
@@ -0,0 +1,2317 @@
+/*
+ * Copyright (C) 2013 Marvell Inc.
+ *
+ * Author:
+ *	Chao Xie <xiechao.mail@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/resource.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mv_usb.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/mv_usb2_phy.h>
+#include <dt-bindings/usb/mv_usb_phy.h>
+#include <linux/features.h>
+#include <linux/cputype.h>
+#include <linux/usb/ehci_def.h>
+#include <soc/asr/regs-addr.h>
+
+#include <linux/pm_qos.h>
+#include "phy-mv-usb.h"
+#include <linux/usb/composite.h>
+
+/* phy regs */
+/* for pxa910 and mmp2, there is no revision register */
+#define PHY_55NM_REVISION		0x0
+#define PHY_55NM_CTRL		0x4
+#define PHY_55NM_PLL		0x8
+#define PHY_55NM_TX			0xc
+#define PHY_55NM_RX			0x10
+#define PHY_55NM_IVREF		0x14
+#define PHY_55NM_T0			0x18
+#define PHY_55NM_T1			0x1c
+#define PHY_55NM_T2			0x20
+#define PHY_55NM_T3			0x24
+#define PHY_55NM_T4			0x28
+#define PHY_55NM_T5			0x2c
+#define PHY_55NM_RESERVE		0x30
+#define PHY_55NM_USB_INT		0x34
+#define PHY_55NM_DBG_CTL		0x38
+#define PHY_55NM_OTG_ADDON		0x3c
+
+/* For UTMICTRL Register */
+#define PHY_55NM_CTRL_USB_CLK_EN			(1 << 31)
+/* pxa168 */
+#define PHY_55NM_CTRL_SUSPEND_SET1			(1 << 30)
+#define PHY_55NM_CTRL_SUSPEND_SET2			(1 << 29)
+#define PHY_55NM_CTRL_RXBUF_PDWN			(1 << 24)
+#define PHY_55NM_CTRL_TXBUF_PDWN			(1 << 11)
+
+#define PHY_55NM_CTRL_INPKT_DELAY_SHIFT		30
+#define PHY_55NM_CTRL_INPKT_DELAY_SOF_SHIFT		28
+#define PHY_55NM_CTRL_PU_REF_SHIFT			20
+#define PHY_55NM_CTRL_ARC_PULLDN_SHIFT		12
+#define PHY_55NM_CTRL_PLL_PWR_UP_SHIFT		1
+#define PHY_55NM_CTRL_PWR_UP_SHIFT			0
+
+/* For UTMI_PLL Register */
+#define PHY_55NM_PLL_PLLCALI12_SHIFT		29
+#define PHY_55NM_PLL_PLLCALI12_MASK			(0x3 << 29)
+
+#define PHY_55NM_PLL_PLLVDD18_SHIFT			27
+#define PHY_55NM_PLL_PLLVDD18_MASK			(0x3 << 27)
+
+#define PHY_55NM_PLL_PLLVDD12_SHIFT			25
+#define PHY_55NM_PLL_PLLVDD12_MASK			(0x3 << 25)
+
+#define PHY_55NM_PLL_PLL_READY			(0x1 << 23)
+#define PHY_55NM_PLL_KVCO_EXT			(0x1 << 22)
+#define PHY_55NM_PLL_VCOCAL_START			(0x1 << 21)
+
+#define PHY_55NM_PLL_KVCO_SHIFT			15
+#define PHY_55NM_PLL_KVCO_MASK			(0x7 << 15)
+
+#define PHY_55NM_PLL_ICP_SHIFT			12
+#define PHY_55NM_PLL_ICP_MASK			(0x7 << 12)
+
+#define PHY_55NM_PLL_FBDIV_SHIFT			4
+#define PHY_55NM_PLL_FBDIV_MASK			(0xFF << 4)
+
+#define PHY_55NM_PLL_REFDIV_SHIFT			0
+#define PHY_55NM_PLL_REFDIV_MASK			(0xF << 0)
+
+/* For UTMI_TX Register */
+#define PHY_55NM_TX_REG_EXT_FS_RCAL_SHIFT		27
+#define PHY_55NM_TX_REG_EXT_FS_RCAL_MASK		(0xf << 27)
+
+#define PHY_55NM_TX_REG_EXT_FS_RCAL_EN_SHIFT	26
+#define PHY_55NM_TX_REG_EXT_FS_RCAL_EN_MASK		(0x1 << 26)
+
+#define PHY_55NM_TX_TXVDD12_SHIFT			22
+#define PHY_55NM_TX_TXVDD12_MASK			(0x3 << 22)
+
+#define PHY_55NM_TX_CK60_PHSEL_SHIFT		17
+#define PHY_55NM_TX_CK60_PHSEL_MASK			(0xf << 17)
+
+#define PHY_55NM_TX_IMPCAL_VTH_SHIFT		14
+#define PHY_55NM_TX_IMPCAL_VTH_MASK			(0x7 << 14)
+
+#define PHY_55NM_TX_REG_RCAL_START			(0x1 << 12)
+
+#define PHY_55NM_TX_LOW_VDD_EN_SHIFT		11
+
+#define PHY_55NM_TX_AMP_SHIFT			0
+#define PHY_55NM_TX_AMP_MASK			(0x7 << 0)
+
+/* For UTMI_RX Register */
+#define PHY_55NM_RX_REG_SQ_LENGTH_SHIFT		15
+#define PHY_55NM_RX_REG_SQ_LENGTH_MASK		(0x3 << 15)
+
+#define PHY_55NM_RX_SQ_THRESH_SHIFT			4
+#define PHY_55NM_RX_SQ_THRESH_MASK			(0xf << 4)
+
+/* For UTMI_OTG_ADDON Register. Only for pxa168 */
+#define PHY_55NM_OTG_ADDON_OTG_ON			(1 << 0)
+
+
+/* For pxa988 the register mapping are changed*/
+#define PHY_40NM_PLL0		0x4
+#define PHY_40NM_PLL1		0x8
+#define PHY_40NM_TX0			0x10
+#define PHY_40NM_TX1			0x14
+#define PHY_40NM_TX2			0x18
+#define PHY_40NM_RX0			0x20
+#define PHY_40NM_RX1			0x24
+#define PHY_40NM_RX2			0x28
+#define PHY_40NM_ANA0		0x30
+#define PHY_40NM_ANA1		0x34
+#define PHY_40NM_DIG0		0x3c
+#define PHY_40NM_DIG1		0x40
+#define PHY_40NM_DIG2		0x44
+#define PHY_40NM_T0			0x4c
+#define PHY_40NM_T1			0x50
+#define PHY_40NM_CHARGE0		0x58
+#define PHY_40NM_OTG			0x5C
+#define PHY_40NM_PHY_MON		0x60
+#define PHY_40NM_RESERVE0		0x64
+#define PHY_40NM_CTRL		0x104
+#define PHY_40NM_STATUS_A0C		0x108
+#define PHY_40NM_STATUS		0x128
+
+/* default values are got from spec */
+#define PHY_40NM_PLL0_DEFAULT	0x5A78
+#define PHY_40NM_PLL1_DEFAULT	0x0231
+#define PHY_40NM_TX0_DEFAULT		0x0488
+#define PHY_40NM_TX1_DEFAULT		0x05B0
+#define PHY_40NM_TX2_DEFAULT		0x02FF
+#define PHY_40NM_RX0_DEFAULT		0xAA71
+#define PHY_40NM_RX1_DEFAULT		0x3892
+#define PHY_40NM_RX2_DEFAULT		0x0125
+#define PHY_40NM_ANA1_DEFAULT	0x1680
+#define PHY_40NM_OTG_DEFAULT		0x0
+#define PHY_40NM_CTRL_DEFAULT	0x00801000
+#define PHY_40NM_CTRL_OTG_DEFAULT	0x0398F000
+
+#define PHY_40NM_PLL0_PLLVDD18(x)			(((x) & 0x3) << 14)
+#define PHY_40NM_PLL0_REFDIV(x)			(((x) & 0x1f) << 9)
+#define PHY_40NM_PLL0_FBDIV(x)			(((x) & 0x1ff) << 0)
+
+#define PHY_40NM_PLL1_PLL_READY			(0x1 << 15)
+#define PHY_40NM_PLL1_PLL_CONTROL_BY_PIN		(0x1 << 14)
+#define PHY_40NM_PLL1_PU_PLL				(0x1 << 13)
+#define PHY_40NM_PLL1_PLL_LOCK_BYPASS		(0x1 << 12)
+#define PHY_40NM_PLL1_DLL_RESET			(0x1 << 11)
+#define PHY_40NM_PLL1_ICP(x)				(((x) & 0x7) << 8)
+#define PHY_40NM_PLL1_KVCO_EXT			(0x1 << 7)
+#define PHY_40NM_PLL1_KVCO(x)			(((x) & 0x7) << 4)
+#define PHY_40NM_PLL1_CLK_BLK_EN			(0x1 << 3)
+#define PHY_40NM_PLL1_VCOCAL_START			(0x1 << 2)
+#define PHY_40NM_PLL1_PLLCAL12(x)			(((x) & 0x3) << 0)
+
+#define PHY_40NM_TX0_TXDATA_BLK_EN			(0x1 << 14)
+#define PHY_40NM_TX0_RCAL_START			(0x1 << 13)
+#define PHY_40NM_TX0_EXT_HS_RCAL_EN			(0x1 << 12)
+#define PHY_40NM_TX0_EXT_FS_RCAL_EN			(0x1 << 11)
+#define PHY_40NM_TX0_IMPCAL_VTH(x)			(((x) & 0x7) << 8)
+#define PHY_40NM_TX0_EXT_HS_RCAL(x)			(((x) & 0xf) << 4)
+#define PHY_40NM_TX0_EXT_FS_RCAL(x)			(((x) & 0xf) << 0)
+
+#define PHY_40NM_TX1_TXVDD15(x)			(((x) & 0x3) << 10)
+#define PHY_40NM_TX1_TXVDD12(x)			(((x) & 0x3) << 8)
+#define PHY_40NM_TX1_LOWVDD_EN			(0x1 << 7)
+#define PHY_40NM_TX1_AMP(x)				(((x) & 0x7) << 4)
+#define PHY_40NM_TX1_CK60_PHSEL(x)			(((x) & 0xf) << 0)
+
+#define PHY_40NM_TX2_DRV_SLEWRATE(x)			(((x) & 0x3) << 10)
+#define PHY_40NM_TX2_IMP_CAL_DLY(x)			(((x) & 0x3) << 8)
+#define PHY_40NM_TX2_FSDRV_EN(x)			(((x) & 0xf) << 4)
+#define PHY_40NM_TX2_HSDEV_EN(x)			(((x) & 0xf) << 0)
+
+#define PHY_40NM_RX0_PHASE_FREEZE_DLY		(0x1 << 15)
+#define PHY_40NM_RX0_USQ_LENGTH			(0x1 << 14)
+#define PHY_40NM_RX0_ACQ_LENGTH(x)			(((x) & 0x3) << 12)
+#define PHY_40NM_RX0_SQ_LENGTH(x)			(((x) & 0x3) << 10)
+#define PHY_40NM_RX0_DISCON_THRESH(x)		(((x) & 0x3) << 8)
+#define PHY_40NM_RX0_SQ_THRESH(x)			(((x) & 0xf) << 4)
+#define PHY_40NM_RX0_LPF_COEF(x)			(((x) & 0x3) << 2)
+#define PHY_40NM_RX0_INTPI(x)			(((x) & 0x3) << 0)
+
+#define PHY_40NM_RX1_EARLY_VOS_ON_EN			(0x1 << 13)
+#define PHY_40NM_RX1_RXDATA_BLOCK_EN			(0x1 << 12)
+#define PHY_40NM_RX1_EDGE_DET_EN			(0x1 << 11)
+#define PHY_40NM_RX1_CAP_SEL(x)			(((x) & 0x7) << 8)
+#define PHY_40NM_RX1_RXDATA_BLOCK_LENGTH(x)		(((x) & 0x3) << 6)
+#define PHY_40NM_RX1_EDGE_DET_SEL(x)			(((x) & 0x3) << 4)
+#define PHY_40NM_RX1_CDR_COEF_SEL			(0x1 << 3)
+#define PHY_40NM_RX1_CDR_FASTLOCK_EN			(0x1 << 2)
+#define PHY_40NM_RX1_S2TO3_DLY_SEL(x)		(((x) & 0x3) << 0)
+
+#define PHY_40NM_RX2_USQ_FILTER			(0x1 << 8)
+#define PHY_40NM_RX2_SQ_CM_SEL			(0x1 << 7)
+#define PHY_40NM_RX2_SAMPLER_CTRL			(0x1 << 6)
+#define PHY_40NM_RX2_SQ_BUFFER_EN			(0x1 << 5)
+#define PHY_40NM_RX2_SQ_ALWAYS_ON			(0x1 << 4)
+#define PHY_40NM_RX2_RXVDD18(x)			(((x) & 0x3) << 2)
+#define PHY_40NM_RX2_RXVDD12(x)			(((x) & 0x3) << 0)
+
+#define PHY_40NM_ANA0_BG_VSEL(x)			(((x) & 0x3) << 8)
+#define PHY_40NM_ANA0_DIG_SEL(x)			(((x) & 0x3) << 6)
+#define PHY_40NM_ANA0_TOPVDD18(x)			(((x) & 0x3) << 4)
+#define PHY_40NM_ANA0_VDD_USB2_DIG_TOP_SEL		(0x1 << 3)
+#define PHY_40NM_ANA0_IPTAT_SEL(x)			(((x) & 0x7) << 0)
+
+#define PHY_40NM_ANA1_PU_ANA				(0x1 << 14)
+#define PHY_40NM_ANA1_ANA_CONTROL_BY_PIN		(0x1 << 13)
+#define PHY_40NM_ANA1_SEL_LPFR			(0x1 << 12)
+#define PHY_40NM_ANA1_V2I_EXT			(0x1 << 11)
+#define PHY_40NM_ANA1_V2I(x)				(((x) & 0x7) << 8)
+#define PHY_40NM_ANA1_R_ROTATE_SEL			(0x1 << 7)
+#define PHY_40NM_ANA1_STRESS_TEST_MODE		(0x1 << 6)
+#define PHY_40NM_ANA1_TESTMON_ANA(x)			(((x) & 0x3f) << 0)
+
+#define PHY_40NM_DIG0_FIFO_UF			(0x1 << 15)
+#define PHY_40NM_DIG0_FIFO_OV			(0x1 << 14)
+#define PHY_40NM_DIG0_FS_EOP_MODE			(0x1 << 13)
+#define PHY_40NM_DIG0_HOST_DISCON_SEL1		(0x1 << 12)
+#define PHY_40NM_DIG0_HOST_DISCON_SEL0		(0x1 << 11)
+#define PHY_40NM_DIG0_FORCE_END_EN			(0x1 << 10)
+#define PHY_40NM_DIG0_SYNCDET_WINDOW_EN		(0x1 << 8)
+#define PHY_40NM_DIG0_CLK_SUSPEND_EN			(0x1 << 7)
+#define PHY_40NM_DIG0_HS_DRIBBLE_EN			(0x1 << 6)
+#define PHY_40NM_DIG0_SYNC_NUM(x)			(((x) & 0x3) << 4)
+#define PHY_40NM_DIG0_FIFO_FILL_NUM(x)		(((x) & 0xf) << 0)
+
+#define PHY_40NM_DIG1_FS_RX_ERROR_MODE2		(0x1 << 15)
+#define PHY_40NM_DIG1_FS_RX_ERROR_MODE1		(0x1 << 14)
+#define PHY_40NM_DIG1_FS_RX_ERROR_MODE		(0x1 << 13)
+#define PHY_40NM_DIG1_CLK_OUT_SEL			(0x1 << 12)
+#define PHY_40NM_DIG1_EXT_TX_CLK_SEL			(0x1 << 11)
+#define PHY_40NM_DIG1_ARC_DPDM_MODE			(0x1 << 10)
+#define PHY_40NM_DIG1_DP_PULLDOWN			(0x1 << 9)
+#define PHY_40NM_DIG1_DM_PULLDOWN			(0x1 << 8)
+#define PHY_40NM_DIG1_SYNC_IGNORE_SQ			(0x1 << 7)
+#define PHY_40NM_DIG1_SQ_RST_RX			(0x1 << 6)
+#define PHY_40NM_DIG1_MON_SEL(x)			(((x) & 0x3f) << 0)
+
+#define PHY_40NM_DIG2_PAD_STRENGTH(x)		(((x) & 0x1f) << 8)
+#define PHY_40NM_DIG2_LONG_EOP			(0x1 << 5)
+#define PHY_40NM_DIG2_NOVBUS_DPDM00			(0x1 << 4)
+#define PHY_40NM_DIG2_ALIGN_FS_OUTEN			(0x1 << 2)
+#define PHY_40NM_DIG2_HS_HDL_SYNC			(0x1 << 1)
+#define PHY_40NM_DIG2_FS_HDL_OPMD			(0x1 << 0)
+
+#define PHY_40NM_CHARGE0_ENABLE_SWITCH		(0x1 << 3)
+#define PHY_40NM_CHARGE0_PU_CHRG_DTC			(0x1 << 2)
+#define PHY_40NM_CHARGE0_TESTMON_CHRGDTC(x)		(((x) & 0x3) << 0)
+
+#define PHY_40NM_OTG_TESTMODE(x)			(((x) & 0x7) << 0)
+#define PHY_40NM_OTG_CONTROL_BY_PIN			(0x1 << 4)
+#define PHY_40NM_OTG_PU				(0x1 << 3)
+
+#define PHY_40NM_CDP_EN				(0x1 << 2)
+#define PHY_40NM_DCP_EN				(0x1 << 3)
+#define PHY_40NM_PD_EN				(0x1 << 4)
+#define PHY_40NM_CDP_DM_AUTO_SWITCH			(0x1 << 5)
+#define PHY_40NM_ENABLE_SWITCH_DM			(0x1 << 6)
+#define PHY_40NM_ENABLE_SWITCH_DP			(0x1 << 7)
+#define PHY_40NM_VDAT_CHARGE				(0x3 << 8)
+#define PHY_40NM_VSRC_CHARGE				(0x3 << 10)
+#define PHY_40NM_DP_DM_SWAP_CTRL			(0x1 << 15)
+#define PHY_40NM_CHARGER_STATUS_MASK			(0x1 << 4)
+
+#define PHY_40NM_CTRL_AVALID_CONTROL			(0x3 << 24)
+#define PHY_40NM_CTRL_PU_PLL				(0x1 << 1)
+#define PHY_40NM_CTRL_PU				(0x1 << 0)
+
+/* USB PXA1928 PHY mapping */
+#define PHY_28NM_PLL_REG0		0x0
+#define PHY_28NM_PLL_REG1		0x4
+#define PHY_28NM_CAL_REG		0x8
+#define PHY_28NM_TX_REG0		0x0C
+#define PHY_28NM_TX_REG1		0x10
+#define PHY_28NM_RX_REG0		0x14
+#define PHY_28NM_RX_REG1		0x18
+#define PHY_28NM_DIG_REG0		0x1C
+#define PHY_28NM_DIG_REG1		0x20
+#define PHY_28NM_TEST_REG0		0x24
+#define PHY_28NM_TEST_REG1		0x28
+#define PHY_28NM_MOC_REG		0x2C
+#define PHY_28NM_PHY_RESERVE	0x30
+#define PHY_28NM_OTG_REG		0x34
+#define PHY_28NM_CHRG_DET		0x38
+#define PHY_28NM_CTRL_REG0		0xC4
+#define PHY_28NM_CTRL_REG1		0xC8
+#define PHY_28NM_CTRL_REG2		0xD4
+#define PHY_28NM_CTRL_REG3		0xDC
+#define PHY_28NM_VBUSVALID_CTRL_SHIFT	4
+
+/* PHY_28NM_PLL_REG0 */
+#define PHY_28NM_PLL_READY_MASK		(0x1 << 31)
+
+#define PHY_28NM_PLL_SELLPFR_SHIFT		28
+#define PHY_28NM_PLL_SELLPFR_MASK		(0x3 << 28)
+
+#define PHY_28NM_PLL_FBDIV_SHIFT		16
+#define PHY_28NM_PLL_FBDIV_MASK		(0x1ff << 16)
+
+#define PHY_28NM_PLL_ICP_SHIFT			8
+#define PHY_28NM_PLL_ICP_MASK			(0x7 << 8)
+
+#define PHY_28NM_PLL_REFDIV_SHIFT		0
+#define PHY_28NM_PLL_REFDIV_MASK		0x7f
+
+/* PHY_28NM_PLL_REG1 */
+#define PHY_28NM_PLL_PU_BY_REG_SHIFT		1
+#define PHY_28NM_PLL_PU_BY_REG_MASK		(0x1 << 1)
+
+#define PHY_28NM_PLL_PU_PLL_SHIFT		0
+#define PHY_28NM_PLL_PU_PLL_MASK		(0x1 << 0)
+
+/* PHY_28NM_CAL_REG */
+#define PHY_28NM_PLL_PLLCAL_DONE_SHIFT		31
+#define PHY_28NM_PLL_PLLCAL_DONE_MASK		(0x1 << 31)
+
+#define PHY_28NM_PLL_IMPCAL_DONE_SHIFT		23
+#define PHY_28NM_PLL_IMPCAL_DONE_MASK		(0x1 << 23)
+
+#define PHY_28NM_PLL_KVCO_SHIFT		16
+#define PHY_28NM_PLL_KVCO_MASK			(0x7 << 16)
+
+#define PHY_28NM_PLL_CAL12_SHIFT		20
+#define PHY_28NM_PLL_CAL12_MASK		(0x3 << 20)
+
+#define PHY_28NM_IMPCAL_VTH_SHIFT		8
+#define PHY_28NM_IMPCAL_VTH_MASK		(0x7 << 8)
+
+#define PHY_28NM_PLLCAL_START_SHIFT		22
+#define PHY_28NM_IMPCAL_START_SHIFT		13
+
+/* PHY_28NM_TX_REG0 */
+#define PHY_28NM_TX_PU_BY_REG_SHIFT		25
+
+#define PHY_28NM_TX_PU_ANA_SHIFT		24
+
+#define PHY_28NM_TX_AMP_SHIFT			20
+#define PHY_28NM_TX_AMP_MASK			(0x7 << 20)
+
+/* PHY_28NM_RX_REG0 */
+#define PHY_28NM_RX_SQ_THRESH_SHIFT		0
+#define PHY_28NM_RX_SQ_THRESH_MASK		(0xf << 0)
+
+/* PHY_28NM_RX_REG1 */
+#define PHY_28NM_RX_SQCAL_DONE_SHIFT		31
+#define PHY_28NM_RX_SQCAL_DONE_MASK		(0x1 << 31)
+
+/* PHY_28NM_DIG_REG0 */
+#define PHY_28NM_DIG_BITSTAFFING_ERR_MASK	(0x1 << 31)
+#define PHY_28NM_DIG_SYNC_ERR_MASK		(0x1 << 30)
+
+#define PHY_28NM_DIG_SQ_FILT_SHIFT		16
+#define PHY_28NM_DIG_SQ_FILT_MASK		(0x7 << 16)
+
+#define PHY_28NM_DIG_SQ_BLK_SHIFT		12
+#define PHY_28NM_DIG_SQ_BLK_MASK		(0x7 << 12)
+
+#define PHY_28NM_DIG_SYNC_NUM_SHIFT		0
+#define PHY_28NM_DIG_SYNC_NUM_MASK		(0x3 << 0)
+
+#define PHY_28NM_PLL_LOCK_BYPASS_SHIFT		7
+
+/* PHY_28NM_OTG_REG */
+#define PHY_28NM_OTG_CONTROL_BY_PIN_SHIFT	5
+#define PHY_28NM_OTG_PU_OTG_SHIFT		4
+
+#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13
+#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12
+#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28	10
+#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28	8
+#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7
+#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28	6
+#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28	5
+#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28			4
+#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28		3
+#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28		2
+#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0
+
+#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28	4
+#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28		2
+#define PHY_28NM_RX_SQCAL_START_SHIFT		4
+#define PHY_28NM_RX_SQCAL_START_MASK		(0x1 << 4)
+
+#define PHY_28NM_CTRL3_OVERWRITE_SHIFT		0
+#define PHY_28NM_CTRL3_VBUS_VALID_SHIFT		4
+#define PHY_28NM_CTRL3_AVALID_SHIFT		5
+#define PHY_28NM_CTRL3_BVALID_SHIFT		6
+
+#define PHY_28NM_HSIC_PLL_CTRL01 0x1c
+#define PHY_28NM_HSIC_PLL_CTRL2 0x20
+#define PHY_28NM_HSIC_INT 0x28
+
+#if defined(CONFIG_CPU_ASR18XX)
+#define PHY_28NM_HSIC_CTL0			0x08
+#define PHY_28NM_HSIC_TEST2			0x18
+#endif
+
+#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26
+#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0
+#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9
+
+#define PHY_28NM_HSIC_S2H_PU_PLL_SHIFT 10
+#define PHY_28NM_HSIC_H2S_PLL_LOCK_SHIFT 15
+#define PHY_28NM_HSIC_CTRL 0x08
+#define PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT 7
+#define PHY_28NM_HSIC_S2H_HSIC_CONN_BYP_SHIFT 3
+#define PHY_28NM_HSIC_IMPCAL_CAL 0x18
+#define PHY_28NM_HSIC_USB_CTRL 0x24
+#define PHY_28NM_HSIC_CONNECT_INT_BIT (0x2)
+#define PHY_28NM_HSIC_INT_HS_READY (0x4)
+
+#if defined(CONFIG_CPU_ASR18XX)
+#define PHY_28NM_HSIC_H2S_IMPCAL_DONE_SHIFT 11
+#else
+#define PHY_28NM_HSIC_H2S_IMPCAL_DONE_SHIFT 27
+#endif
+#if defined(CONFIG_CPU_ASR18XX)
+#define	PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT 0
+#define	PHY_28NM_HSIC_CTL0_S2H_DRV_SE0_4RESUME			14
+#define	PHY_28NM_HSIC_CTL0_S2H_BUS_RESET_EN				5
+#define PHY_28NM_HSIC_DPPULLDOWN_CTRL_DEVICE			(0x1 << 5)
+#define PHY_28NM_HSIC_DPPULLDOWN_CTRL_MASK			(0x3 << 5)
+#endif
+
+#define PHY_28NMHP_PHY_REG00	(0x00)
+#define PHY_28NMHP_PHY_REG01	(0x04)
+#define PHY_28NMHP_PHY_REG02	(0x08)
+#define PHY_28NMHP_PHY_REG03	(0x0C)
+#define PHY_28NMHP_PHY_REG04	(0x10)
+#define PHY_28NMHP_PHY_REG05	(0x14)
+#define PHY_28NMHP_PHY_REG06	(0x18)
+#define PHY_28NMHP_PHY_REG07	(0x1C)
+#define PHY_28NMHP_PHY_REG08	(0x20)
+#define PHY_28NMHP_PHY_REG09	(0x24)
+#define PHY_28NMHP_PHY_REG0A	(0x28)
+#define PHY_28NMHP_PHY_REG0B	(0x2C)
+#define PHY_28NMHP_PHY_REG0C	(0x30)
+#define PHY_28NMHP_PHY_REG0D	(0x34)
+#define PHY_28NMHP_PHY_REG0E	(0x38)
+#define PHY_28NMHP_PHY_REG10	(0x40)
+#define PHY_28NMHP_PHY_REG22	(0x88)
+#define PHY_28NMHP_PHY_REG23	(0x8C)
+#define PHY_28NMHP_PHY_REG25	(0x94)
+#define PHY_28NMHP_PHY_REG28	(0xA0)
+#define PHY_28NMHP_PHY_REG29	(0xA4)
+#define PHY_28NMHP_PHY_REG33	(0xCC)
+
+#define PHY_28NMHP_PLL_BIT_RDY			(0x1 << 0)
+#define PHY_28NMHP_CFG_HS_SRCS_SEL		(0x1 << 0)
+#define PHY_28NMHP_SUSPEND_PLL			(0x1 << 13)
+#define PHY_28NMHP_SUSPEND_PHY			(0x1 << 12)
+#define PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN			(0x1 << 14)
+#define PHY_28NMHP_DCP_DET_PULL_UP_DOWN				((0x1 << 11) | (0x1 << 8))
+#define PHY_28NMHP_DCP_DET_PULL_UP_NONE				((0x1 << 11) | (0x1 << 10))
+#define PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN			((0x1 << 10) | (0x1 << 8))
+#define PHY_28NMHP_DCP_DET_PULL_MASK				(0xF << 8)
+#define PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_MASK			(0xF << 8)
+#define PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_VAL			(0x9 << 8)
+#define APMU_USB_CLK_CTRL 					(0x5C)
+
+static int _mv_usb2_phy_55nm_init(struct mv_usb2_phy *mv_phy)
+{
+	struct platform_device *pdev = mv_phy->pdev;
+	unsigned int loops = 0;
+	void __iomem *base = mv_phy->base;
+	unsigned int val;
+
+	val = readl(base + PHY_55NM_CTRL);
+	/* Initialize the USB PHY power */
+	if (mv_phy->drv_data.phy_rev == REV_PXA910) {
+		val |= (1 << PHY_55NM_CTRL_INPKT_DELAY_SOF_SHIFT)
+			| (1 << PHY_55NM_CTRL_PU_REF_SHIFT);
+	}
+
+	val |= (1 << PHY_55NM_CTRL_PLL_PWR_UP_SHIFT)
+		| (1 << PHY_55NM_CTRL_PWR_UP_SHIFT);
+	writel(val, base + PHY_55NM_CTRL);
+
+	/* UTMI_PLL settings */
+	val = readl(base + PHY_55NM_PLL);
+	val &= ~(PHY_55NM_PLL_PLLVDD18_MASK
+		| PHY_55NM_PLL_PLLVDD12_MASK
+		| PHY_55NM_PLL_PLLCALI12_MASK
+		| PHY_55NM_PLL_FBDIV_MASK
+		| PHY_55NM_PLL_REFDIV_MASK
+		| PHY_55NM_PLL_ICP_MASK
+		| PHY_55NM_PLL_KVCO_MASK
+		| PHY_55NM_PLL_VCOCAL_START);
+
+	val |= (0xee << PHY_55NM_PLL_FBDIV_SHIFT)
+		| (0xb << PHY_55NM_PLL_REFDIV_SHIFT)
+		| (3 << PHY_55NM_PLL_PLLVDD18_SHIFT)
+		| (3 << PHY_55NM_PLL_PLLVDD12_SHIFT)
+		| (3 << PHY_55NM_PLL_PLLCALI12_SHIFT)
+		| (1 << PHY_55NM_PLL_ICP_SHIFT)
+		| (3 << PHY_55NM_PLL_KVCO_SHIFT);
+	writel(val, base + PHY_55NM_PLL);
+
+	/* UTMI_TX */
+	val = readl(base + PHY_55NM_TX);
+	val &= ~(PHY_55NM_TX_REG_EXT_FS_RCAL_EN_MASK
+		| PHY_55NM_TX_TXVDD12_MASK
+		| PHY_55NM_TX_CK60_PHSEL_MASK
+		| PHY_55NM_TX_IMPCAL_VTH_MASK
+		| PHY_55NM_TX_REG_EXT_FS_RCAL_MASK
+		| PHY_55NM_TX_AMP_MASK
+		| PHY_55NM_TX_REG_RCAL_START);
+	val |= (3 << PHY_55NM_TX_TXVDD12_SHIFT)
+		| (4 << PHY_55NM_TX_CK60_PHSEL_SHIFT)
+		| (4 << PHY_55NM_TX_IMPCAL_VTH_SHIFT)
+		| (8 << PHY_55NM_TX_REG_EXT_FS_RCAL_SHIFT)
+		| (3 << PHY_55NM_TX_AMP_SHIFT);
+	writel(val, base + PHY_55NM_TX);
+
+	/* UTMI_RX */
+	val = readl(base + PHY_55NM_RX);
+	val &= ~(PHY_55NM_RX_SQ_THRESH_MASK
+		| PHY_55NM_RX_REG_SQ_LENGTH_MASK);
+	val |= (7 << PHY_55NM_RX_SQ_THRESH_SHIFT)
+		| (2 << PHY_55NM_RX_REG_SQ_LENGTH_SHIFT);
+	writel(val, base + PHY_55NM_RX);
+
+	/* UTMI_IVREF */
+	if (mv_phy->drv_data.phy_rev == REV_PXA168)
+		/*
+		 * Fixing Microsoft Altair board interface with NEC hub issue -
+		 * Set UTMI_IVREF from 0x4a3 to 0x4bf.
+		 */
+		writel(0x4bf, base + PHY_55NM_IVREF);
+
+	/*
+	 * Toggle VCOCAL_START bit of UTMI_PLL. It is active at rising edge.
+	 * It is low level by UTMI_PLL initialization above.
+	 */
+	val = readl(base + PHY_55NM_PLL);
+	/*
+	 * Delay 200us for low level, and 40us for high level.
+	 * Make sure we can get a effective rising edege.
+	 * It should be set to low after issue rising edge.
+	 * The delay value is suggested by DE.
+	 */
+	udelay(300);
+	val |= PHY_55NM_PLL_VCOCAL_START;
+	writel(val, base + PHY_55NM_PLL);
+	udelay(40);
+	val &= ~PHY_55NM_PLL_VCOCAL_START;
+	writel(val, base + PHY_55NM_PLL);
+
+	/*
+	 * Toggle REG_RCAL_START bit of UTMI_TX.
+	 * it is low level by UTMI_TX initialization above.
+	 */
+	val = readl(base + PHY_55NM_TX);
+	/* same as VCOCAL_START, except it is triggered by low->high->low */
+	udelay(400);
+	val |= PHY_55NM_TX_REG_RCAL_START;
+	writel(val, base + PHY_55NM_TX);
+	udelay(40);
+	val &= ~PHY_55NM_TX_REG_RCAL_START;
+	writel(val, base + PHY_55NM_TX);
+	udelay(400);
+
+	/* Make sure PHY PLL is ready */
+	loops = 0;
+	while (1) {
+		val = readl(base + PHY_55NM_PLL);
+		if (val & PHY_55NM_PLL_PLL_READY)
+			break;
+		udelay(1000);
+		loops++;
+		if (loops > 100) {
+			dev_warn(&pdev->dev, "calibrate timeout, UTMI_PLL %x\n",
+				 readl(base + PHY_55NM_PLL));
+			return -ETIME;
+		}
+	}
+
+	if (mv_phy->drv_data.phy_rev == REV_PXA168) {
+		val = readl(base + PHY_55NM_RESERVE);
+		val |= (1 << 5);
+		writel(val, base + PHY_55NM_RESERVE);
+		/* Turn on UTMI PHY OTG extension */
+		writel(PHY_55NM_OTG_ADDON_OTG_ON,
+		       base + PHY_55NM_OTG_ADDON);
+	}
+
+	return 0;
+}
+
+static int _mv_usb2_phy_55nm_shutdown(struct mv_usb2_phy *mv_phy)
+{
+	void __iomem *base = mv_phy->base;
+	unsigned int val;
+
+	if (mv_phy->drv_data.phy_rev == REV_PXA168)
+		writel(0, base + PHY_55NM_OTG_ADDON);
+
+	val = readl(base + PHY_55NM_CTRL);
+	val &= ~(PHY_55NM_CTRL_RXBUF_PDWN
+		| PHY_55NM_CTRL_TXBUF_PDWN
+		| PHY_55NM_CTRL_USB_CLK_EN
+		| (1 << PHY_55NM_CTRL_PWR_UP_SHIFT)
+		| (1 << PHY_55NM_CTRL_PLL_PWR_UP_SHIFT));
+
+	writel(val, base + PHY_55NM_CTRL);
+
+	return 0;
+}
+
+static int _mv_usb2_phy_40nm_init(struct mv_usb2_phy *mv_phy)
+{
+	void __iomem *base = mv_phy->base;
+
+	u16 tmp16;
+	u32 tmp32;
+
+	/* Program 0xd4207004[8:0]= 0xF0 */
+	/* Program 0xd4207004[13:9]=0xD */
+	writew((PHY_40NM_PLL0_DEFAULT
+		& (~PHY_40NM_PLL0_FBDIV(~0))
+		& (~PHY_40NM_PLL0_REFDIV(~0)))
+		| PHY_40NM_PLL0_FBDIV(0xF0)
+		| PHY_40NM_PLL0_REFDIV(0xD), base + PHY_40NM_PLL0);
+
+	/* Program 0xd4207008[11:8]=0x1 */
+	/* Program 0xd4207008[14]=0x0 */
+	/* Program 0xd4207008[13]=0x1 */
+	/* Program 0xd4207008[12]=0x1 */
+	/* Program 0xd4207008[1:0]=0x3 */
+	writew((PHY_40NM_PLL1_DEFAULT
+		& (~PHY_40NM_PLL1_ICP(~0))
+		& (~PHY_40NM_PLL1_PLL_CONTROL_BY_PIN)
+		& (~PHY_40NM_PLL1_PLLCAL12(~0)))
+		| PHY_40NM_PLL1_ICP(0x1)
+		| PHY_40NM_PLL1_PU_PLL
+		| PHY_40NM_PLL1_PLL_LOCK_BYPASS
+		| PHY_40NM_PLL1_PLLCAL12(3), base + PHY_40NM_PLL1);
+
+	/* Program 0xd4207014[6:4]=0x5 */
+	/* Program 0xd4207014[9:8]=0x3 */
+	/* Program 0xd4207014[3:0]=0x4 */
+	writew((PHY_40NM_TX1_DEFAULT
+		& (~PHY_40NM_TX1_AMP(~0))
+		& (~PHY_40NM_TX1_TXVDD12(~0))
+		& (~PHY_40NM_TX1_CK60_PHSEL(~0)))
+		| PHY_40NM_TX1_AMP(5)
+		| PHY_40NM_TX1_TXVDD12(3)
+		| PHY_40NM_TX1_CK60_PHSEL(4), base + PHY_40NM_TX1);
+
+	/* Program 0xd4207018[11:10]=0x2 */
+	writew((PHY_40NM_TX2_DEFAULT & (~PHY_40NM_TX2_DRV_SLEWRATE(3)))
+		| PHY_40NM_TX2_DRV_SLEWRATE(2), base + PHY_40NM_TX2);
+
+	/* Program 0xd4207020[7:4]=0xa */
+	writew((PHY_40NM_RX0_DEFAULT
+		& (~PHY_40NM_RX0_SQ_THRESH(~0)))
+		| PHY_40NM_RX0_SQ_THRESH(0xA), base + PHY_40NM_RX0);
+
+	/* Program 0xd4207034[14]=0x1 */
+	writew(PHY_40NM_ANA1_DEFAULT
+		| PHY_40NM_ANA1_PU_ANA, base + PHY_40NM_ANA1);
+
+	/* Program 0xd420705c[3]=0x1 */
+	writew(PHY_40NM_OTG_DEFAULT
+		| PHY_40NM_OTG_PU, base + PHY_40NM_OTG);
+
+	/* Program 0xD4207104[1] = 0x1 */
+	/* Program 0xD4207104[0] = 0x1 */
+	tmp32 = readl(base + PHY_40NM_CTRL);
+	writel(tmp32 | PHY_40NM_CTRL_PU_PLL
+		| PHY_40NM_CTRL_PU, base + PHY_40NM_CTRL);
+
+	/* Wait for 200us */
+	udelay(200);
+
+	/* Program 0xd4207008[2]=0x1 */
+	tmp16 = readw(base + PHY_40NM_PLL1);
+	writew(tmp16
+		| PHY_40NM_PLL1_VCOCAL_START, base + PHY_40NM_PLL1);
+
+	/* Wait for 400us */
+	udelay(400);
+
+	/* Polling 0xd4207008[15]=0x1 */
+	while ((readw(base + PHY_40NM_PLL1)
+		& PHY_40NM_PLL1_PLL_READY) == 0)
+		pr_info("polling usb phy\n");
+
+	/* Program 0xd4207010[13]=0x1 */
+	tmp16 = readw(base + PHY_40NM_TX0);
+	writew(tmp16 | PHY_40NM_TX0_RCAL_START, base + PHY_40NM_TX0);
+
+	/* Wait for 40us */
+	udelay(40);
+
+	/* Program 0xd4207010[13]=0x0 */
+	tmp16 = readw(base + PHY_40NM_TX0);
+	writew(tmp16 & (~PHY_40NM_TX0_RCAL_START), base + PHY_40NM_TX0);
+
+	/* Wait for 400us */
+	udelay(400);
+
+	pr_info("usb phy inited %x!\n", readl(base + PHY_40NM_CTRL));
+
+	return 0;
+}
+
+static void _mv_usb2_phy_40nm_shutdown(struct mv_usb2_phy *mv_phy)
+{
+	void __iomem *base = mv_phy->base;
+	u16 tmp16;
+	u32 tmp32;
+
+	pr_info("usb phy deinit!\n");
+
+	/* Program 0xd4207008[14:13]=0x0 */
+	tmp16 = readw(base + PHY_40NM_PLL1);
+	writew(tmp16
+		& (~PHY_40NM_PLL1_PLL_CONTROL_BY_PIN)
+		& (~PHY_40NM_PLL1_PU_PLL), base + PHY_40NM_PLL1);
+
+	/* Program 0xd4207034[14]=0x0 */
+	tmp16 = readw(base + PHY_40NM_ANA1);
+	writew(tmp16
+		& (~PHY_40NM_ANA1_PU_ANA), base + PHY_40NM_ANA1);
+
+	/* Program 0xD4207104[1] = 0x0 */
+	/* Program 0xD4207104[0] = 0x0 */
+	tmp32 = readl(base + PHY_40NM_CTRL);
+	writel(tmp32
+		& (~PHY_40NM_CTRL_PU_PLL)
+		& (~PHY_40NM_CTRL_PU), base + PHY_40NM_CTRL);
+}
+
+#if defined(CONFIG_CPU_ASR18XX)
+static int asr_usb_host_phy_private(struct usb_phy *phy, u32 option)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	void __iomem *phy_base = mv_phy->base;
+	int count = 0x100000;
+
+	pr_info("asr usb phy host priviate: %d\n", option);
+
+	if (option) {
+		/*
+		* set cfg_en_hstsof, cfg_discon_det_en
+		* clear rb_hshost_disc_raw
+		*/
+		if (cpu_is_asr1803_z1()) {
+			writel(readl(phy_base + PHY_28NMHP_PHY_REG04) | 0x1,
+					phy_base + PHY_28NMHP_PHY_REG04);
+			writel(readl(phy_base + PHY_28NMHP_PHY_REG08) | (0x1 << 9),
+					phy_base + PHY_28NMHP_PHY_REG08);
+		}
+	} else {
+		/*
+		* clar cfg_en_hstsof, cfg_discon_det_en and rb_hshost_disc_raw
+		*/
+		if (cpu_is_asr1803_z1()) {
+			writel(readl(phy_base + PHY_28NMHP_PHY_REG04) & (~0x1),
+					phy_base + PHY_28NMHP_PHY_REG04);
+			writel(readl(phy_base + PHY_28NMHP_PHY_REG08) & (~(0x1 << 9)),
+					phy_base + PHY_28NMHP_PHY_REG08);
+		}
+		writel(readl(phy_base + PHY_28NMHP_PHY_REG10) | 0x1,
+				phy_base + PHY_28NMHP_PHY_REG10);
+		while((readl(phy_base + PHY_28NMHP_PHY_REG10) & 0x1) && (count--));
+		if (unlikely(count == 0))
+			pr_err("phy rd_hshost_disc_raw clear failed\n");
+	}
+
+	return 0;
+
+}
+#endif
+
+#if defined(CONFIG_CPU_ASR18XX)
+static int asr_usb_phy_softrst_private(struct usb_phy *phy, u32 arg)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	void __iomem *phy_base = mv_phy->base;
+	u32 regval;
+	static u32 phy_sts_rcd = 0;
+
+	if (cpu_is_asr1806()) {
+		regval = readl(phy_base + PHY_28NMHP_PHY_REG0E);
+		phy_sts_rcd = phy_sts_rcd << 1;
+		if ((regval & (0x3 << 13)) == (0x3 << 13)) {
+			phy_sts_rcd |= 0x1;
+			pr_err("phy sts: 0x%x, 0x%x\n", regval, phy_sts_rcd);
+			if ((phy_sts_rcd & 0x3) == 0x3)
+				goto do_softrst;
+			else
+				goto out_no_softrst;
+		}
+	} else if (cpu_is_asr1803()) {
+		regval = readl(phy_base + PHY_28NMHP_PHY_REG04);
+		regval &= ~(0xff << 8);
+		regval |= (0x9 << 8);
+		writel(regval, phy_base + PHY_28NMHP_PHY_REG04);
+
+		regval = readl(phy_base + PHY_28NMHP_PHY_REG33);
+		if (regval & (0x1 << 21)) {
+			pr_err("phy reg_cc err: 0x%x\n", regval);
+			goto do_softrst;
+		}
+	}
+	/* no err */
+	return 0;
+
+do_softrst:
+	regval = readl(phy_base + PHY_28NMHP_PHY_REG01);
+	regval &= ~(0x3 << 6);
+	writel(regval, phy_base + PHY_28NMHP_PHY_REG01);
+	udelay(10);
+	regval = readl(phy_base + PHY_28NMHP_PHY_REG01);
+	regval |= (0x3 << 6);
+	writel(regval, phy_base + PHY_28NMHP_PHY_REG01);
+
+out_no_softrst:
+	/* Write 1s to clear phy err status */
+	writel(0xFFFFFFFF, phy_base + PHY_28NMHP_PHY_REG0E);
+
+	return 1;
+}
+#endif
+
+static int _mv_usb2_phy_28nmhp_init(struct mv_usb2_phy *mv_phy)
+{
+	unsigned int loops = 200;
+	void __iomem *base = mv_phy->base;
+	unsigned int val;
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+
+#if defined(CONFIG_CPU_ASR18XX)
+	if (cpu_is_asr1803() || cpu_is_asr1806())
+		mv_phy->phy.phy_private2 = asr_usb_host_phy_private;
+#endif
+
+#if defined(CONFIG_CPU_ASR18XX)
+	if (cpu_is_asr1803() || cpu_is_asr1806())
+		mv_phy->phy.phy_private3 = asr_usb_phy_softrst_private;
+#endif
+
+	/* WAIT FOR PHY PLL RDY */
+	while (((readl(base + PHY_28NMHP_PHY_REG01) & PHY_28NMHP_PLL_BIT_RDY) !=
+			PHY_28NMHP_PLL_BIT_RDY)
+			&& (loops--))
+		udelay(1);
+	if (loops == 0)
+		pr_err("!!!!!!!!!!phy pll not ready after 200us\n");
+
+	if (cpu_is_asr1802s()) {
+		/* pullup dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG28);
+		val &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG28);
+	} else if (cpu_is_asr1803() || cpu_is_asr1806()) {
+		/* pullup dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG29);
+		val &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG29);
+	}
+
+	/* Release usb2 phy internal reset and enable clock gating */
+	writel(0x60ef, base + PHY_28NMHP_PHY_REG01);
+#if 0
+	/* enable phy hw reset for asr1806 */
+	if (cpu_is_asr1806())
+		writel(0xC01C, base + PHY_28NMHP_PHY_REG0D);
+	else
+#endif
+	writel(0x1C, base + PHY_28NMHP_PHY_REG0D);
+	/*
+	* This is 1802s only
+	* 1803 has new reg -> threshold map table
+	* HS RX sequelch threshold: 4 -> 7 for yield rate
+	*/
+	if (cpu_is_asr1802s())	
+		writel(0x7468, base + PHY_28NMHP_PHY_REG23);
+	else if (cpu_is_asr1803() || cpu_is_asr1806()) {
+		/* set to serial mode for stability */
+		writel(readl(base + PHY_28NMHP_PHY_REG06) | (0x1), base + PHY_28NMHP_PHY_REG06);
+
+		/* increase tx dac by 10%, will add back if needed in some special cases */
+		/* writel((readl(base + PHY_28NMHP_PHY_REG29) & (~0x1F)) | (0x1B), base + PHY_28NMHP_PHY_REG29); */
+	}
+
+	val = readl(base + PHY_28NMHP_PHY_REG0A);
+	val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+	writel(val, base + PHY_28NMHP_PHY_REG0A);
+
+	/* Write 1s to clear phy err status */
+	writel(0xFFFFFFFF, base + PHY_28NMHP_PHY_REG0E);
+
+	/* rls controller */
+	if (cpu_is_asr1806()) {
+		writel(readl(apmu_base + APMU_USB_CLK_CTRL) | (0x1 << 2), (apmu_base + APMU_USB_CLK_CTRL));
+		pr_info("asr1806 usbphy done\n");
+	}
+	return 0;
+}
+
+static int _mv_usb2_phy_28nmhp_preinit(struct mv_usb2_phy *mv_phy)
+{
+	unsigned int loops = 400;
+	void __iomem *base = mv_phy->base;
+	unsigned int val;
+
+	pr_info("%s\n", __func__);
+	/* WAIT FOR PHY PLL RDY */
+	while (((readl(base + PHY_28NMHP_PHY_REG01) & PHY_28NMHP_PLL_BIT_RDY) !=
+			PHY_28NMHP_PLL_BIT_RDY)
+			&& (loops--))
+		udelay(1);
+	if (loops == 0)
+		pr_err("!!!!!!!!!!phy pll not ready after 200us\n");
+
+	if (cpu_is_asr1802s()) {
+		/* pullup dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG25);
+		val &= ~PHY_28NMHP_DCP_DET_PULL_MASK;
+		val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN;
+		writel(val, base + PHY_28NMHP_PHY_REG25);
+
+		val = readl(base + PHY_28NMHP_PHY_REG28);
+		val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG28);
+	} else if (cpu_is_asr1803()) {
+		/* pullup dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG25);
+		val &= ~PHY_28NMHP_DCP_DET_PULL_MASK;
+		val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN;
+		writel(val, base + PHY_28NMHP_PHY_REG25);
+
+		val = readl(base + PHY_28NMHP_PHY_REG29);
+		val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG29);
+	}
+	return 0;
+}
+
+static void _mv_usb2_phy_28nmhp_shutdown(struct mv_usb2_phy *mv_phy)
+{
+	void __iomem *base = mv_phy->base;
+	unsigned int val;
+
+#if defined(CONFIG_CPU_ASR18XX)
+	if (cpu_is_asr1803() || cpu_is_asr1806())
+		mv_phy->phy.phy_private2 = NULL;
+#endif
+
+#if defined(CONFIG_CPU_ASR18XX)
+	if (cpu_is_asr1803() || cpu_is_asr1806())
+		mv_phy->phy.phy_private3 = NULL;
+#endif
+
+	val = readl(base + PHY_28NMHP_PHY_REG0B);
+	val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+	writel(val, base + PHY_28NMHP_PHY_REG0B);
+
+	val = readl(base + PHY_28NMHP_PHY_REG0A);
+	val |= (PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+	writel(val, base + PHY_28NMHP_PHY_REG0A);
+
+	if (cpu_is_asr1802s()) {
+		/* pulldown dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG25);
+		val &= ~PHY_28NMHP_DCP_DET_PULL_MASK;
+		val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN;
+		writel(val, base + PHY_28NMHP_PHY_REG25);
+
+		val = readl(base + PHY_28NMHP_PHY_REG28);
+		val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG28);
+	} else if (cpu_is_asr1803()) {
+		/* pulldown dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG25);
+		val &= ~PHY_28NMHP_DCP_DET_PULL_MASK;
+		val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN;
+		writel(val, base + PHY_28NMHP_PHY_REG25);
+		
+		val = readl(base + PHY_28NMHP_PHY_REG29);
+		val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG29);
+	}
+}
+
+static int mv_usb2_phy_suspend(struct usb_phy *phy, int suspend)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	void __iomem *base = mv_phy->base;
+	unsigned int val;
+
+	pr_info("USB2 PHY set_suspend: %d...\n", suspend);
+
+	if (suspend) {
+		val = readl(base + PHY_28NMHP_PHY_REG0B);
+		val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+		writel(val, base + PHY_28NMHP_PHY_REG0B);
+		
+		val = readl(base + PHY_28NMHP_PHY_REG0A);
+		val |= (PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+		writel(val, base + PHY_28NMHP_PHY_REG0A);
+
+		/* pulldown dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG25);
+		val &= ~PHY_28NMHP_DCP_DET_PULL_MASK;
+		val |= PHY_28NMHP_DCP_DET_PULL_DOWN_DOWN;
+		writel(val, base + PHY_28NMHP_PHY_REG25);
+		
+		val = readl(base + PHY_28NMHP_PHY_REG29);
+		val |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG29);
+	} else {
+		val = readl(base + PHY_28NMHP_PHY_REG0A);
+		val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+		writel(val, base + PHY_28NMHP_PHY_REG0A);
+	
+		/* disable pullup dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG29);
+		val &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG29);
+
+		/* Write 1s to clear phy err status */
+		writel(0xFFFFFFFF, base + PHY_28NMHP_PHY_REG0E);
+	}
+
+	return 0;
+}
+
+static int mv_usb2_phy_suspend2(struct usb_phy *phy, int suspend)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	void __iomem *base = mv_phy->base;
+	unsigned int val;
+
+	pr_info("USB2 PHY set_suspend2: %d...\n", suspend);
+
+	if (suspend) {
+		val = readl(base + PHY_28NMHP_PHY_REG0B);
+		val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+		writel(val, base + PHY_28NMHP_PHY_REG0B);
+		
+		val = readl(base + PHY_28NMHP_PHY_REG0A);
+		val |= (PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+		writel(val, base + PHY_28NMHP_PHY_REG0A);
+	} else {
+		val = readl(base + PHY_28NMHP_PHY_REG0A);
+		val &= ~(PHY_28NMHP_SUSPEND_PLL | PHY_28NMHP_SUSPEND_PHY);
+		writel(val, base + PHY_28NMHP_PHY_REG0A);
+	
+		/* disable pullup dp and pulldown dm */
+		val = readl(base + PHY_28NMHP_PHY_REG29);
+		val &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(val, base + PHY_28NMHP_PHY_REG29);
+
+		/* Write 1s to clear phy err status */
+		writel(0xFFFFFFFF, base + PHY_28NMHP_PHY_REG0E);
+	}
+
+	return 0;
+}
+
+static int mv_usb2_phy_dumpcfg(struct usb_phy *phy)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	void __iomem *base = mv_phy->base;
+	u32 i;
+
+	pr_info("usb phy regs\n");
+	for (i = 0x0; i < 0x100; i += 32) {
+		pr_info("0x%08x: %08x %08x %08x %08x %08x %08x %08x %08x\n", i,
+			readl(base + i + 0),
+			readl(base + i + 4),
+			readl(base + i + 8),
+			readl(base + i + 12),
+			readl(base + i + 16),
+			readl(base + i + 20),
+			readl(base + i + 24),
+			readl(base + i + 28));
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_USB_GADGET_CHARGE_ONLY
+void usb_phy_force_dp_dm(struct usb_phy *phy, bool is_force)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	void __iomem *base = mv_phy->base;
+	u16 reg16;
+
+	/* chips that has otg do not need to force dp dm down */
+	if (has_feat_force_dpdm()) {
+		reg16 = readw(base + PHY_40NM_DIG1);
+		if (is_force) {
+			reg16 = (reg16 & (~PHY_40NM_DIG1_ARC_DPDM_MODE))
+				| PHY_40NM_DIG1_DP_PULLDOWN
+				| PHY_40NM_DIG1_DM_PULLDOWN;
+		} else {
+			reg16 = (reg16 | PHY_40NM_DIG1_ARC_DPDM_MODE)
+				& (~PHY_40NM_DIG1_DP_PULLDOWN)
+				& (~PHY_40NM_DIG1_DM_PULLDOWN);
+		}
+
+		writew(reg16, base + PHY_40NM_DIG1);
+		pr_info("dp_dm force? %s, reg=%x\n",
+				is_force ? "yes" : "no", reg16);
+	}
+}
+#endif /* CONFIG_USB_GADGET_CHARGE_ONLY */
+
+static int _mv_usb2_phy_28nm_init(struct mv_usb2_phy *mv_phy)
+{
+	struct platform_device *pdev = mv_phy->pdev;
+	unsigned int loops = 0;
+	void __iomem *base = mv_phy->base;
+	unsigned int tmp, val;
+
+	if (cpu_is_pxa1826()) {
+		/* PHY_28NM_PLL_REG0 */
+		writel(readl(base + PHY_28NM_PLL_REG0) &
+			~(PHY_28NM_PLL_SELLPFR_MASK
+			| PHY_28NM_PLL_FBDIV_MASK
+			| PHY_28NM_PLL_REFDIV_MASK),
+			base + PHY_28NM_PLL_REG0);
+		writel(readl(base + PHY_28NM_PLL_REG0) |
+			(0x1 << PHY_28NM_PLL_SELLPFR_SHIFT
+			| 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT
+			| 0xd << PHY_28NM_PLL_REFDIV_SHIFT),
+			base + PHY_28NM_PLL_REG0);
+	} else {
+		/* PHY_28NM_PLL_REG0 */
+		writel(readl(base + PHY_28NM_PLL_REG0) &
+			~(PHY_28NM_PLL_SELLPFR_MASK
+			| PHY_28NM_PLL_FBDIV_MASK
+			| PHY_28NM_PLL_ICP_MASK
+			| PHY_28NM_PLL_REFDIV_MASK),
+			base + PHY_28NM_PLL_REG0);
+		writel(readl(base + PHY_28NM_PLL_REG0) |
+			(0x1 << PHY_28NM_PLL_SELLPFR_SHIFT
+			| 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT
+			| 0x3 << PHY_28NM_PLL_ICP_SHIFT
+			| 0xd << PHY_28NM_PLL_REFDIV_SHIFT),
+			base + PHY_28NM_PLL_REG0);
+	}
+
+	/* PHY_28NM_PLL_REG1 */
+	writel(readl(base + PHY_28NM_PLL_REG1) &
+		~(PHY_28NM_PLL_PU_PLL_MASK
+		| PHY_28NM_PLL_PU_BY_REG_MASK),
+		base + PHY_28NM_PLL_REG1);
+
+	writel(readl(base + PHY_28NM_PLL_REG1) |
+		(0x1 << PHY_28NM_PLL_PU_PLL_SHIFT
+		| 0x1 << PHY_28NM_PLL_PU_BY_REG_SHIFT),
+		base + PHY_28NM_PLL_REG1);
+
+	if (cpu_is_pxa1826()) {
+		/* set TX AMP to 445mv */
+		if (system_is_prod_mode())
+			writel((((readl(base + PHY_28NM_TX_REG0))
+				& (~PHY_28NM_TX_AMP_MASK))
+				| (0x4 << PHY_28NM_TX_AMP_SHIFT)),
+				base + PHY_28NM_TX_REG0);
+
+		writel(readl(base + PHY_28NM_TX_REG0)
+			| (0x3 << PHY_28NM_TX_PU_ANA_SHIFT),
+			base + PHY_28NM_TX_REG0);
+	} else {
+		/* PHY_28NM_TX_REG0 */
+		writel(readl(base + PHY_28NM_TX_REG0) &
+			~PHY_28NM_TX_AMP_MASK,
+			base + PHY_28NM_TX_REG0);
+		writel(readl(base + PHY_28NM_TX_REG0) |
+			(0x1 << PHY_28NM_TX_PU_BY_REG_SHIFT
+			| 0x3 << PHY_28NM_TX_AMP_SHIFT
+			| 0x1 << PHY_28NM_TX_PU_ANA_SHIFT),
+			base + PHY_28NM_TX_REG0);
+	}
+
+	/* PHY_28NM_RX_REG0 */
+	writel(readl(base + PHY_28NM_RX_REG0) &
+		~PHY_28NM_RX_SQ_THRESH_MASK,
+		base + PHY_28NM_RX_REG0);
+
+	writel(readl(base + PHY_28NM_RX_REG0) |
+		0xa << PHY_28NM_RX_SQ_THRESH_SHIFT,
+		base + PHY_28NM_RX_REG0);
+
+	/* PHY_28NM_DIG_REG0 */
+	writel(readl(base + PHY_28NM_DIG_REG0) &
+		~(PHY_28NM_DIG_BITSTAFFING_ERR_MASK
+		| PHY_28NM_DIG_SYNC_ERR_MASK
+		| PHY_28NM_DIG_SQ_FILT_MASK
+		| PHY_28NM_DIG_SQ_BLK_MASK
+		| PHY_28NM_DIG_SYNC_NUM_MASK),
+		base + PHY_28NM_DIG_REG0);
+
+	if (cpu_is_pxa1928_b0()) {
+		writel(readl(base + PHY_28NM_DIG_REG0) |
+			(0x0 << PHY_28NM_DIG_SQ_FILT_SHIFT
+			| 0x0 << PHY_28NM_DIG_SQ_BLK_SHIFT
+			| 0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT),
+			base + PHY_28NM_DIG_REG0);
+	} else {
+		writel(readl(base + PHY_28NM_DIG_REG0) |
+			(0x1 << PHY_28NM_DIG_SQ_FILT_SHIFT
+			| 0x0 << PHY_28NM_DIG_SQ_BLK_SHIFT
+			| 0x2 << PHY_28NM_DIG_SYNC_NUM_SHIFT),
+			base + PHY_28NM_DIG_REG0);
+	}
+
+	if (mv_phy->drv_data.phy_flag & MV_PHY_FLAG_PLL_LOCK_BYPASS)
+		writel(readl(base + PHY_28NM_DIG_REG0)
+			| (0x1 << PHY_28NM_PLL_LOCK_BYPASS_SHIFT),
+			base + PHY_28NM_DIG_REG0);
+
+	/* PHY_28NM_OTG_REG */
+	writel(readl(base + PHY_28NM_OTG_REG) |
+		0x1 << PHY_28NM_OTG_PU_OTG_SHIFT,
+		base + PHY_28NM_OTG_REG);
+
+	writel(readl(base + PHY_28NM_OTG_REG) &
+		~(0x1 << PHY_28NM_OTG_CONTROL_BY_PIN_SHIFT),
+		base + PHY_28NM_OTG_REG);
+
+	/*  Calibration Timing
+	*		   ____________________________
+	*  CAL START   ___|
+	*			   ____________________
+	*  CAL_DONE    ___________|
+	*		  | 400us |
+	*/
+
+	/* IMP Calibrate */
+	writel(readl(base + PHY_28NM_CAL_REG) |
+		1 << PHY_28NM_IMPCAL_START_SHIFT,
+		base + PHY_28NM_CAL_REG);
+	/* Wait For Calibrate PHY */
+	udelay(400);
+
+	/* Make sure PHY Calibration is ready */
+	loops = 0;
+	do {
+		tmp = readl(base + PHY_28NM_CAL_REG);
+		val = PHY_28NM_PLL_IMPCAL_DONE_MASK;
+		tmp &= val;
+		if (tmp == val)
+			break;
+		udelay(1000);
+	} while ((loops++) && (loops < 100));
+	if (loops >= 100)
+		dev_warn(&pdev->dev, "USB PHY Calibrate not done after 100mS.");
+	writel(readl(base + PHY_28NM_CAL_REG) &
+		~(1 << PHY_28NM_IMPCAL_START_SHIFT),
+		base + PHY_28NM_CAL_REG);
+
+	/* PLL Calibrate */
+	writel(readl(base + PHY_28NM_CAL_REG) |
+		1 << PHY_28NM_PLLCAL_START_SHIFT,
+		base + PHY_28NM_CAL_REG);
+	udelay(400);
+	loops = 0;
+	do {
+		tmp = readl(base + PHY_28NM_CAL_REG);
+		val = PHY_28NM_PLL_PLLCAL_DONE_MASK;
+		tmp &= val;
+		if (tmp == val)
+			break;
+		udelay(1000);
+	} while ((loops++) && (loops < 100));
+	if (loops >= 100)
+		dev_warn(&pdev->dev, "USB PHY Calibrate not done after 100mS.");
+	writel(readl(base + PHY_28NM_CAL_REG) &
+		~(1 << PHY_28NM_PLLCAL_START_SHIFT),
+		base + PHY_28NM_CAL_REG);
+
+	/* SQ Calibrate */
+	writel(readl(base + PHY_28NM_RX_REG1) |
+		1 << PHY_28NM_RX_SQCAL_START_SHIFT,
+		base + PHY_28NM_RX_REG1);
+	/* Wait For Calibrate PHY */
+	udelay(400);
+	/* Make sure PHY Calibration is ready */
+	loops = 0;
+	do {
+		tmp = readl(base + PHY_28NM_RX_REG1);
+		val = PHY_28NM_RX_SQCAL_DONE_MASK;
+		tmp &= val;
+		if (tmp == val)
+			break;
+
+		udelay(1000);
+	} while ((loops++) && (loops < 100));
+	if (loops >= 100)
+		dev_warn(&pdev->dev, "USB PHY Calibrate not done after 100mS.");
+	writel(readl(base + PHY_28NM_RX_REG1) &
+		~(1 << PHY_28NM_RX_SQCAL_START_SHIFT),
+		base + PHY_28NM_RX_REG1);
+
+
+	/* Make sure PHY PLL is ready */
+	loops = 0;
+	tmp = readl(base + PHY_28NM_PLL_REG0);
+	while (!(tmp & PHY_28NM_PLL_READY_MASK)) {
+		udelay(1000);
+		loops++;
+		if (loops > 200) {
+			dev_warn(&pdev->dev, "PLL_READY not set after 200mS.");
+			break;
+		}
+	}
+
+	if (cpu_is_pxa1826()) {
+		val = readl(base + PHY_28NM_CTRL_REG0);
+		writel(val | (3 << PHY_28NM_VBUSVALID_CTRL_SHIFT),
+			base + PHY_28NM_CTRL_REG0);
+	} else if (cpu_is_pxa1928()) {
+		writel(readl(base + PHY_28NM_CTRL_REG3) |
+			(0x1 << PHY_28NM_CTRL3_OVERWRITE_SHIFT
+			| 0x1 << PHY_28NM_CTRL3_VBUS_VALID_SHIFT
+			| 0x1 << PHY_28NM_CTRL3_AVALID_SHIFT
+			| 0x1 << PHY_28NM_CTRL3_BVALID_SHIFT),
+			base + PHY_28NM_CTRL_REG3);
+	}
+
+	return 0;
+}
+
+static void _mv_usb2_phy_28nm_shutdown(struct mv_usb2_phy *mv_phy)
+{
+	void __iomem *base = mv_phy->base;
+	#if defined(CONFIG_CPU_ASR18XX)
+	struct mv_otg_regs *otg_regs = (struct mv_otg_regs *)mv_phy->phy.io_op_regs;
+	#endif
+	unsigned int val;
+	u32 tmp;
+
+	if (cpu_is_pxa1826()) {
+		val = readl(base + PHY_28NM_CTRL_REG0);
+		writel(val & (~(3 << PHY_28NM_VBUSVALID_CTRL_SHIFT)),
+			base + PHY_28NM_CTRL_REG0);
+	} else if (cpu_is_pxa1928()) {
+		writel(readl(base + PHY_28NM_CTRL_REG3) &
+			~(0x1 << PHY_28NM_CTRL3_OVERWRITE_SHIFT
+			| 0x1 << PHY_28NM_CTRL3_VBUS_VALID_SHIFT
+			| 0x1 << PHY_28NM_CTRL3_AVALID_SHIFT
+			| 0x1 << PHY_28NM_CTRL3_BVALID_SHIFT),
+			base + PHY_28NM_CTRL_REG3);
+	}
+
+	/*need clear suspendm for power leakage if we use 28nm phy*/
+	if (cpu_is_pxa1826()) {
+		tmp = readl(&otg_regs->portsc[0]);
+		tmp |= PORTSCX_PHY_LOW_POWER_SPD;
+		writel(tmp, &otg_regs->portsc[0]);
+	}
+
+	val = readw(base + PHY_28NM_PLL_REG1);
+	val &= ~PHY_28NM_PLL_PU_PLL_MASK;
+	writew(val, base + PHY_28NM_PLL_REG1);
+
+	/* power down PHY Analog part */
+	val = readw(base + PHY_28NM_TX_REG0);
+	val &= ~(0x1 << PHY_28NM_TX_PU_ANA_SHIFT);
+	writew(val, base + PHY_28NM_TX_REG0);
+
+	/* power down PHY OTG part */
+	val = readw(base + PHY_28NM_OTG_REG);
+	val &= ~(0x1 << PHY_28NM_OTG_PU_OTG_SHIFT);
+	writew(val, base + PHY_28NM_OTG_REG);
+
+}
+
+#ifdef CONFIG_USB_MV_HSIC_UDC
+#if defined(CONFIG_CPU_ASR18XX)
+#define HSIC_UDC_PORTSC_OFFSET	(0x184)
+static int mv_hsic_phy_private(struct usb_phy *phy)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	u32 hsic_int, hsic_ctrl;
+	u32 status;
+	int count, ret = 0;
+	void __iomem *phy_base = mv_phy->base;
+	void __iomem *udc_reg_base = phy->io_op_regs;
+
+	count = 10000;
+	do {
+		hsic_int = __raw_readl(phy_base + PHY_28NM_HSIC_INT);
+		count--;
+		udelay(1);
+	} while ((count >= 0)
+		&& !(hsic_int & (PHY_28NM_HSIC_CONNECT_INT_BIT)));
+
+	if (count <= 0) {
+		pr_err("HSIC HS_CONNETC NOT set: hsic_int 0x%x\n",
+				hsic_int);
+
+		hsic_ctrl = __raw_readl(phy_base + PHY_28NM_HSIC_CTRL);
+		pr_info("hsic_ctrl: 0x%x\n", hsic_ctrl);
+		if (!((0x1 << 31) & hsic_ctrl)) {
+			ret = -EPROTO;
+			goto out;
+		}
+	} else {
+		pr_info("HSIC HS_CONNECT set\n");
+	}
+
+	__raw_writel(__raw_readl(phy_base + PHY_28NM_HSIC_INT) | PHY_28NM_HSIC_CONNECT_INT_BIT,
+			phy_base + PHY_28NM_HSIC_INT);
+
+	/* test mode: force enable hs */
+	status = __raw_readl((void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET));
+	status &= ~(0xf << 16 | 0x1 << 24);
+	status |= (0x5 << 16);
+	__raw_writel(status, (void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET));
+	status = __raw_readl((void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET));
+
+	/* disable test mode */
+	status = __raw_readl((void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET));
+	status &= ~(0xf << 16 | 0x1 << 24);
+	__raw_writel(status, (void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET));
+
+	/* check HS ready */
+	count = 20000;
+	do {
+		hsic_int = __raw_readl(phy_base + PHY_28NM_HSIC_INT);
+		count--;
+		udelay(1);
+	} while ((count >= 0)
+		&& !(hsic_int & (PHY_28NM_HSIC_INT_HS_READY)));
+
+	if (count <= 0) {
+		pr_err("HSIC HS_READY not set: hsic_int 0x%x\n",
+				hsic_int);
+		ret = -EPROTO;
+	} else {
+		pr_info("HSIC HS_READY set\n");
+	}
+
+out:
+	pr_info("%s: hsic_int: 0x%x, portsc: 0x%x\n", __func__, hsic_int,
+			__raw_readl((void __iomem *)((u32)udc_reg_base + HSIC_UDC_PORTSC_OFFSET)));
+
+	if ((hsic_int & (PHY_28NM_HSIC_INT_HS_READY))
+		|| (hsic_int & (PHY_28NM_HSIC_CONNECT_INT_BIT)))
+		__raw_writel(__raw_readl(phy_base + PHY_28NM_HSIC_INT) |
+				(PHY_28NM_HSIC_INT_HS_READY| PHY_28NM_HSIC_CONNECT_INT_BIT),
+				phy_base + PHY_28NM_HSIC_INT);
+
+	return ret;
+}
+#endif
+
+/* HSIC device mode phy init code */
+static int _mv_usb2_phy_hsic_init(struct mv_usb2_phy *mv_phy)
+{
+	struct platform_device *pdev = mv_phy->pdev;
+	void __iomem *base = mv_phy->base;
+	int loops;
+
+	dev_info(&pdev->dev, "hsic_dev_phy_init !!!\n");
+#if defined(CONFIG_CPU_ASR18XX)
+	mv_phy->phy.need_forceHS = false;
+	mv_phy->phy.is_hsic = true;
+	mv_phy->phy.phy_private = mv_hsic_phy_private;
+	mv_phy->phy.phy_reg_base = base;
+#endif
+
+	/* SET DPPULLDOWN_CTRL TO 0b'01 */
+	writel(((readl(base + PHY_28NM_HSIC_USB_CTRL) &
+		~(PHY_28NM_HSIC_DPPULLDOWN_CTRL_MASK))
+		| PHY_28NM_HSIC_DPPULLDOWN_CTRL_DEVICE),
+		base + PHY_28NM_HSIC_USB_CTRL);
+
+	/* Set reference clock */
+	writel(readl(base + PHY_28NM_HSIC_PLL_CTRL01) &
+		~(0x3 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT
+		| 0x1ff << PHY_28NM_HSIC_PLL_FBDIV_SHIFT
+		| 0x7f << PHY_28NM_HSIC_PLL_REFDIV_SHIFT),
+		base + PHY_28NM_HSIC_PLL_CTRL01);
+
+	writel(readl(base + PHY_28NM_HSIC_PLL_CTRL01) |
+		(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT
+		| 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT
+		| 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT),
+		base + PHY_28NM_HSIC_PLL_CTRL01);
+
+	/* Turn on PLL */
+	writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
+		0x1 << PHY_28NM_HSIC_S2H_PU_PLL_SHIFT,
+		base + PHY_28NM_HSIC_PLL_CTRL2);
+	if (cpu_is_pxa1826()) {
+		writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) &
+			~((0xf<<PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT) |
+			(0x1<<PHY_28NM_HSIC_CTL0_S2H_DRV_SE0_4RESUME)),
+			base + PHY_28NM_HSIC_PLL_CTRL2);
+		writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
+			0x8 << PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT,
+			base + PHY_28NM_HSIC_PLL_CTRL2);
+	}
+
+	/* Make sure PHY PLL is locked */
+	udelay(400);
+
+	loops = 0;
+	while (!(readl(base + PHY_28NM_HSIC_PLL_CTRL2)
+			& (0x1 << PHY_28NM_HSIC_H2S_PLL_LOCK_SHIFT))) {
+		mdelay(1);
+		loops++;
+		if (loops > 100) {
+			dev_warn(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
+			break;
+		}
+	}
+
+	/* Enable HSIC PHY */
+	writel(((readl(base + PHY_28NM_HSIC_CTRL) |
+		(0x1 << PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT)) &
+		~(0x1 << PHY_28NM_HSIC_S2H_HSIC_CONN_BYP_SHIFT)),
+		base + PHY_28NM_HSIC_CTRL);
+
+	/*  Calibration Timing for EDEN
+	*		   ____________________________
+	*  CAL START   ___|
+	*			   ____________________
+	*  CAL_DONE    ___________|
+	*		  | 400us |
+	*/
+	/* Wait For Calibrate PHY */
+	udelay(400);
+
+	/* Make sure PHY Calibration is ready */
+	loops = 0;
+	while (!(readl(base + PHY_28NM_HSIC_IMPCAL_CAL)
+			& (0x1 << PHY_28NM_HSIC_H2S_IMPCAL_DONE_SHIFT))) {
+		mdelay(1);
+		loops++;
+		if (loops > 100) {
+			dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
+			break;
+		}
+	}
+
+	return 0;
+}
+#else
+#if defined(CONFIG_CPU_ASR18XX)
+static int host_hsic_connected, hsic_init_connected, port_reset_done;
+static int mv_hsic_phy_private(struct usb_phy *phy)
+{
+#ifdef CONFIG_USB_EHCI_MV_U2H_HSIC
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	u32 hsic_int;
+	u32 status;
+	int count;
+	void __iomem *phy_base = mv_phy->base;
+	struct ehci_regs *ehci_regs = (struct ehci_regs *)phy->io_op_regs;
+
+	hsic_int = __raw_readl(phy_base + 0x28);
+
+	if (!host_hsic_connected) {
+		count = 10000;
+		do {
+			hsic_int = __raw_readl(phy_base + PHY_28NM_HSIC_INT);
+			count--;
+			udelay(1);
+		} while ((count >= 0)
+			&& !(hsic_int & (PHY_28NM_HSIC_CONNECT_INT_BIT)));
+
+		if (count <= 0) {
+			pr_err("HSIC HS_CONNECT NOT SET: hsic_int 0x%x\n",
+					hsic_int);
+			goto out;
+		} else {
+			host_hsic_connected = 1;
+			pr_info("HSIC HS_CONNECT set\n");
+		}
+
+		__raw_writel(__raw_readl(phy_base + PHY_28NM_HSIC_INT) |
+									PHY_28NM_HSIC_CONNECT_INT_BIT,
+				phy_base + PHY_28NM_HSIC_INT);
+	}
+
+	if (phy->need_forceHS == true && host_hsic_connected) {
+		phy->need_forceHS = false;
+
+		/* test mode: force enable hs */
+		status = __raw_readl(&ehci_regs->port_status[0]);
+		if ((status & (0x3 << 26)) == (0x2 << 26)) {
+			pr_info("%s: already in hs mode\n", __func__);
+			goto port_reset;
+		}
+
+		status &= ~(0xf << 16 | 0x1 << 24);
+		status |= (0x5 << 16);
+		__raw_writel(status, &ehci_regs->port_status[0]);
+		status = __raw_readl(&ehci_regs->port_status[0]);
+
+		/* disable test mode */
+		status = __raw_readl(&ehci_regs->port_status[0]);
+		status &= ~(0xf << 16 | 0x1 << 24);
+		__raw_writel(status, &ehci_regs->port_status[0]);
+
+		/* check HS ready */
+		count = 10000;
+		do {
+			hsic_int = __raw_readl(phy_base + PHY_28NM_HSIC_INT);
+			count--;
+			udelay(1);
+		} while ((count >= 0)
+			&& !(hsic_int & (PHY_28NM_HSIC_INT_HS_READY)));
+
+		if (count <= 0) {
+			pr_err("HS_READY not set: hsic_int 0x%x, portsc: 0x%x\n",
+					hsic_int, __raw_readl(&ehci_regs->port_status[0]));
+		}
+
+		status = __raw_readl(&ehci_regs->port_status[0]);
+		status |= (0x1 << 1);
+		__raw_writel(status, &ehci_regs->port_status[0]);
+
+port_reset:
+		if (!port_reset_done && !hsic_init_connected) {
+			pr_info("%s: do port reset\n", __func__);
+			/* issue port reset */
+			status = __raw_readl(&ehci_regs->port_status[0]);
+			status |= (1 << 8);
+			status &= ~(1 << 2);
+			__raw_writel(status, &ehci_regs->port_status[0]);
+
+			/* check reset done */
+			count = 0x10000;
+			do {
+				status = __raw_readl(&ehci_regs->port_status[0]);
+				count--;
+			} while ((count >= 0) && !(status & PORT_RESET));
+			if (count <= 0) {
+				pr_err("HSIC port reset not done: port_status 0x%x\n", status);
+				goto out;
+			}
+			port_reset_done = 1;
+		}
+	}
+
+out:
+	if ((hsic_int & (PHY_28NM_HSIC_INT_HS_READY)) || (hsic_int & (PHY_28NM_HSIC_CONNECT_INT_BIT)))
+		__raw_writel(__raw_readl(phy_base + PHY_28NM_HSIC_INT) |
+					(PHY_28NM_HSIC_INT_HS_READY | PHY_28NM_HSIC_CONNECT_INT_BIT),
+			phy_base + PHY_28NM_HSIC_INT);
+#endif
+	return 0;
+
+}
+#endif
+
+static int _mv_usb2_phy_hsic_init(struct mv_usb2_phy *mv_phy)
+{
+	struct platform_device *pdev = mv_phy->pdev;
+	void __iomem *base = mv_phy->base;
+	int loops;
+
+#if defined(CONFIG_CPU_ASR18XX)
+	mv_phy->phy.need_forceHS = false;
+	mv_phy->phy.is_hsic = true;
+	mv_phy->phy.phy_private = mv_hsic_phy_private;
+#endif
+
+	dev_info(&pdev->dev, "hsic_host_phy_init !!!\n");
+
+	/* Set reference clock */
+	writel(readl(base + PHY_28NM_HSIC_PLL_CTRL01) &
+		~(0x3 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT
+		| 0x1ff << PHY_28NM_HSIC_PLL_FBDIV_SHIFT
+		| 0x7f << PHY_28NM_HSIC_PLL_REFDIV_SHIFT),
+		base + PHY_28NM_HSIC_PLL_CTRL01);
+
+	writel(readl(base + PHY_28NM_HSIC_PLL_CTRL01) |
+		(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT
+		| 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT
+		| 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT),
+		base + PHY_28NM_HSIC_PLL_CTRL01);
+
+	/* Turn on PLL */
+	writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
+		0x1 << PHY_28NM_HSIC_S2H_PU_PLL_SHIFT,
+		base + PHY_28NM_HSIC_PLL_CTRL2);
+	if (cpu_is_pxa1826()) {
+		writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) &
+			~(0xf<<PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT),
+			base + PHY_28NM_HSIC_PLL_CTRL2);
+		writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
+			0x8 << PHY_28NM_HSIC_PLL_CTRL1_ICP_SHIFT,
+			base + PHY_28NM_HSIC_PLL_CTRL2);
+	}
+
+	/* Make sure PHY PLL is locked */
+	udelay(400);
+
+	loops = 0;
+	while (!(readl(base + PHY_28NM_HSIC_PLL_CTRL2) &
+				(0x1 << PHY_28NM_HSIC_H2S_PLL_LOCK_SHIFT))) {
+		mdelay(1);
+		loops++;
+		if (loops > 100) {
+			dev_warn(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
+			break;
+		}
+	}
+	/* Enable HSIC PHY */
+	if (cpu_is_pxa1826()) {
+			writel(readl(base + PHY_28NM_HSIC_CTRL) |
+		(0x1 << PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT |
+		0x1<<PHY_28NM_HSIC_CTL0_S2H_DRV_SE0_4RESUME),
+		base + PHY_28NM_HSIC_CTRL);
+	} else {
+		writel(readl(base + PHY_28NM_HSIC_CTRL) |
+		0x1 << PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT,
+		base + PHY_28NM_HSIC_CTRL);
+	}
+
+	/*  Calibration Timing for EDEN
+	*		   ____________________________
+	*  CAL START   ___|
+	*			   ____________________
+	*  CAL_DONE    ___________|
+	*		  | 400us |
+	*/
+	/* Wait For Calibrate PHY */
+	udelay(400);
+
+	/* Make sure PHY Calibration is ready */
+	loops = 0;
+	while (!(readl(base + PHY_28NM_HSIC_IMPCAL_CAL) &
+				(0x1 << PHY_28NM_HSIC_H2S_IMPCAL_DONE_SHIFT))) {
+		mdelay(1);
+		loops++;
+		if (loops > 100) {
+			dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
+			break;
+		}
+	}
+	if (cpu_is_pxa1826()) {
+		writel(readl(base + PHY_28NM_HSIC_CTRL) |
+			0x1 << PHY_28NM_HSIC_CTL0_S2H_BUS_RESET_EN,
+			base + PHY_28NM_HSIC_CTRL);
+	}
+
+	/* Waiting for HSIC connect int*/
+	loops = 0;
+	host_hsic_connected = 1;
+	hsic_init_connected = 1;
+	port_reset_done = 1;
+	while (!(readl(base + PHY_28NM_HSIC_INT) &
+					PHY_28NM_HSIC_CONNECT_INT_BIT)) {
+		mdelay(1);
+		loops++;
+		if (loops > 100) {
+			dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout.");
+			host_hsic_connected = 0;
+			hsic_init_connected = 0;
+			port_reset_done = 0;
+			break;
+		}
+	}
+
+	return 0;
+}
+#endif
+static void _mv_usb2_phy_hsic_shutdown(struct mv_usb2_phy *mv_phy)
+{
+	struct platform_device *pdev = mv_phy->pdev;
+	void __iomem *base = mv_phy->base;
+
+	dev_info(&pdev->dev, "hsic_phy_shutdown !!!\n");
+#if defined(CONFIG_CPU_ASR18XX)
+	mv_phy->phy.need_forceHS = false;
+	mv_phy->phy.is_hsic = false;
+	mv_phy->phy.phy_private = NULL;
+
+#ifndef CONFIG_USB_MV_HSIC_UDC
+	host_hsic_connected = 0;
+	hsic_init_connected = 0;
+	port_reset_done = 0;
+#endif
+
+#endif
+	writel(readl(base + PHY_28NM_HSIC_CTRL) &
+		~(0x1 << PHY_28NM_HSIC_S2H_HSIC_ENABLE_SHIFT),
+		base + PHY_28NM_HSIC_CTRL);
+}
+
+static int mv_usb2_phy_init(struct usb_phy *phy)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	int ret = 0;
+
+	clk_enable(mv_phy->clk);
+	switch (mv_phy->drv_data.phy_type) {
+	case PHY_40LP:
+		ret = _mv_usb2_phy_40nm_init(mv_phy);
+		break;
+	case PHY_28LP:
+		ret = _mv_usb2_phy_28nm_init(mv_phy);
+		break;
+	case PHY_55LP:
+		ret = _mv_usb2_phy_55nm_init(mv_phy);
+		break;
+	case PHY_28LP_HSIC:
+		ret = _mv_usb2_phy_hsic_init(mv_phy);
+		break;
+	case PHY_28HP:
+		ret = _mv_usb2_phy_28nmhp_init(mv_phy);
+		break;
+
+	default:
+		pr_err("No such phy type supported!\n");
+		break;
+	}
+
+	return ret;
+}
+
+static void mv_usb2_phy_shutdown(struct usb_phy *phy)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+
+	switch (mv_phy->drv_data.phy_type) {
+	case PHY_40LP:
+		_mv_usb2_phy_40nm_shutdown(mv_phy);
+		break;
+	case PHY_28LP:
+		_mv_usb2_phy_28nm_shutdown(mv_phy);
+		break;
+	case PHY_55LP:
+		_mv_usb2_phy_55nm_shutdown(mv_phy);
+		break;
+	case PHY_28LP_HSIC:
+		_mv_usb2_phy_hsic_shutdown(mv_phy);
+		break;
+	case PHY_28HP:
+		_mv_usb2_phy_28nmhp_shutdown(mv_phy);
+		break;
+
+	default:
+		pr_err("No such phy type supported!\n");
+		break;
+	}
+
+	clk_disable(mv_phy->clk);
+}
+
+/*
+ * can only detect DEFAULT_CHARGER, DCP_CHARGER, SDP_CHARGER
+ * for other charger types, need to be confirmed by USB enumuration
+ */
+static int _mv_usb2_phy_40nm_charger_detect(struct mv_usb2_phy *mv_phy)
+{
+	void __iomem *base = mv_phy->base;
+	int charger_type_bc12 = NULL_CHARGER;
+	u16 reg16;
+
+	/* debounce time for slowly insering USB cable */
+	msleep(80);
+
+	/*set PU_ANA*/
+	reg16 = readw(base + PHY_40NM_ANA1);
+	reg16 |= PHY_40NM_ANA1_PU_ANA;
+	writew(reg16, base + PHY_40NM_ANA1);
+
+	/* power up the charger detector */
+	reg16 = readw(base + PHY_40NM_CHARGE0);
+	reg16 |= PHY_40NM_CHARGE0_ENABLE_SWITCH |
+		PHY_40NM_CHARGE0_PU_CHRG_DTC;
+	writew(reg16, base + PHY_40NM_CHARGE0);
+	usleep_range(2, 5);
+
+	/*
+	 * primary detect:
+	 * to detect if there is a charger at upstream port
+	 */
+	reg16 = readw(base + PHY_40NM_RESERVE0);
+	reg16 &= ~(PHY_40NM_CDP_EN | PHY_40NM_DCP_EN |
+			PHY_40NM_CDP_DM_AUTO_SWITCH |
+			PHY_40NM_DP_DM_SWAP_CTRL);
+	writew(reg16, base + PHY_40NM_RESERVE0);
+
+	reg16 = readw(base + PHY_40NM_RESERVE0);
+	reg16 |= (PHY_40NM_PD_EN | PHY_40NM_ENABLE_SWITCH_DM |
+			PHY_40NM_ENABLE_SWITCH_DP |
+			PHY_40NM_VDAT_CHARGE |
+			PHY_40NM_VSRC_CHARGE);
+	writew(reg16, base + PHY_40NM_RESERVE0);
+	/* wait 20 us */
+	usleep_range(20, 30);
+
+	/* read charger detector output register */
+	if (readl(base + PHY_40NM_CTRL) & PHY_40NM_CTRL_AVALID_CONTROL)
+		reg16 = readw(base + PHY_40NM_STATUS_A0C);
+	else
+		reg16 = readw(base + PHY_40NM_STATUS);
+	if (reg16 & PHY_40NM_CHARGER_STATUS_MASK) {
+		/*
+		 * secondary detect:
+		 * detect if the upstream port is a dedicated
+		 * charger or a USB2 host port
+		 */
+		reg16 = readw(base + PHY_40NM_RESERVE0);
+		reg16 |= PHY_40NM_DP_DM_SWAP_CTRL;
+		writew(reg16, base + PHY_40NM_RESERVE0);
+		/* wait 20us */
+		usleep_range(20, 30);
+
+		/* read charger detector output register */
+		if (readl(base + PHY_40NM_CTRL)
+		   & PHY_40NM_CTRL_AVALID_CONTROL)
+			reg16 = readw(base +
+					PHY_40NM_STATUS_A0C);
+		else
+			reg16 = readw(base +
+					PHY_40NM_STATUS);
+		if (reg16 & PHY_40NM_CHARGER_STATUS_MASK)
+			charger_type_bc12 = DCP_CHARGER;
+		else
+			charger_type_bc12 = CDP_CHARGER;
+	} else
+		charger_type_bc12 = DEFAULT_CHARGER;
+
+	/*disable switch, shutdown the charger detector*/
+	reg16 = readw(base + PHY_40NM_CHARGE0);
+	reg16 &= ~(PHY_40NM_CHARGE0_ENABLE_SWITCH |
+			PHY_40NM_CHARGE0_PU_CHRG_DTC);
+	writew(reg16, base + PHY_40NM_CHARGE0);
+	return charger_type_bc12;
+}
+
+static int _mv_usb2_phy_28nm_charger_detect(struct mv_usb2_phy *mv_phy)
+{
+	void __iomem *base = mv_phy->base;
+	int charger_type_bc12 = NULL_CHARGER;
+	u32 reg32;
+
+	/* Power up Analog */
+	reg32 = readl(base + PHY_28NM_TX_REG0);
+	reg32 |= (1 << PHY_28NM_TX_PU_BY_REG_SHIFT |
+			1 << PHY_28NM_TX_PU_ANA_SHIFT);
+	writel(reg32, base + PHY_28NM_TX_REG0);
+
+	/* Power up Charger Detector */
+	reg32 = readl(base + PHY_28NM_CHRG_DET);
+	reg32 |= (1 << PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28);
+	writel(reg32, base + PHY_28NM_CHRG_DET);
+
+	usleep_range(2, 5);
+
+	/* Primary detection */
+	reg32 = readl(base + PHY_28NM_CHRG_DET);
+	reg32 &= ~(1  <<  PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 |
+			3 << PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 |
+			3 << PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 |
+			1 << PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 |
+			1 << PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 |
+			1 << PHY_28NM_CHGDTC_CDP_EN_SHIFT_28);
+	writel(reg32, base + PHY_28NM_CHRG_DET);
+
+	reg32 = readl(base + PHY_28NM_CHRG_DET);
+	reg32 |= (1 << PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 |
+			1 << PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 |
+			1 << PHY_28NM_CHGDTC_PD_EN_SHIFT_28);
+	writel(reg32, base + PHY_28NM_CHRG_DET);
+
+	/* Enable swtich DM/DP */
+	reg32 = readl(base + PHY_28NM_CHRG_DET);
+	reg32 |= (1 << PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 |
+			1 << PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28);
+	writel(reg32, base + PHY_28NM_CHRG_DET);
+
+	if ((readl(base + PHY_28NM_CTRL_REG1)) &
+		(1 << PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28)) {
+		/* We have CHRG_DTC_OUT set.
+		 * Now proceed with Secondary Detection
+		 */
+
+		msleep(60);
+
+		reg32 = readl(base + PHY_28NM_CHRG_DET);
+		reg32 |= (1 << PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28);
+		writel(reg32, base + PHY_28NM_CHRG_DET);
+
+		msleep(80);
+
+		if ((readl(base + PHY_28NM_CTRL_REG1)) &
+			(1 << PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28))
+			charger_type_bc12 = DCP_CHARGER;
+		else
+			charger_type_bc12 = CDP_CHARGER;
+	} else
+		charger_type_bc12 = DEFAULT_CHARGER;
+
+	/* Disable swtich DM/DP */
+	reg32 = readl(base + PHY_28NM_CHRG_DET);
+	reg32 &= ~(1<<PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 |
+			1 << PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28);
+	writel(reg32, base + PHY_28NM_CHRG_DET);
+
+	/* Power down Charger Detector */
+	reg32 = readl(base + PHY_28NM_CHRG_DET);
+	reg32 &= ~(1 << PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28);
+	writel(reg32, base + PHY_28NM_CHRG_DET);
+
+	return charger_type_bc12;
+}
+
+static int _mv_usb2_phy_28nmhp_charger_detect(struct mv_usb2_phy *mv_phy)
+{
+	void __iomem *base = mv_phy->base;
+	int charger_type_bc12 = NULL_CHARGER;
+	u32 reg32;
+	struct mv_otg_regs *otg_regs = (struct mv_otg_regs *)mv_phy->phy.io_op_regs;
+
+	if ((otg_regs->usbcmd & 0x1) != 0x1)
+		return DEFAULT_CHARGER;
+
+	reg32 = readl(base + PHY_28NMHP_PHY_REG25);
+	reg32 &= ~(PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_MASK);
+	reg32 |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_VAL;
+	writel(reg32, base + PHY_28NMHP_PHY_REG25);
+
+	if (cpu_is_asr1802s()) {
+		/* pullup dp and pulldown dm */
+		reg32 = readl(base + PHY_28NMHP_PHY_REG25);
+		reg32 &= ~PHY_28NMHP_DCP_DET_PULL_MASK;
+		reg32 |= PHY_28NMHP_DCP_DET_PULL_UP_DOWN;
+		writel(reg32, base + PHY_28NMHP_PHY_REG25);
+
+		reg32 = readl(base + PHY_28NMHP_PHY_REG28);
+		reg32 |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(reg32, base + PHY_28NMHP_PHY_REG28);
+	} else {
+		/* pullup dp and pulldown dm */
+		reg32 = readl(base + PHY_28NMHP_PHY_REG25);
+		reg32 &= ~PHY_28NMHP_DCP_DET_PULL_MASK;
+		reg32 |= PHY_28NMHP_DCP_DET_PULL_UP_DOWN;
+		writel(reg32, base + PHY_28NMHP_PHY_REG25);
+
+		reg32 = readl(base + PHY_28NMHP_PHY_REG29);
+		reg32 |= PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(reg32, base + PHY_28NMHP_PHY_REG29);
+	}
+
+	udelay(5);
+	if ((readl(&otg_regs->portsc[0]) & (0x3 << 10)) == (0x2 << 10))
+		charger_type_bc12 = NONE_STANDARD_CHARGER;
+	else
+		charger_type_bc12 = DCP_CHARGER;
+	if (cpu_is_asr1802s()) {
+		reg32 = readl(base + PHY_28NMHP_PHY_REG28);
+		reg32 &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(reg32, base + PHY_28NMHP_PHY_REG28);
+	} else {
+		reg32 = readl(base + PHY_28NMHP_PHY_REG29);
+		reg32 &= ~PHY_28NMHP_DRV_PULLDOWN_OVERRIDE_EN;
+		writel(reg32, base + PHY_28NMHP_PHY_REG29);
+	}
+
+	return charger_type_bc12;
+}
+
+static int mv_usb2_phy_charger_detect(struct usb_phy *phy)
+{
+	struct mv_usb2_phy *mv_phy = container_of(phy, struct mv_usb2_phy, phy);
+	int ret;
+
+	switch (mv_phy->drv_data.phy_type) {
+	case PHY_40LP:
+		ret = _mv_usb2_phy_40nm_charger_detect(mv_phy);
+		break;
+	case PHY_28LP:
+		ret = _mv_usb2_phy_28nm_charger_detect(mv_phy);
+		break;
+	case PHY_28HP:
+		ret = _mv_usb2_phy_28nmhp_charger_detect(mv_phy);
+		break;
+
+	default:
+		pr_err("Such phy does not support to detect charger type!\n");
+		ret = DEFAULT_CHARGER;
+		break;
+	}
+
+	return ret;
+}
+#if defined(CONFIG_CPU_ASR18XX)
+#define APMU_SD_ROT_WAKE_CLR 0x7C
+#define USB_VBUS_STS  (1<<15)
+static int mv_usb2_phy_get_vbus(struct usb_phy *phy)
+{
+
+	int ret;
+	u32 reg32;
+	void __iomem *apmu_base = regs_addr_get_va(REGS_ADDR_APMU);
+	reg32 = __raw_readl(apmu_base + APMU_SD_ROT_WAKE_CLR);
+	if (reg32&USB_VBUS_STS)
+		ret = 1;
+	else
+		ret = 0;
+
+	return ret;
+
+}
+#endif
+static const struct of_device_id mv_usbphy_dt_match[];
+
+static int mv_usb2_get_phydata(struct platform_device *pdev,
+				struct mv_usb2_phy *mv_phy)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match;
+	u32 phy_rev;
+
+	match = of_match_device(mv_usbphy_dt_match, &pdev->dev);
+	if (!match)
+		return -ENODEV;
+
+	mv_phy->drv_data.phy_type = (unsigned long)match->data;
+
+	if (!of_property_read_u32(np, "marvell,usb2-phy-rev", &phy_rev))
+		mv_phy->drv_data.phy_rev = phy_rev;
+	else
+		pr_info("No PHY revision found, use the default setting!");
+
+	if (of_property_read_bool(np, "marvell,pll-lock-bypass"))
+		mv_phy->drv_data.phy_flag |= MV_PHY_FLAG_PLL_LOCK_BYPASS;
+
+	return 0;
+}
+
+static void mv_usb2_phy_bind_device(struct mv_usb2_phy *mv_phy)
+{
+	const char *device_name;
+
+	struct device_node *np = (mv_phy->phy.dev)->of_node;
+
+	if (!of_property_read_string(np, "marvell,udc-name", &device_name))
+		usb_bind_phy(device_name, MV_USB2_PHY_INDEX,
+						dev_name(mv_phy->phy.dev));
+
+	if (!of_property_read_string(np, "marvell,ehci-name", &device_name))
+		usb_bind_phy(device_name, MV_USB2_PHY_INDEX,
+						dev_name(mv_phy->phy.dev));
+
+	if (!of_property_read_string(np, "marvell,otg-name", &device_name))
+		usb_bind_phy(device_name, MV_USB2_PHY_INDEX,
+						dev_name(mv_phy->phy.dev));
+}
+
+static int mv_usb2_phy_probe(struct platform_device *pdev)
+{
+	struct mv_usb2_phy *mv_phy;
+	struct resource *r;
+	int ret = 0;
+
+	mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL);
+	if (mv_phy == NULL) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	mv_phy->pdev = pdev;
+
+	ret = mv_usb2_get_phydata(pdev, mv_phy);
+	if (ret) {
+		dev_err(&pdev->dev, "No matching phy founded\n");
+		return ret;
+	}
+
+	mv_phy->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(mv_phy->clk)) {
+		dev_err(&pdev->dev, "failed to get clock.\n");
+		return PTR_ERR(mv_phy->clk);
+	}
+	clk_prepare(mv_phy->clk);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
+		return -ENODEV;
+	}
+	mv_phy->base = devm_ioremap_resource(&pdev->dev, r);
+	if (mv_phy->base == NULL) {
+		dev_err(&pdev->dev, "error map register base\n");
+		return -EBUSY;
+	}
+
+	mv_phy->phy.dev = &pdev->dev;
+	mv_phy->phy.label = "mv-usb2";
+	mv_phy->phy.type = USB_PHY_TYPE_USB2;
+	mv_phy->phy.init = mv_usb2_phy_init;
+	mv_phy->phy.shutdown = mv_usb2_phy_shutdown;
+	mv_phy->phy.charger_detect = mv_usb2_phy_charger_detect;
+	if (cpu_is_asr1806()) {
+		mv_phy->phy.set_suspend = mv_usb2_phy_suspend;
+		mv_phy->phy.set_suspend2 = mv_usb2_phy_suspend2;
+	}
+	mv_phy->phy.dump_cfg = mv_usb2_phy_dumpcfg;
+
+	mv_phy->phy.io_priv = mv_phy->base;
+
+	mv_usb2_phy_bind_device(mv_phy);
+
+	usb_add_phy_dev(&mv_phy->phy);
+
+	if (mv_phy->drv_data.phy_type == PHY_28HP) {
+		clk_enable(mv_phy->clk);
+		_mv_usb2_phy_28nmhp_preinit(mv_phy);
+	}
+
+	platform_set_drvdata(pdev, mv_phy);
+#if defined(CONFIG_CPU_ASR18XX)
+	mv_phy->phy.is_hsic = false;
+	mv_phy->phy.get_vbus = mv_usb2_phy_get_vbus;
+#endif
+	pr_info("usb2 phy ok\n");
+	return 0;
+}
+
+static int mv_usb2_phy_remove(struct platform_device *pdev)
+{
+	struct mv_usb2_phy *mv_phy = platform_get_drvdata(pdev);
+
+	usb_remove_phy(&mv_phy->phy);
+
+	clk_unprepare(mv_phy->clk);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id mv_usbphy_dt_match[] = {
+	{ .compatible = "marvell,usb2-phy-55lp", .data = (void *)PHY_55LP },
+	{ .compatible = "marvell,usb2-phy-40lp", .data = (void *)PHY_40LP },
+	{ .compatible = "marvell,usb2-phy-28lp", .data = (void *)PHY_28LP },
+	{ .compatible = "marvell,hsic-phy-28lp", .data = (void *)PHY_28LP_HSIC },
+	{ .compatible = "asr,usb2-phy-28hp", .data = (void *)PHY_28HP },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match);
+
+static struct platform_driver mv_usb2_phy_driver = {
+	.probe	= mv_usb2_phy_probe,
+	.remove = mv_usb2_phy_remove,
+	.driver = {
+		.name   = "mv-usb2-phy",
+		.owner  = THIS_MODULE,
+		.of_match_table = of_match_ptr(mv_usbphy_dt_match),
+	},
+};
+
+#ifndef CONFIG_USB_DWC2
+module_platform_driver(mv_usb2_phy_driver);
+#else
+static int __init asr_usb2_phydrv_init(void)
+{
+	return platform_driver_register(&mv_usb2_phy_driver);
+}
+
+static void __exit asr_usb2_phydrv_exit(void)
+{
+	platform_driver_unregister(&mv_usb2_phy_driver);
+}
+
+arch_initcall(asr_usb2_phydrv_init);
+module_exit(asr_usb2_phydrv_exit);
+#endif
+MODULE_ALIAS("platform: mv_usb2");
+MODULE_AUTHOR("Marvell Inc.");
+MODULE_DESCRIPTION("Marvell USB2 phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/marvell/linux/drivers/usb/phy/phy-mxs-usb.c b/marvell/linux/drivers/usb/phy/phy-mxs-usb.c
new file mode 100644
index 0000000..6dfecbd
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-mxs-usb.c
@@ -0,0 +1,880 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2012-2014 Freescale Semiconductor, Inc.
+ * Copyright (C) 2012 Marek Vasut <marex@denx.de>
+ * on behalf of DENX Software Engineering GmbH
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/usb/otg.h>
+#include <linux/stmp_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/iopoll.h>
+
+#define DRIVER_NAME "mxs_phy"
+
+/* Register Macro */
+#define HW_USBPHY_PWD				0x00
+#define HW_USBPHY_TX				0x10
+#define HW_USBPHY_CTRL				0x30
+#define HW_USBPHY_CTRL_SET			0x34
+#define HW_USBPHY_CTRL_CLR			0x38
+
+#define HW_USBPHY_DEBUG_SET			0x54
+#define HW_USBPHY_DEBUG_CLR			0x58
+
+#define HW_USBPHY_IP				0x90
+#define HW_USBPHY_IP_SET			0x94
+#define HW_USBPHY_IP_CLR			0x98
+
+#define GM_USBPHY_TX_TXCAL45DP(x)            (((x) & 0xf) << 16)
+#define GM_USBPHY_TX_TXCAL45DN(x)            (((x) & 0xf) << 8)
+#define GM_USBPHY_TX_D_CAL(x)                (((x) & 0xf) << 0)
+
+/* imx7ulp */
+#define HW_USBPHY_PLL_SIC			0xa0
+#define HW_USBPHY_PLL_SIC_SET			0xa4
+#define HW_USBPHY_PLL_SIC_CLR			0xa8
+
+#define BM_USBPHY_CTRL_SFTRST			BIT(31)
+#define BM_USBPHY_CTRL_CLKGATE			BIT(30)
+#define BM_USBPHY_CTRL_OTG_ID_VALUE		BIT(27)
+#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS	BIT(26)
+#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE	BIT(25)
+#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP		BIT(23)
+#define BM_USBPHY_CTRL_ENIDCHG_WKUP		BIT(22)
+#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP		BIT(21)
+#define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD	BIT(20)
+#define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE	BIT(19)
+#define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL		BIT(18)
+#define BM_USBPHY_CTRL_ENUTMILEVEL3		BIT(15)
+#define BM_USBPHY_CTRL_ENUTMILEVEL2		BIT(14)
+#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT	BIT(1)
+
+#define BM_USBPHY_IP_FIX                       (BIT(17) | BIT(18))
+
+#define BM_USBPHY_DEBUG_CLKGATE			BIT(30)
+/* imx7ulp */
+#define BM_USBPHY_PLL_LOCK			BIT(31)
+#define BM_USBPHY_PLL_REG_ENABLE		BIT(21)
+#define BM_USBPHY_PLL_BYPASS			BIT(16)
+#define BM_USBPHY_PLL_POWER			BIT(12)
+#define BM_USBPHY_PLL_EN_USB_CLKS		BIT(6)
+
+/* Anatop Registers */
+#define ANADIG_ANA_MISC0			0x150
+#define ANADIG_ANA_MISC0_SET			0x154
+#define ANADIG_ANA_MISC0_CLR			0x158
+
+#define ANADIG_USB1_CHRG_DETECT_SET		0x1b4
+#define ANADIG_USB1_CHRG_DETECT_CLR		0x1b8
+#define ANADIG_USB2_CHRG_DETECT_SET		0x214
+#define ANADIG_USB1_CHRG_DETECT_EN_B		BIT(20)
+#define ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B	BIT(19)
+#define ANADIG_USB1_CHRG_DETECT_CHK_CONTACT	BIT(18)
+
+#define ANADIG_USB1_VBUS_DET_STAT		0x1c0
+#define ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID	BIT(3)
+
+#define ANADIG_USB1_CHRG_DET_STAT		0x1d0
+#define ANADIG_USB1_CHRG_DET_STAT_DM_STATE	BIT(2)
+#define ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED	BIT(1)
+#define ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT	BIT(0)
+
+#define ANADIG_USB2_VBUS_DET_STAT		0x220
+
+#define ANADIG_USB1_LOOPBACK_SET		0x1e4
+#define ANADIG_USB1_LOOPBACK_CLR		0x1e8
+#define ANADIG_USB1_LOOPBACK_UTMI_TESTSTART	BIT(0)
+
+#define ANADIG_USB2_LOOPBACK_SET		0x244
+#define ANADIG_USB2_LOOPBACK_CLR		0x248
+
+#define ANADIG_USB1_MISC			0x1f0
+#define ANADIG_USB2_MISC			0x250
+
+#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG	BIT(12)
+#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11)
+
+#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID	BIT(3)
+#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID	BIT(3)
+
+#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1	BIT(2)
+#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN	BIT(5)
+#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1	BIT(2)
+#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN	BIT(5)
+
+#define BM_ANADIG_USB1_MISC_RX_VPIN_FS		BIT(29)
+#define BM_ANADIG_USB1_MISC_RX_VMIN_FS		BIT(28)
+#define BM_ANADIG_USB2_MISC_RX_VPIN_FS		BIT(29)
+#define BM_ANADIG_USB2_MISC_RX_VMIN_FS		BIT(28)
+
+#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
+
+/* Do disconnection between PHY and controller without vbus */
+#define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS	BIT(0)
+
+/*
+ * The PHY will be in messy if there is a wakeup after putting
+ * bus to suspend (set portsc.suspendM) but before setting PHY to low
+ * power mode (set portsc.phcd).
+ */
+#define MXS_PHY_ABNORMAL_IN_SUSPEND		BIT(1)
+
+/*
+ * The SOF sends too fast after resuming, it will cause disconnection
+ * between host and high speed device.
+ */
+#define MXS_PHY_SENDING_SOF_TOO_FAST		BIT(2)
+
+/*
+ * IC has bug fixes logic, they include
+ * MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST
+ * which are described at above flags, the RTL will handle it
+ * according to different versions.
+ */
+#define MXS_PHY_NEED_IP_FIX			BIT(3)
+
+/* Minimum and maximum values for device tree entries */
+#define MXS_PHY_TX_CAL45_MIN			30
+#define MXS_PHY_TX_CAL45_MAX			55
+#define MXS_PHY_TX_D_CAL_MIN			79
+#define MXS_PHY_TX_D_CAL_MAX			119
+
+struct mxs_phy_data {
+	unsigned int flags;
+};
+
+static const struct mxs_phy_data imx23_phy_data = {
+	.flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST,
+};
+
+static const struct mxs_phy_data imx6q_phy_data = {
+	.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
+		MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+		MXS_PHY_NEED_IP_FIX,
+};
+
+static const struct mxs_phy_data imx6sl_phy_data = {
+	.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+		MXS_PHY_NEED_IP_FIX,
+};
+
+static const struct mxs_phy_data vf610_phy_data = {
+	.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+		MXS_PHY_NEED_IP_FIX,
+};
+
+static const struct mxs_phy_data imx6sx_phy_data = {
+	.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+};
+
+static const struct mxs_phy_data imx6ul_phy_data = {
+	.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+};
+
+static const struct mxs_phy_data imx7ulp_phy_data = {
+};
+
+static const struct of_device_id mxs_phy_dt_ids[] = {
+	{ .compatible = "fsl,imx6sx-usbphy", .data = &imx6sx_phy_data, },
+	{ .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, },
+	{ .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, },
+	{ .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, },
+	{ .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, },
+	{ .compatible = "fsl,imx6ul-usbphy", .data = &imx6ul_phy_data, },
+	{ .compatible = "fsl,imx7ulp-usbphy", .data = &imx7ulp_phy_data, },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
+
+struct mxs_phy {
+	struct usb_phy phy;
+	struct clk *clk;
+	const struct mxs_phy_data *data;
+	struct regmap *regmap_anatop;
+	int port_id;
+	u32 tx_reg_set;
+	u32 tx_reg_mask;
+};
+
+static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
+{
+	return mxs_phy->data == &imx6q_phy_data;
+}
+
+static inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy)
+{
+	return mxs_phy->data == &imx6sl_phy_data;
+}
+
+static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy)
+{
+	return mxs_phy->data == &imx7ulp_phy_data;
+}
+
+/*
+ * PHY needs some 32K cycles to switch from 32K clock to
+ * bus (such as AHB/AXI, etc) clock.
+ */
+static void mxs_phy_clock_switch_delay(void)
+{
+	usleep_range(300, 400);
+}
+
+static void mxs_phy_tx_init(struct mxs_phy *mxs_phy)
+{
+	void __iomem *base = mxs_phy->phy.io_priv;
+	u32 phytx;
+
+	/* Update TX register if there is anything to write */
+	if (mxs_phy->tx_reg_mask) {
+		phytx = readl(base + HW_USBPHY_TX);
+		phytx &= ~mxs_phy->tx_reg_mask;
+		phytx |= mxs_phy->tx_reg_set;
+		writel(phytx, base + HW_USBPHY_TX);
+	}
+}
+
+static int mxs_phy_pll_enable(void __iomem *base, bool enable)
+{
+	int ret = 0;
+
+	if (enable) {
+		u32 value;
+
+		writel(BM_USBPHY_PLL_REG_ENABLE, base + HW_USBPHY_PLL_SIC_SET);
+		writel(BM_USBPHY_PLL_BYPASS, base + HW_USBPHY_PLL_SIC_CLR);
+		writel(BM_USBPHY_PLL_POWER, base + HW_USBPHY_PLL_SIC_SET);
+		ret = readl_poll_timeout(base + HW_USBPHY_PLL_SIC,
+			value, (value & BM_USBPHY_PLL_LOCK) != 0,
+			100, 10000);
+		if (ret)
+			return ret;
+
+		writel(BM_USBPHY_PLL_EN_USB_CLKS, base +
+				HW_USBPHY_PLL_SIC_SET);
+	} else {
+		writel(BM_USBPHY_PLL_EN_USB_CLKS, base +
+				HW_USBPHY_PLL_SIC_CLR);
+		writel(BM_USBPHY_PLL_POWER, base + HW_USBPHY_PLL_SIC_CLR);
+		writel(BM_USBPHY_PLL_BYPASS, base + HW_USBPHY_PLL_SIC_SET);
+		writel(BM_USBPHY_PLL_REG_ENABLE, base + HW_USBPHY_PLL_SIC_CLR);
+	}
+
+	return ret;
+}
+
+static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
+{
+	int ret;
+	void __iomem *base = mxs_phy->phy.io_priv;
+
+	if (is_imx7ulp_phy(mxs_phy)) {
+		ret = mxs_phy_pll_enable(base, true);
+		if (ret)
+			return ret;
+	}
+
+	ret = stmp_reset_block(base + HW_USBPHY_CTRL);
+	if (ret)
+		goto disable_pll;
+
+	/* Power up the PHY */
+	writel(0, base + HW_USBPHY_PWD);
+
+	/*
+	 * USB PHY Ctrl Setting
+	 * - Auto clock/power on
+	 * - Enable full/low speed support
+	 */
+	writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
+		BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
+		BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
+		BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
+		BM_USBPHY_CTRL_ENAUTO_PWRON_PLL |
+		BM_USBPHY_CTRL_ENUTMILEVEL2 |
+		BM_USBPHY_CTRL_ENUTMILEVEL3,
+	       base + HW_USBPHY_CTRL_SET);
+
+	if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX)
+		writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET);
+
+	if (mxs_phy->regmap_anatop) {
+		unsigned int reg = mxs_phy->port_id ?
+			ANADIG_USB1_CHRG_DETECT_SET :
+			ANADIG_USB2_CHRG_DETECT_SET;
+		/*
+		 * The external charger detector needs to be disabled,
+		 * or the signal at DP will be poor
+		 */
+		regmap_write(mxs_phy->regmap_anatop, reg,
+			     ANADIG_USB1_CHRG_DETECT_EN_B |
+			     ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B);
+	}
+
+	mxs_phy_tx_init(mxs_phy);
+
+	return 0;
+
+disable_pll:
+	if (is_imx7ulp_phy(mxs_phy))
+		mxs_phy_pll_enable(base, false);
+	return ret;
+}
+
+/* Return true if the vbus is there */
+static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy)
+{
+	unsigned int vbus_value = 0;
+
+	if (!mxs_phy->regmap_anatop)
+		return false;
+
+	if (mxs_phy->port_id == 0)
+		regmap_read(mxs_phy->regmap_anatop,
+			ANADIG_USB1_VBUS_DET_STAT,
+			&vbus_value);
+	else if (mxs_phy->port_id == 1)
+		regmap_read(mxs_phy->regmap_anatop,
+			ANADIG_USB2_VBUS_DET_STAT,
+			&vbus_value);
+
+	if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)
+		return true;
+	else
+		return false;
+}
+
+static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
+{
+	void __iomem *base = mxs_phy->phy.io_priv;
+	u32 reg;
+
+	if (disconnect)
+		writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
+			base + HW_USBPHY_DEBUG_CLR);
+
+	if (mxs_phy->port_id == 0) {
+		reg = disconnect ? ANADIG_USB1_LOOPBACK_SET
+			: ANADIG_USB1_LOOPBACK_CLR;
+		regmap_write(mxs_phy->regmap_anatop, reg,
+			BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 |
+			BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN);
+	} else if (mxs_phy->port_id == 1) {
+		reg = disconnect ? ANADIG_USB2_LOOPBACK_SET
+			: ANADIG_USB2_LOOPBACK_CLR;
+		regmap_write(mxs_phy->regmap_anatop, reg,
+			BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 |
+			BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN);
+	}
+
+	if (!disconnect)
+		writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
+			base + HW_USBPHY_DEBUG_SET);
+
+	/* Delay some time, and let Linestate be SE0 for controller */
+	if (disconnect)
+		usleep_range(500, 1000);
+}
+
+static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
+{
+	return mxs_phy->phy.last_event == USB_EVENT_ID;
+}
+
+static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
+{
+	bool vbus_is_on = false;
+
+	/* If the SoCs don't need to disconnect line without vbus, quit */
+	if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
+		return;
+
+	/* If the SoCs don't have anatop, quit */
+	if (!mxs_phy->regmap_anatop)
+		return;
+
+	vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
+
+	if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
+		__mxs_phy_disconnect_line(mxs_phy, true);
+	else
+		__mxs_phy_disconnect_line(mxs_phy, false);
+
+}
+
+static int mxs_phy_init(struct usb_phy *phy)
+{
+	int ret;
+	struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
+	mxs_phy_clock_switch_delay();
+	ret = clk_prepare_enable(mxs_phy->clk);
+	if (ret)
+		return ret;
+
+	return mxs_phy_hw_init(mxs_phy);
+}
+
+static void mxs_phy_shutdown(struct usb_phy *phy)
+{
+	struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+	u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP |
+			BM_USBPHY_CTRL_ENDPDMCHG_WKUP |
+			BM_USBPHY_CTRL_ENIDCHG_WKUP |
+			BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
+			BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
+			BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
+			BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
+			BM_USBPHY_CTRL_ENAUTO_PWRON_PLL;
+
+	writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR);
+	writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD);
+
+	writel(BM_USBPHY_CTRL_CLKGATE,
+	       phy->io_priv + HW_USBPHY_CTRL_SET);
+
+	if (is_imx7ulp_phy(mxs_phy))
+		mxs_phy_pll_enable(phy->io_priv, false);
+
+	clk_disable_unprepare(mxs_phy->clk);
+}
+
+static bool mxs_phy_is_low_speed_connection(struct mxs_phy *mxs_phy)
+{
+	unsigned int line_state;
+	/* bit definition is the same for all controllers */
+	unsigned int dp_bit = BM_ANADIG_USB1_MISC_RX_VPIN_FS,
+		     dm_bit = BM_ANADIG_USB1_MISC_RX_VMIN_FS;
+	unsigned int reg = ANADIG_USB1_MISC;
+
+	/* If the SoCs don't have anatop, quit */
+	if (!mxs_phy->regmap_anatop)
+		return false;
+
+	if (mxs_phy->port_id == 0)
+		reg = ANADIG_USB1_MISC;
+	else if (mxs_phy->port_id == 1)
+		reg = ANADIG_USB2_MISC;
+
+	regmap_read(mxs_phy->regmap_anatop, reg, &line_state);
+
+	if ((line_state & (dp_bit | dm_bit)) ==  dm_bit)
+		return true;
+	else
+		return false;
+}
+
+static int mxs_phy_suspend(struct usb_phy *x, int suspend)
+{
+	int ret;
+	struct mxs_phy *mxs_phy = to_mxs_phy(x);
+	bool low_speed_connection, vbus_is_on;
+
+	low_speed_connection = mxs_phy_is_low_speed_connection(mxs_phy);
+	vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
+
+	if (suspend) {
+		/*
+		 * FIXME: Do not power down RXPWD1PT1 bit for low speed
+		 * connect. The low speed connection will have problem at
+		 * very rare cases during usb suspend and resume process.
+		 */
+		if (low_speed_connection & vbus_is_on) {
+			/*
+			 * If value to be set as pwd value is not 0xffffffff,
+			 * several 32Khz cycles are needed.
+			 */
+			mxs_phy_clock_switch_delay();
+			writel(0xffbfffff, x->io_priv + HW_USBPHY_PWD);
+		} else {
+			writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
+		}
+		writel(BM_USBPHY_CTRL_CLKGATE,
+		       x->io_priv + HW_USBPHY_CTRL_SET);
+		clk_disable_unprepare(mxs_phy->clk);
+	} else {
+		mxs_phy_clock_switch_delay();
+		ret = clk_prepare_enable(mxs_phy->clk);
+		if (ret)
+			return ret;
+		writel(BM_USBPHY_CTRL_CLKGATE,
+		       x->io_priv + HW_USBPHY_CTRL_CLR);
+		writel(0, x->io_priv + HW_USBPHY_PWD);
+	}
+
+	return 0;
+}
+
+static int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled)
+{
+	struct mxs_phy *mxs_phy = to_mxs_phy(x);
+	u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP |
+			BM_USBPHY_CTRL_ENDPDMCHG_WKUP |
+				BM_USBPHY_CTRL_ENIDCHG_WKUP;
+	if (enabled) {
+		mxs_phy_disconnect_line(mxs_phy, true);
+		writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET);
+	} else {
+		writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR);
+		mxs_phy_disconnect_line(mxs_phy, false);
+	}
+
+	return 0;
+}
+
+static int mxs_phy_on_connect(struct usb_phy *phy,
+		enum usb_device_speed speed)
+{
+	dev_dbg(phy->dev, "%s device has connected\n",
+		(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
+
+	if (speed == USB_SPEED_HIGH)
+		writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+		       phy->io_priv + HW_USBPHY_CTRL_SET);
+
+	return 0;
+}
+
+static int mxs_phy_on_disconnect(struct usb_phy *phy,
+		enum usb_device_speed speed)
+{
+	dev_dbg(phy->dev, "%s device has disconnected\n",
+		(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
+
+	/* Sometimes, the speed is not high speed when the error occurs */
+	if (readl(phy->io_priv + HW_USBPHY_CTRL) &
+			BM_USBPHY_CTRL_ENHOSTDISCONDETECT)
+		writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+		       phy->io_priv + HW_USBPHY_CTRL_CLR);
+
+	return 0;
+}
+
+#define MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT	100
+static int mxs_charger_data_contact_detect(struct mxs_phy *x)
+{
+	struct regmap *regmap = x->regmap_anatop;
+	int i, stable_contact_count = 0;
+	u32 val;
+
+	/* Check if vbus is valid */
+	regmap_read(regmap, ANADIG_USB1_VBUS_DET_STAT, &val);
+	if (!(val & ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)) {
+		dev_err(x->phy.dev, "vbus is not valid\n");
+		return -EINVAL;
+	}
+
+	/* Enable charger detector */
+	regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_CLR,
+				ANADIG_USB1_CHRG_DETECT_EN_B);
+	/*
+	 * - Do not check whether a charger is connected to the USB port
+	 * - Check whether the USB plug has been in contact with each other
+	 */
+	regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET,
+			ANADIG_USB1_CHRG_DETECT_CHK_CONTACT |
+			ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B);
+
+	/* Check if plug is connected */
+	for (i = 0; i < MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT; i++) {
+		regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val);
+		if (val & ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT) {
+			stable_contact_count++;
+			if (stable_contact_count > 5)
+				/* Data pin makes contact */
+				break;
+			else
+				usleep_range(5000, 10000);
+		} else {
+			stable_contact_count = 0;
+			usleep_range(5000, 6000);
+		}
+	}
+
+	if (i == MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT) {
+		dev_err(x->phy.dev,
+			"Data pin can't make good contact.\n");
+		/* Disable charger detector */
+		regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET,
+				ANADIG_USB1_CHRG_DETECT_EN_B |
+				ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x)
+{
+	struct regmap *regmap = x->regmap_anatop;
+	enum usb_charger_type chgr_type = UNKNOWN_TYPE;
+	u32 val;
+
+	/*
+	 * - Do check whether a charger is connected to the USB port
+	 * - Do not Check whether the USB plug has been in contact with
+	 *   each other
+	 */
+	regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_CLR,
+			ANADIG_USB1_CHRG_DETECT_CHK_CONTACT |
+			ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B);
+
+	msleep(100);
+
+	/* Check if it is a charger */
+	regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val);
+	if (!(val & ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED)) {
+		chgr_type = SDP_TYPE;
+		dev_dbg(x->phy.dev, "It is a standard downstream port\n");
+	}
+
+	/* Disable charger detector */
+	regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET,
+			ANADIG_USB1_CHRG_DETECT_EN_B |
+			ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B);
+
+	return chgr_type;
+}
+
+/*
+ * It must be called after DP is pulled up, which is used to
+ * differentiate DCP and CDP.
+ */
+static enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x)
+{
+	struct regmap *regmap = x->regmap_anatop;
+	int val;
+
+	msleep(80);
+
+	regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val);
+	if (val & ANADIG_USB1_CHRG_DET_STAT_DM_STATE) {
+		dev_dbg(x->phy.dev, "It is a dedicate charging port\n");
+		return DCP_TYPE;
+	} else {
+		dev_dbg(x->phy.dev, "It is a charging downstream port\n");
+		return CDP_TYPE;
+	}
+}
+
+static enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy)
+{
+	struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+	struct regmap *regmap = mxs_phy->regmap_anatop;
+	void __iomem *base = phy->io_priv;
+	enum usb_charger_type chgr_type = UNKNOWN_TYPE;
+
+	if (!regmap)
+		return UNKNOWN_TYPE;
+
+	if (mxs_charger_data_contact_detect(mxs_phy))
+		return chgr_type;
+
+	chgr_type = mxs_charger_primary_detection(mxs_phy);
+
+	if (chgr_type != SDP_TYPE) {
+		/* Pull up DP via test */
+		writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
+				base + HW_USBPHY_DEBUG_CLR);
+		regmap_write(regmap, ANADIG_USB1_LOOPBACK_SET,
+				ANADIG_USB1_LOOPBACK_UTMI_TESTSTART);
+
+		chgr_type = mxs_charger_secondary_detection(mxs_phy);
+
+		/* Stop the test */
+		regmap_write(regmap, ANADIG_USB1_LOOPBACK_CLR,
+				ANADIG_USB1_LOOPBACK_UTMI_TESTSTART);
+		writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
+				base + HW_USBPHY_DEBUG_SET);
+	}
+
+	return chgr_type;
+}
+
+static int mxs_phy_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	void __iomem *base;
+	struct clk *clk;
+	struct mxs_phy *mxs_phy;
+	int ret;
+	const struct of_device_id *of_id;
+	struct device_node *np = pdev->dev.of_node;
+	u32 val;
+
+	of_id = of_match_device(mxs_phy_dt_ids, &pdev->dev);
+	if (!of_id)
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev,
+			"can't get the clock, err=%ld", PTR_ERR(clk));
+		return PTR_ERR(clk);
+	}
+
+	mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
+	if (!mxs_phy)
+		return -ENOMEM;
+
+	/* Some SoCs don't have anatop registers */
+	if (of_get_property(np, "fsl,anatop", NULL)) {
+		mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle
+			(np, "fsl,anatop");
+		if (IS_ERR(mxs_phy->regmap_anatop)) {
+			dev_dbg(&pdev->dev,
+				"failed to find regmap for anatop\n");
+			return PTR_ERR(mxs_phy->regmap_anatop);
+		}
+	}
+
+	/* Precompute which bits of the TX register are to be updated, if any */
+	if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
+	    val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
+		/* Scale to a 4-bit value */
+		val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF
+			/ (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN);
+		mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DN(~0);
+		mxs_phy->tx_reg_set  |= GM_USBPHY_TX_TXCAL45DN(val);
+	}
+
+	if (!of_property_read_u32(np, "fsl,tx-cal-45-dp-ohms", &val) &&
+	    val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
+		/* Scale to a 4-bit value. */
+		val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF
+			/ (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN);
+		mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DP(~0);
+		mxs_phy->tx_reg_set  |= GM_USBPHY_TX_TXCAL45DP(val);
+	}
+
+	if (!of_property_read_u32(np, "fsl,tx-d-cal", &val) &&
+	    val >= MXS_PHY_TX_D_CAL_MIN && val <= MXS_PHY_TX_D_CAL_MAX) {
+		/* Scale to a 4-bit value.  Round up the values and heavily
+		 * weight the rounding by adding 2/3 of the denominator.
+		 */
+		val = ((MXS_PHY_TX_D_CAL_MAX - val) * 0xF
+			+ (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN) * 2/3)
+			/ (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN);
+		mxs_phy->tx_reg_mask |= GM_USBPHY_TX_D_CAL(~0);
+		mxs_phy->tx_reg_set  |= GM_USBPHY_TX_D_CAL(val);
+	}
+
+	ret = of_alias_get_id(np, "usbphy");
+	if (ret < 0)
+		dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+	mxs_phy->port_id = ret;
+
+	mxs_phy->phy.io_priv		= base;
+	mxs_phy->phy.dev		= &pdev->dev;
+	mxs_phy->phy.label		= DRIVER_NAME;
+	mxs_phy->phy.init		= mxs_phy_init;
+	mxs_phy->phy.shutdown		= mxs_phy_shutdown;
+	mxs_phy->phy.set_suspend	= mxs_phy_suspend;
+	mxs_phy->phy.notify_connect	= mxs_phy_on_connect;
+	mxs_phy->phy.notify_disconnect	= mxs_phy_on_disconnect;
+	mxs_phy->phy.type		= USB_PHY_TYPE_USB2;
+	mxs_phy->phy.set_wakeup		= mxs_phy_set_wakeup;
+	mxs_phy->phy.charger_detect	= mxs_phy_charger_detect;
+
+	mxs_phy->clk = clk;
+	mxs_phy->data = of_id->data;
+
+	platform_set_drvdata(pdev, mxs_phy);
+
+	device_set_wakeup_capable(&pdev->dev, true);
+
+	return usb_add_phy_dev(&mxs_phy->phy);
+}
+
+static int mxs_phy_remove(struct platform_device *pdev)
+{
+	struct mxs_phy *mxs_phy = platform_get_drvdata(pdev);
+
+	usb_remove_phy(&mxs_phy->phy);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
+{
+	unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
+
+	/* If the SoCs don't have anatop, quit */
+	if (!mxs_phy->regmap_anatop)
+		return;
+
+	if (is_imx6q_phy(mxs_phy))
+		regmap_write(mxs_phy->regmap_anatop, reg,
+			BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
+	else if (is_imx6sl_phy(mxs_phy))
+		regmap_write(mxs_phy->regmap_anatop,
+			reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
+}
+
+static int mxs_phy_system_suspend(struct device *dev)
+{
+	struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
+
+	return 0;
+}
+
+static int mxs_phy_system_resume(struct device *dev)
+{
+	struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
+
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend,
+		mxs_phy_system_resume);
+
+static struct platform_driver mxs_phy_driver = {
+	.probe = mxs_phy_probe,
+	.remove = mxs_phy_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = mxs_phy_dt_ids,
+		.pm = &mxs_phy_pm,
+	 },
+};
+
+static int __init mxs_phy_module_init(void)
+{
+	return platform_driver_register(&mxs_phy_driver);
+}
+postcore_initcall(mxs_phy_module_init);
+
+static void __exit mxs_phy_module_exit(void)
+{
+	platform_driver_unregister(&mxs_phy_driver);
+}
+module_exit(mxs_phy_module_exit);
+
+MODULE_ALIAS("platform:mxs-usb-phy");
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
+MODULE_DESCRIPTION("Freescale MXS USB PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/drivers/usb/phy/phy-omap-otg.c b/marvell/linux/drivers/usb/phy/phy-omap-otg.c
new file mode 100644
index 0000000..ee0863c
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-omap-otg.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * OMAP OTG controller driver
+ *
+ * Based on code from tahvo-usb.c and isp1301_omap.c drivers.
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
+ */
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/usb-omap1.h>
+
+struct otg_device {
+	void __iomem			*base;
+	bool				id;
+	bool				vbus;
+	struct extcon_dev		*extcon;
+	struct notifier_block		vbus_nb;
+	struct notifier_block		id_nb;
+};
+
+#define OMAP_OTG_CTRL		0x0c
+#define OMAP_OTG_ASESSVLD	(1 << 20)
+#define OMAP_OTG_BSESSEND	(1 << 19)
+#define OMAP_OTG_BSESSVLD	(1 << 18)
+#define OMAP_OTG_VBUSVLD	(1 << 17)
+#define OMAP_OTG_ID		(1 << 16)
+#define OMAP_OTG_XCEIV_OUTPUTS \
+	(OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \
+	 OMAP_OTG_VBUSVLD  | OMAP_OTG_ID)
+
+static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs)
+{
+	u32 l;
+
+	l = readl(otg_dev->base + OMAP_OTG_CTRL);
+	l &= ~OMAP_OTG_XCEIV_OUTPUTS;
+	l |= outputs;
+	writel(l, otg_dev->base + OMAP_OTG_CTRL);
+}
+
+static void omap_otg_set_mode(struct otg_device *otg_dev)
+{
+	if (!otg_dev->id && otg_dev->vbus)
+		/* Set B-session valid. */
+		omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD);
+	else if (otg_dev->vbus)
+		/* Set A-session valid. */
+		omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD);
+	else if (!otg_dev->id)
+		/* Set B-session end to indicate no VBUS. */
+		omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND);
+}
+
+static int omap_otg_id_notifier(struct notifier_block *nb,
+				unsigned long event, void *ptr)
+{
+	struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb);
+
+	otg_dev->id = event;
+	omap_otg_set_mode(otg_dev);
+
+	return NOTIFY_DONE;
+}
+
+static int omap_otg_vbus_notifier(struct notifier_block *nb,
+				  unsigned long event, void *ptr)
+{
+	struct otg_device *otg_dev = container_of(nb, struct otg_device,
+						  vbus_nb);
+
+	otg_dev->vbus = event;
+	omap_otg_set_mode(otg_dev);
+
+	return NOTIFY_DONE;
+}
+
+static int omap_otg_probe(struct platform_device *pdev)
+{
+	const struct omap_usb_config *config = pdev->dev.platform_data;
+	struct otg_device *otg_dev;
+	struct extcon_dev *extcon;
+	int ret;
+	u32 rev;
+
+	if (!config || !config->extcon)
+		return -ENODEV;
+
+	extcon = extcon_get_extcon_dev(config->extcon);
+	if (!extcon)
+		return -EPROBE_DEFER;
+
+	otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
+	if (!otg_dev)
+		return -ENOMEM;
+
+	otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
+	if (IS_ERR(otg_dev->base))
+		return PTR_ERR(otg_dev->base);
+
+	otg_dev->extcon = extcon;
+	otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
+	otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
+
+	ret = devm_extcon_register_notifier(&pdev->dev, extcon,
+					EXTCON_USB_HOST, &otg_dev->id_nb);
+	if (ret)
+		return ret;
+
+	ret = devm_extcon_register_notifier(&pdev->dev, extcon,
+					EXTCON_USB, &otg_dev->vbus_nb);
+	if (ret) {
+		return ret;
+	}
+
+	otg_dev->id = extcon_get_state(extcon, EXTCON_USB_HOST);
+	otg_dev->vbus = extcon_get_state(extcon, EXTCON_USB);
+	omap_otg_set_mode(otg_dev);
+
+	rev = readl(otg_dev->base);
+
+	dev_info(&pdev->dev,
+		 "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n",
+		 (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id,
+		 otg_dev->vbus);
+
+	platform_set_drvdata(pdev, otg_dev);
+
+	return 0;
+}
+
+static struct platform_driver omap_otg_driver = {
+	.probe		= omap_otg_probe,
+	.driver		= {
+		.name	= "omap_otg",
+	},
+};
+module_platform_driver(omap_otg_driver);
+
+MODULE_DESCRIPTION("OMAP USB OTG controller driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
diff --git a/marvell/linux/drivers/usb/phy/phy-tahvo.c b/marvell/linux/drivers/usb/phy/phy-tahvo.c
new file mode 100644
index 0000000..d0672b6
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-tahvo.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Tahvo USB transceiver driver
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * Parts copied from isp1301_omap.c.
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
+ *
+ * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs.
+ * Modified for Retu/Tahvo MFD by Aaro Koskinen.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/usb.h>
+#include <linux/extcon-provider.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/otg.h>
+#include <linux/mfd/retu.h>
+#include <linux/usb/gadget.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME     "tahvo-usb"
+
+#define TAHVO_REG_IDSR	0x02
+#define TAHVO_REG_USBR	0x06
+
+#define USBR_SLAVE_CONTROL	(1 << 8)
+#define USBR_VPPVIO_SW		(1 << 7)
+#define USBR_SPEED		(1 << 6)
+#define USBR_REGOUT		(1 << 5)
+#define USBR_MASTER_SW2		(1 << 4)
+#define USBR_MASTER_SW1		(1 << 3)
+#define USBR_SLAVE_SW		(1 << 2)
+#define USBR_NSUSPEND		(1 << 1)
+#define USBR_SEMODE		(1 << 0)
+
+#define TAHVO_MODE_HOST		0
+#define TAHVO_MODE_PERIPHERAL	1
+
+struct tahvo_usb {
+	struct platform_device	*pt_dev;
+	struct usb_phy		phy;
+	int			vbus_state;
+	struct mutex		serialize;
+	struct clk		*ick;
+	int			irq;
+	int			tahvo_mode;
+	struct extcon_dev	*extcon;
+};
+
+static const unsigned int tahvo_cable[] = {
+	EXTCON_USB,
+	EXTCON_USB_HOST,
+
+	EXTCON_NONE,
+};
+
+static ssize_t vbus_show(struct device *device,
+			       struct device_attribute *attr, char *buf)
+{
+	struct tahvo_usb *tu = dev_get_drvdata(device);
+	return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
+}
+static DEVICE_ATTR_RO(vbus);
+
+static void check_vbus_state(struct tahvo_usb *tu)
+{
+	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+	int reg, prev_state;
+
+	reg = retu_read(rdev, TAHVO_REG_IDSR);
+	if (reg & TAHVO_STAT_VBUS) {
+		switch (tu->phy.otg->state) {
+		case OTG_STATE_B_IDLE:
+			/* Enable the gadget driver */
+			if (tu->phy.otg->gadget)
+				usb_gadget_vbus_connect(tu->phy.otg->gadget);
+			tu->phy.otg->state = OTG_STATE_B_PERIPHERAL;
+			usb_phy_set_event(&tu->phy, USB_EVENT_ENUMERATED);
+			break;
+		case OTG_STATE_A_IDLE:
+			/*
+			 * Session is now valid assuming the USB hub is driving
+			 * Vbus.
+			 */
+			tu->phy.otg->state = OTG_STATE_A_HOST;
+			break;
+		default:
+			break;
+		}
+		dev_info(&tu->pt_dev->dev, "USB cable connected\n");
+	} else {
+		switch (tu->phy.otg->state) {
+		case OTG_STATE_B_PERIPHERAL:
+			if (tu->phy.otg->gadget)
+				usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
+			tu->phy.otg->state = OTG_STATE_B_IDLE;
+			usb_phy_set_event(&tu->phy, USB_EVENT_NONE);
+			break;
+		case OTG_STATE_A_HOST:
+			tu->phy.otg->state = OTG_STATE_A_IDLE;
+			break;
+		default:
+			break;
+		}
+		dev_info(&tu->pt_dev->dev, "USB cable disconnected\n");
+	}
+
+	prev_state = tu->vbus_state;
+	tu->vbus_state = reg & TAHVO_STAT_VBUS;
+	if (prev_state != tu->vbus_state) {
+		extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
+		sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
+	}
+}
+
+static void tahvo_usb_become_host(struct tahvo_usb *tu)
+{
+	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+
+	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, true);
+
+	/* Power up the transceiver in USB host mode */
+	retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
+		   USBR_MASTER_SW2 | USBR_MASTER_SW1);
+	tu->phy.otg->state = OTG_STATE_A_IDLE;
+
+	check_vbus_state(tu);
+}
+
+static void tahvo_usb_stop_host(struct tahvo_usb *tu)
+{
+	tu->phy.otg->state = OTG_STATE_A_IDLE;
+}
+
+static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
+{
+	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+
+	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, false);
+
+	/* Power up transceiver and set it in USB peripheral mode */
+	retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
+		   USBR_NSUSPEND | USBR_SLAVE_SW);
+	tu->phy.otg->state = OTG_STATE_B_IDLE;
+
+	check_vbus_state(tu);
+}
+
+static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
+{
+	if (tu->phy.otg->gadget)
+		usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
+	tu->phy.otg->state = OTG_STATE_B_IDLE;
+}
+
+static void tahvo_usb_power_off(struct tahvo_usb *tu)
+{
+	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+
+	/* Disable gadget controller if any */
+	if (tu->phy.otg->gadget)
+		usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
+
+	/* Power off transceiver */
+	retu_write(rdev, TAHVO_REG_USBR, 0);
+	tu->phy.otg->state = OTG_STATE_UNDEFINED;
+}
+
+static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend)
+{
+	struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy);
+	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+	u16 w;
+
+	dev_dbg(&tu->pt_dev->dev, "%s\n", __func__);
+
+	w = retu_read(rdev, TAHVO_REG_USBR);
+	if (suspend)
+		w &= ~USBR_NSUSPEND;
+	else
+		w |= USBR_NSUSPEND;
+	retu_write(rdev, TAHVO_REG_USBR, w);
+
+	return 0;
+}
+
+static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
+					    phy);
+
+	dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host);
+
+	mutex_lock(&tu->serialize);
+
+	if (host == NULL) {
+		if (tu->tahvo_mode == TAHVO_MODE_HOST)
+			tahvo_usb_power_off(tu);
+		otg->host = NULL;
+		mutex_unlock(&tu->serialize);
+		return 0;
+	}
+
+	if (tu->tahvo_mode == TAHVO_MODE_HOST) {
+		otg->host = NULL;
+		tahvo_usb_become_host(tu);
+	}
+
+	otg->host = host;
+
+	mutex_unlock(&tu->serialize);
+
+	return 0;
+}
+
+static int tahvo_usb_set_peripheral(struct usb_otg *otg,
+				    struct usb_gadget *gadget)
+{
+	struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
+					    phy);
+
+	dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget);
+
+	mutex_lock(&tu->serialize);
+
+	if (!gadget) {
+		if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
+			tahvo_usb_power_off(tu);
+		tu->phy.otg->gadget = NULL;
+		mutex_unlock(&tu->serialize);
+		return 0;
+	}
+
+	tu->phy.otg->gadget = gadget;
+	if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
+		tahvo_usb_become_peripheral(tu);
+
+	mutex_unlock(&tu->serialize);
+
+	return 0;
+}
+
+static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu)
+{
+	struct tahvo_usb *tu = _tu;
+
+	mutex_lock(&tu->serialize);
+	check_vbus_state(tu);
+	mutex_unlock(&tu->serialize);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t otg_mode_show(struct device *device,
+			     struct device_attribute *attr, char *buf)
+{
+	struct tahvo_usb *tu = dev_get_drvdata(device);
+
+	switch (tu->tahvo_mode) {
+	case TAHVO_MODE_HOST:
+		return sprintf(buf, "host\n");
+	case TAHVO_MODE_PERIPHERAL:
+		return sprintf(buf, "peripheral\n");
+	}
+
+	return -EINVAL;
+}
+
+static ssize_t otg_mode_store(struct device *device,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct tahvo_usb *tu = dev_get_drvdata(device);
+	int r;
+
+	mutex_lock(&tu->serialize);
+	if (count >= 4 && strncmp(buf, "host", 4) == 0) {
+		if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
+			tahvo_usb_stop_peripheral(tu);
+		tu->tahvo_mode = TAHVO_MODE_HOST;
+		if (tu->phy.otg->host) {
+			dev_info(device, "HOST mode: host controller present\n");
+			tahvo_usb_become_host(tu);
+		} else {
+			dev_info(device, "HOST mode: no host controller, powering off\n");
+			tahvo_usb_power_off(tu);
+		}
+		r = strlen(buf);
+	} else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) {
+		if (tu->tahvo_mode == TAHVO_MODE_HOST)
+			tahvo_usb_stop_host(tu);
+		tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
+		if (tu->phy.otg->gadget) {
+			dev_info(device, "PERIPHERAL mode: gadget driver present\n");
+			tahvo_usb_become_peripheral(tu);
+		} else {
+			dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n");
+			tahvo_usb_power_off(tu);
+		}
+		r = strlen(buf);
+	} else {
+		r = -EINVAL;
+	}
+	mutex_unlock(&tu->serialize);
+
+	return r;
+}
+static DEVICE_ATTR_RW(otg_mode);
+
+static struct attribute *tahvo_attrs[] = {
+	&dev_attr_vbus.attr,
+	&dev_attr_otg_mode.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(tahvo);
+
+static int tahvo_usb_probe(struct platform_device *pdev)
+{
+	struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
+	struct tahvo_usb *tu;
+	int ret;
+
+	tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL);
+	if (!tu)
+		return -ENOMEM;
+
+	tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg),
+				   GFP_KERNEL);
+	if (!tu->phy.otg)
+		return -ENOMEM;
+
+	tu->pt_dev = pdev;
+
+	/* Default mode */
+#ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT
+	tu->tahvo_mode = TAHVO_MODE_HOST;
+#else
+	tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
+#endif
+
+	mutex_init(&tu->serialize);
+
+	tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick");
+	if (!IS_ERR(tu->ick))
+		clk_enable(tu->ick);
+
+	/*
+	 * Set initial state, so that we generate kevents only on state changes.
+	 */
+	tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS;
+
+	tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable);
+	if (IS_ERR(tu->extcon)) {
+		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
+		ret = PTR_ERR(tu->extcon);
+		goto err_disable_clk;
+	}
+
+	ret = devm_extcon_dev_register(&pdev->dev, tu->extcon);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register extcon device: %d\n",
+			ret);
+		goto err_disable_clk;
+	}
+
+	/* Set the initial cable state. */
+	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST,
+			       tu->tahvo_mode == TAHVO_MODE_HOST);
+	extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
+
+	/* Create OTG interface */
+	tahvo_usb_power_off(tu);
+	tu->phy.dev = &pdev->dev;
+	tu->phy.otg->state = OTG_STATE_UNDEFINED;
+	tu->phy.label = DRIVER_NAME;
+	tu->phy.set_suspend = tahvo_usb_set_suspend;
+
+	tu->phy.otg->usb_phy = &tu->phy;
+	tu->phy.otg->set_host = tahvo_usb_set_host;
+	tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral;
+
+	ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "cannot register USB transceiver: %d\n",
+			ret);
+		goto err_disable_clk;
+	}
+
+	dev_set_drvdata(&pdev->dev, tu);
+
+	tu->irq = ret = platform_get_irq(pdev, 0);
+	if (ret < 0)
+		goto err_remove_phy;
+	ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt,
+				   IRQF_ONESHOT,
+				   "tahvo-vbus", tu);
+	if (ret) {
+		dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n",
+			ret);
+		goto err_remove_phy;
+	}
+
+	return 0;
+
+err_remove_phy:
+	usb_remove_phy(&tu->phy);
+err_disable_clk:
+	if (!IS_ERR(tu->ick))
+		clk_disable(tu->ick);
+
+	return ret;
+}
+
+static int tahvo_usb_remove(struct platform_device *pdev)
+{
+	struct tahvo_usb *tu = platform_get_drvdata(pdev);
+
+	free_irq(tu->irq, tu);
+	usb_remove_phy(&tu->phy);
+	if (!IS_ERR(tu->ick))
+		clk_disable(tu->ick);
+
+	return 0;
+}
+
+static struct platform_driver tahvo_usb_driver = {
+	.probe		= tahvo_usb_probe,
+	.remove		= tahvo_usb_remove,
+	.driver		= {
+		.name	= "tahvo-usb",
+		.dev_groups = tahvo_groups,
+	},
+};
+module_platform_driver(tahvo_usb_driver);
+
+MODULE_DESCRIPTION("Tahvo USB transceiver driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs");
+MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
diff --git a/marvell/linux/drivers/usb/phy/phy-tegra-usb.c b/marvell/linux/drivers/usb/phy/phy-tegra-usb.c
new file mode 100644
index 0000000..ea7ef1d
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-tegra-usb.c
@@ -0,0 +1,1176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2013 NVIDIA Corporation
+ *
+ * Author:
+ *	Erik Gilling <konkers@google.com>
+ *	Benoit Goby <benoit@android.com>
+ *	Venu Byravarasu <vbyravarasu@nvidia.com>
+ */
+
+#include <linux/resource.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/iopoll.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+#include <linux/usb/of.h>
+#include <linux/usb/ehci_def.h>
+#include <linux/usb/tegra_usb_phy.h>
+#include <linux/regulator/consumer.h>
+
+#define ULPI_VIEWPORT		0x170
+
+/* PORTSC PTS/PHCD bits, Tegra20 only */
+#define TEGRA_USB_PORTSC1				0x184
+#define TEGRA_USB_PORTSC1_PTS(x)			(((x) & 0x3) << 30)
+#define TEGRA_USB_PORTSC1_PHCD				(1 << 23)
+
+/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
+#define TEGRA_USB_HOSTPC1_DEVLC		0x1b4
+#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x)	(((x) & 0x7) << 29)
+#define TEGRA_USB_HOSTPC1_DEVLC_PHCD	(1 << 22)
+
+/* Bits of PORTSC1, which will get cleared by writing 1 into them */
+#define TEGRA_PORTSC1_RWC_BITS	(PORT_CSC | PORT_PEC | PORT_OCC)
+
+#define USB_SUSP_CTRL		0x400
+#define   USB_WAKE_ON_CNNT_EN_DEV	(1 << 3)
+#define   USB_WAKE_ON_DISCON_EN_DEV	(1 << 4)
+#define   USB_SUSP_CLR		(1 << 5)
+#define   USB_PHY_CLK_VALID	(1 << 7)
+#define   UTMIP_RESET			(1 << 11)
+#define   UHSIC_RESET			(1 << 11)
+#define   UTMIP_PHY_ENABLE		(1 << 12)
+#define   ULPI_PHY_ENABLE	(1 << 13)
+#define   USB_SUSP_SET		(1 << 14)
+#define   USB_WAKEUP_DEBOUNCE_COUNT(x)	(((x) & 0x7) << 16)
+
+#define USB1_LEGACY_CTRL	0x410
+#define   USB1_NO_LEGACY_MODE			(1 << 0)
+#define   USB1_VBUS_SENSE_CTL_MASK		(3 << 1)
+#define   USB1_VBUS_SENSE_CTL_VBUS_WAKEUP	(0 << 1)
+#define   USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \
+						(1 << 1)
+#define   USB1_VBUS_SENSE_CTL_AB_SESS_VLD	(2 << 1)
+#define   USB1_VBUS_SENSE_CTL_A_SESS_VLD	(3 << 1)
+
+#define ULPI_TIMING_CTRL_0	0x424
+#define   ULPI_OUTPUT_PINMUX_BYP	(1 << 10)
+#define   ULPI_CLKOUT_PINMUX_BYP	(1 << 11)
+
+#define ULPI_TIMING_CTRL_1	0x428
+#define   ULPI_DATA_TRIMMER_LOAD	(1 << 0)
+#define   ULPI_DATA_TRIMMER_SEL(x)	(((x) & 0x7) << 1)
+#define   ULPI_STPDIRNXT_TRIMMER_LOAD	(1 << 16)
+#define   ULPI_STPDIRNXT_TRIMMER_SEL(x)	(((x) & 0x7) << 17)
+#define   ULPI_DIR_TRIMMER_LOAD		(1 << 24)
+#define   ULPI_DIR_TRIMMER_SEL(x)	(((x) & 0x7) << 25)
+
+#define UTMIP_PLL_CFG1		0x804
+#define   UTMIP_XTAL_FREQ_COUNT(x)		(((x) & 0xfff) << 0)
+#define   UTMIP_PLLU_ENABLE_DLY_COUNT(x)	(((x) & 0x1f) << 27)
+
+#define UTMIP_XCVR_CFG0		0x808
+#define   UTMIP_XCVR_SETUP(x)			(((x) & 0xf) << 0)
+#define   UTMIP_XCVR_SETUP_MSB(x)		((((x) & 0x70) >> 4) << 22)
+#define   UTMIP_XCVR_LSRSLEW(x)			(((x) & 0x3) << 8)
+#define   UTMIP_XCVR_LSFSLEW(x)			(((x) & 0x3) << 10)
+#define   UTMIP_FORCE_PD_POWERDOWN		(1 << 14)
+#define   UTMIP_FORCE_PD2_POWERDOWN		(1 << 16)
+#define   UTMIP_FORCE_PDZI_POWERDOWN		(1 << 18)
+#define   UTMIP_XCVR_LSBIAS_SEL			(1 << 21)
+#define   UTMIP_XCVR_HSSLEW(x)			(((x) & 0x3) << 4)
+#define   UTMIP_XCVR_HSSLEW_MSB(x)		((((x) & 0x1fc) >> 2) << 25)
+
+#define UTMIP_BIAS_CFG0		0x80c
+#define   UTMIP_OTGPD			(1 << 11)
+#define   UTMIP_BIASPD			(1 << 10)
+#define   UTMIP_HSSQUELCH_LEVEL(x)	(((x) & 0x3) << 0)
+#define   UTMIP_HSDISCON_LEVEL(x)	(((x) & 0x3) << 2)
+#define   UTMIP_HSDISCON_LEVEL_MSB(x)	((((x) & 0x4) >> 2) << 24)
+
+#define UTMIP_HSRX_CFG0		0x810
+#define   UTMIP_ELASTIC_LIMIT(x)	(((x) & 0x1f) << 10)
+#define   UTMIP_IDLE_WAIT(x)		(((x) & 0x1f) << 15)
+
+#define UTMIP_HSRX_CFG1		0x814
+#define   UTMIP_HS_SYNC_START_DLY(x)	(((x) & 0x1f) << 1)
+
+#define UTMIP_TX_CFG0		0x820
+#define   UTMIP_FS_PREABMLE_J		(1 << 19)
+#define   UTMIP_HS_DISCON_DISABLE	(1 << 8)
+
+#define UTMIP_MISC_CFG0		0x824
+#define   UTMIP_DPDM_OBSERVE		(1 << 26)
+#define   UTMIP_DPDM_OBSERVE_SEL(x)	(((x) & 0xf) << 27)
+#define   UTMIP_DPDM_OBSERVE_SEL_FS_J	UTMIP_DPDM_OBSERVE_SEL(0xf)
+#define   UTMIP_DPDM_OBSERVE_SEL_FS_K	UTMIP_DPDM_OBSERVE_SEL(0xe)
+#define   UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
+#define   UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
+#define   UTMIP_SUSPEND_EXIT_ON_EDGE	(1 << 22)
+
+#define UTMIP_MISC_CFG1		0x828
+#define   UTMIP_PLL_ACTIVE_DLY_COUNT(x)	(((x) & 0x1f) << 18)
+#define   UTMIP_PLLU_STABLE_COUNT(x)	(((x) & 0xfff) << 6)
+
+#define UTMIP_DEBOUNCE_CFG0	0x82c
+#define   UTMIP_BIAS_DEBOUNCE_A(x)	(((x) & 0xffff) << 0)
+
+#define UTMIP_BAT_CHRG_CFG0	0x830
+#define   UTMIP_PD_CHRG			(1 << 0)
+
+#define UTMIP_SPARE_CFG0	0x834
+#define   FUSE_SETUP_SEL		(1 << 3)
+
+#define UTMIP_XCVR_CFG1		0x838
+#define   UTMIP_FORCE_PDDISC_POWERDOWN	(1 << 0)
+#define   UTMIP_FORCE_PDCHRP_POWERDOWN	(1 << 2)
+#define   UTMIP_FORCE_PDDR_POWERDOWN	(1 << 4)
+#define   UTMIP_XCVR_TERM_RANGE_ADJ(x)	(((x) & 0xf) << 18)
+
+#define UTMIP_BIAS_CFG1		0x83c
+#define   UTMIP_BIAS_PDTRK_COUNT(x)	(((x) & 0x1f) << 3)
+
+/* For Tegra30 and above only, the address is different in Tegra20 */
+#define USB_USBMODE		0x1f8
+#define   USB_USBMODE_MASK		(3 << 0)
+#define   USB_USBMODE_HOST		(3 << 0)
+#define   USB_USBMODE_DEVICE		(2 << 0)
+
+static DEFINE_SPINLOCK(utmip_pad_lock);
+static int utmip_pad_count;
+
+struct tegra_xtal_freq {
+	int freq;
+	u8 enable_delay;
+	u8 stable_count;
+	u8 active_delay;
+	u8 xtal_freq_count;
+	u16 debounce;
+};
+
+static const struct tegra_xtal_freq tegra_freq_table[] = {
+	{
+		.freq = 12000000,
+		.enable_delay = 0x02,
+		.stable_count = 0x2F,
+		.active_delay = 0x04,
+		.xtal_freq_count = 0x76,
+		.debounce = 0x7530,
+	},
+	{
+		.freq = 13000000,
+		.enable_delay = 0x02,
+		.stable_count = 0x33,
+		.active_delay = 0x05,
+		.xtal_freq_count = 0x7F,
+		.debounce = 0x7EF4,
+	},
+	{
+		.freq = 19200000,
+		.enable_delay = 0x03,
+		.stable_count = 0x4B,
+		.active_delay = 0x06,
+		.xtal_freq_count = 0xBB,
+		.debounce = 0xBB80,
+	},
+	{
+		.freq = 26000000,
+		.enable_delay = 0x04,
+		.stable_count = 0x66,
+		.active_delay = 0x09,
+		.xtal_freq_count = 0xFE,
+		.debounce = 0xFDE8,
+	},
+};
+
+static void set_pts(struct tegra_usb_phy *phy, u8 pts_val)
+{
+	void __iomem *base = phy->regs;
+	unsigned long val;
+
+	if (phy->soc_config->has_hostpc) {
+		val = readl(base + TEGRA_USB_HOSTPC1_DEVLC);
+		val &= ~TEGRA_USB_HOSTPC1_DEVLC_PTS(~0);
+		val |= TEGRA_USB_HOSTPC1_DEVLC_PTS(pts_val);
+		writel(val, base + TEGRA_USB_HOSTPC1_DEVLC);
+	} else {
+		val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS;
+		val &= ~TEGRA_USB_PORTSC1_PTS(~0);
+		val |= TEGRA_USB_PORTSC1_PTS(pts_val);
+		writel(val, base + TEGRA_USB_PORTSC1);
+	}
+}
+
+static void set_phcd(struct tegra_usb_phy *phy, bool enable)
+{
+	void __iomem *base = phy->regs;
+	unsigned long val;
+
+	if (phy->soc_config->has_hostpc) {
+		val = readl(base + TEGRA_USB_HOSTPC1_DEVLC);
+		if (enable)
+			val |= TEGRA_USB_HOSTPC1_DEVLC_PHCD;
+		else
+			val &= ~TEGRA_USB_HOSTPC1_DEVLC_PHCD;
+		writel(val, base + TEGRA_USB_HOSTPC1_DEVLC);
+	} else {
+		val = readl(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS;
+		if (enable)
+			val |= TEGRA_USB_PORTSC1_PHCD;
+		else
+			val &= ~TEGRA_USB_PORTSC1_PHCD;
+		writel(val, base + TEGRA_USB_PORTSC1);
+	}
+}
+
+static int utmip_pad_open(struct tegra_usb_phy *phy)
+{
+	int ret;
+
+	phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads");
+	if (IS_ERR(phy->pad_clk)) {
+		ret = PTR_ERR(phy->pad_clk);
+		dev_err(phy->u_phy.dev,
+			"Failed to get UTMIP pad clock: %d\n", ret);
+		return ret;
+	}
+
+	phy->pad_rst = devm_reset_control_get_optional_shared(
+						phy->u_phy.dev, "utmi-pads");
+	if (IS_ERR(phy->pad_rst)) {
+		ret = PTR_ERR(phy->pad_rst);
+		dev_err(phy->u_phy.dev,
+			"Failed to get UTMI-pads reset: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(phy->pad_clk);
+	if (ret) {
+		dev_err(phy->u_phy.dev,
+			"Failed to enable UTMI-pads clock: %d\n", ret);
+		return ret;
+	}
+
+	spin_lock(&utmip_pad_lock);
+
+	ret = reset_control_deassert(phy->pad_rst);
+	if (ret) {
+		dev_err(phy->u_phy.dev,
+			"Failed to initialize UTMI-pads reset: %d\n", ret);
+		goto unlock;
+	}
+
+	ret = reset_control_assert(phy->pad_rst);
+	if (ret) {
+		dev_err(phy->u_phy.dev,
+			"Failed to assert UTMI-pads reset: %d\n", ret);
+		goto unlock;
+	}
+
+	udelay(1);
+
+	ret = reset_control_deassert(phy->pad_rst);
+	if (ret)
+		dev_err(phy->u_phy.dev,
+			"Failed to deassert UTMI-pads reset: %d\n", ret);
+unlock:
+	spin_unlock(&utmip_pad_lock);
+
+	clk_disable_unprepare(phy->pad_clk);
+
+	return ret;
+}
+
+static int utmip_pad_close(struct tegra_usb_phy *phy)
+{
+	int ret;
+
+	ret = clk_prepare_enable(phy->pad_clk);
+	if (ret) {
+		dev_err(phy->u_phy.dev,
+			"Failed to enable UTMI-pads clock: %d\n", ret);
+		return ret;
+	}
+
+	ret = reset_control_assert(phy->pad_rst);
+	if (ret)
+		dev_err(phy->u_phy.dev,
+			"Failed to assert UTMI-pads reset: %d\n", ret);
+
+	udelay(1);
+
+	clk_disable_unprepare(phy->pad_clk);
+
+	return ret;
+}
+
+static void utmip_pad_power_on(struct tegra_usb_phy *phy)
+{
+	unsigned long val, flags;
+	void __iomem *base = phy->pad_regs;
+	struct tegra_utmip_config *config = phy->config;
+
+	clk_prepare_enable(phy->pad_clk);
+
+	spin_lock_irqsave(&utmip_pad_lock, flags);
+
+	if (utmip_pad_count++ == 0) {
+		val = readl(base + UTMIP_BIAS_CFG0);
+		val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
+
+		if (phy->soc_config->requires_extra_tuning_parameters) {
+			val &= ~(UTMIP_HSSQUELCH_LEVEL(~0) |
+				UTMIP_HSDISCON_LEVEL(~0) |
+				UTMIP_HSDISCON_LEVEL_MSB(~0));
+
+			val |= UTMIP_HSSQUELCH_LEVEL(config->hssquelch_level);
+			val |= UTMIP_HSDISCON_LEVEL(config->hsdiscon_level);
+			val |= UTMIP_HSDISCON_LEVEL_MSB(config->hsdiscon_level);
+		}
+		writel(val, base + UTMIP_BIAS_CFG0);
+	}
+
+	spin_unlock_irqrestore(&utmip_pad_lock, flags);
+
+	clk_disable_unprepare(phy->pad_clk);
+}
+
+static int utmip_pad_power_off(struct tegra_usb_phy *phy)
+{
+	unsigned long val, flags;
+	void __iomem *base = phy->pad_regs;
+
+	if (!utmip_pad_count) {
+		dev_err(phy->u_phy.dev, "UTMIP pad already powered off\n");
+		return -EINVAL;
+	}
+
+	clk_prepare_enable(phy->pad_clk);
+
+	spin_lock_irqsave(&utmip_pad_lock, flags);
+
+	if (--utmip_pad_count == 0) {
+		val = readl(base + UTMIP_BIAS_CFG0);
+		val |= UTMIP_OTGPD | UTMIP_BIASPD;
+		writel(val, base + UTMIP_BIAS_CFG0);
+	}
+
+	spin_unlock_irqrestore(&utmip_pad_lock, flags);
+
+	clk_disable_unprepare(phy->pad_clk);
+
+	return 0;
+}
+
+static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+	u32 tmp;
+
+	return readl_poll_timeout(reg, tmp, (tmp & mask) == result,
+				  2000, 6000);
+}
+
+static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	/*
+	 * The USB driver may have already initiated the phy clock
+	 * disable so wait to see if the clock turns off and if not
+	 * then proceed with gating the clock.
+	 */
+	if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) == 0)
+		return;
+
+	if (phy->is_legacy_phy) {
+		val = readl(base + USB_SUSP_CTRL);
+		val |= USB_SUSP_SET;
+		writel(val, base + USB_SUSP_CTRL);
+
+		udelay(10);
+
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~USB_SUSP_SET;
+		writel(val, base + USB_SUSP_CTRL);
+	} else
+		set_phcd(phy, true);
+
+	if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
+		dev_err(phy->u_phy.dev,
+			"Timeout waiting for PHY to stabilize on disable\n");
+}
+
+static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	/*
+	 * The USB driver may have already initiated the phy clock
+	 * enable so wait to see if the clock turns on and if not
+	 * then proceed with ungating the clock.
+	 */
+	if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+			       USB_PHY_CLK_VALID) == 0)
+		return;
+
+	if (phy->is_legacy_phy) {
+		val = readl(base + USB_SUSP_CTRL);
+		val |= USB_SUSP_CLR;
+		writel(val, base + USB_SUSP_CTRL);
+
+		udelay(10);
+
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~USB_SUSP_CLR;
+		writel(val, base + USB_SUSP_CTRL);
+	} else
+		set_phcd(phy, false);
+
+	if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+						     USB_PHY_CLK_VALID))
+		dev_err(phy->u_phy.dev,
+			"Timeout waiting for PHY to stabilize on enable\n");
+}
+
+static int utmi_phy_power_on(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+	struct tegra_utmip_config *config = phy->config;
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= UTMIP_RESET;
+	writel(val, base + USB_SUSP_CTRL);
+
+	if (phy->is_legacy_phy) {
+		val = readl(base + USB1_LEGACY_CTRL);
+		val |= USB1_NO_LEGACY_MODE;
+		writel(val, base + USB1_LEGACY_CTRL);
+	}
+
+	val = readl(base + UTMIP_TX_CFG0);
+	val |= UTMIP_FS_PREABMLE_J;
+	writel(val, base + UTMIP_TX_CFG0);
+
+	val = readl(base + UTMIP_HSRX_CFG0);
+	val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0));
+	val |= UTMIP_IDLE_WAIT(config->idle_wait_delay);
+	val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit);
+	writel(val, base + UTMIP_HSRX_CFG0);
+
+	val = readl(base + UTMIP_HSRX_CFG1);
+	val &= ~UTMIP_HS_SYNC_START_DLY(~0);
+	val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay);
+	writel(val, base + UTMIP_HSRX_CFG1);
+
+	val = readl(base + UTMIP_DEBOUNCE_CFG0);
+	val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
+	val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce);
+	writel(val, base + UTMIP_DEBOUNCE_CFG0);
+
+	val = readl(base + UTMIP_MISC_CFG0);
+	val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
+	writel(val, base + UTMIP_MISC_CFG0);
+
+	if (!phy->soc_config->utmi_pll_config_in_car_module) {
+		val = readl(base + UTMIP_MISC_CFG1);
+		val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) |
+			UTMIP_PLLU_STABLE_COUNT(~0));
+		val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) |
+			UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count);
+		writel(val, base + UTMIP_MISC_CFG1);
+
+		val = readl(base + UTMIP_PLL_CFG1);
+		val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
+			UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
+		val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
+			UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
+		writel(val, base + UTMIP_PLL_CFG1);
+	}
+
+	if (phy->mode == USB_DR_MODE_PERIPHERAL) {
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV);
+		writel(val, base + USB_SUSP_CTRL);
+
+		val = readl(base + UTMIP_BAT_CHRG_CFG0);
+		val &= ~UTMIP_PD_CHRG;
+		writel(val, base + UTMIP_BAT_CHRG_CFG0);
+	} else {
+		val = readl(base + UTMIP_BAT_CHRG_CFG0);
+		val |= UTMIP_PD_CHRG;
+		writel(val, base + UTMIP_BAT_CHRG_CFG0);
+	}
+
+	utmip_pad_power_on(phy);
+
+	val = readl(base + UTMIP_XCVR_CFG0);
+	val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
+		 UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL |
+		 UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) |
+		 UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0));
+
+	if (!config->xcvr_setup_use_fuses) {
+		val |= UTMIP_XCVR_SETUP(config->xcvr_setup);
+		val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup);
+	}
+	val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
+	val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
+
+	if (phy->soc_config->requires_extra_tuning_parameters) {
+		val &= ~(UTMIP_XCVR_HSSLEW(~0) | UTMIP_XCVR_HSSLEW_MSB(~0));
+		val |= UTMIP_XCVR_HSSLEW(config->xcvr_hsslew);
+		val |= UTMIP_XCVR_HSSLEW_MSB(config->xcvr_hsslew);
+	}
+	writel(val, base + UTMIP_XCVR_CFG0);
+
+	val = readl(base + UTMIP_XCVR_CFG1);
+	val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
+		 UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0));
+	val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj);
+	writel(val, base + UTMIP_XCVR_CFG1);
+
+	val = readl(base + UTMIP_BIAS_CFG1);
+	val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
+	val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
+	writel(val, base + UTMIP_BIAS_CFG1);
+
+	val = readl(base + UTMIP_SPARE_CFG0);
+	if (config->xcvr_setup_use_fuses)
+		val |= FUSE_SETUP_SEL;
+	else
+		val &= ~FUSE_SETUP_SEL;
+	writel(val, base + UTMIP_SPARE_CFG0);
+
+	if (!phy->is_legacy_phy) {
+		val = readl(base + USB_SUSP_CTRL);
+		val |= UTMIP_PHY_ENABLE;
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	val = readl(base + USB_SUSP_CTRL);
+	val &= ~UTMIP_RESET;
+	writel(val, base + USB_SUSP_CTRL);
+
+	if (phy->is_legacy_phy) {
+		val = readl(base + USB1_LEGACY_CTRL);
+		val &= ~USB1_VBUS_SENSE_CTL_MASK;
+		val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD;
+		writel(val, base + USB1_LEGACY_CTRL);
+
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~USB_SUSP_SET;
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	utmi_phy_clk_enable(phy);
+
+	if (phy->soc_config->requires_usbmode_setup) {
+		val = readl(base + USB_USBMODE);
+		val &= ~USB_USBMODE_MASK;
+		if (phy->mode == USB_DR_MODE_HOST)
+			val |= USB_USBMODE_HOST;
+		else
+			val |= USB_USBMODE_DEVICE;
+		writel(val, base + USB_USBMODE);
+	}
+
+	if (!phy->is_legacy_phy)
+		set_pts(phy, 0);
+
+	return 0;
+}
+
+static int utmi_phy_power_off(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	utmi_phy_clk_disable(phy);
+
+	if (phy->mode == USB_DR_MODE_PERIPHERAL) {
+		val = readl(base + USB_SUSP_CTRL);
+		val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
+		val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5);
+		writel(val, base + USB_SUSP_CTRL);
+	}
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= UTMIP_RESET;
+	writel(val, base + USB_SUSP_CTRL);
+
+	val = readl(base + UTMIP_BAT_CHRG_CFG0);
+	val |= UTMIP_PD_CHRG;
+	writel(val, base + UTMIP_BAT_CHRG_CFG0);
+
+	val = readl(base + UTMIP_XCVR_CFG0);
+	val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
+	       UTMIP_FORCE_PDZI_POWERDOWN;
+	writel(val, base + UTMIP_XCVR_CFG0);
+
+	val = readl(base + UTMIP_XCVR_CFG1);
+	val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
+	       UTMIP_FORCE_PDDR_POWERDOWN;
+	writel(val, base + UTMIP_XCVR_CFG1);
+
+	return utmip_pad_power_off(phy);
+}
+
+static void utmi_phy_preresume(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = readl(base + UTMIP_TX_CFG0);
+	val |= UTMIP_HS_DISCON_DISABLE;
+	writel(val, base + UTMIP_TX_CFG0);
+}
+
+static void utmi_phy_postresume(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = readl(base + UTMIP_TX_CFG0);
+	val &= ~UTMIP_HS_DISCON_DISABLE;
+	writel(val, base + UTMIP_TX_CFG0);
+}
+
+static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
+				   enum tegra_usb_phy_port_speed port_speed)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = readl(base + UTMIP_MISC_CFG0);
+	val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
+	if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
+		val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
+	else
+		val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
+	writel(val, base + UTMIP_MISC_CFG0);
+	udelay(1);
+
+	val = readl(base + UTMIP_MISC_CFG0);
+	val |= UTMIP_DPDM_OBSERVE;
+	writel(val, base + UTMIP_MISC_CFG0);
+	udelay(10);
+}
+
+static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
+{
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	val = readl(base + UTMIP_MISC_CFG0);
+	val &= ~UTMIP_DPDM_OBSERVE;
+	writel(val, base + UTMIP_MISC_CFG0);
+	udelay(10);
+}
+
+static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
+{
+	int ret;
+	unsigned long val;
+	void __iomem *base = phy->regs;
+
+	ret = gpio_direction_output(phy->reset_gpio, 0);
+	if (ret < 0) {
+		dev_err(phy->u_phy.dev, "GPIO %d not set to 0: %d\n",
+			phy->reset_gpio, ret);
+		return ret;
+	}
+	msleep(5);
+	ret = gpio_direction_output(phy->reset_gpio, 1);
+	if (ret < 0) {
+		dev_err(phy->u_phy.dev, "GPIO %d not set to 1: %d\n",
+			phy->reset_gpio, ret);
+		return ret;
+	}
+
+	clk_prepare_enable(phy->clk);
+	msleep(1);
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= UHSIC_RESET;
+	writel(val, base + USB_SUSP_CTRL);
+
+	val = readl(base + ULPI_TIMING_CTRL_0);
+	val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
+	writel(val, base + ULPI_TIMING_CTRL_0);
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= ULPI_PHY_ENABLE;
+	writel(val, base + USB_SUSP_CTRL);
+
+	val = 0;
+	writel(val, base + ULPI_TIMING_CTRL_1);
+
+	val |= ULPI_DATA_TRIMMER_SEL(4);
+	val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
+	val |= ULPI_DIR_TRIMMER_SEL(4);
+	writel(val, base + ULPI_TIMING_CTRL_1);
+	udelay(10);
+
+	val |= ULPI_DATA_TRIMMER_LOAD;
+	val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+	val |= ULPI_DIR_TRIMMER_LOAD;
+	writel(val, base + ULPI_TIMING_CTRL_1);
+
+	/* Fix VbusInvalid due to floating VBUS */
+	ret = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
+	if (ret) {
+		dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
+	if (ret) {
+		dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", ret);
+		return ret;
+	}
+
+	val = readl(base + USB_SUSP_CTRL);
+	val |= USB_SUSP_CLR;
+	writel(val, base + USB_SUSP_CTRL);
+	udelay(100);
+
+	val = readl(base + USB_SUSP_CTRL);
+	val &= ~USB_SUSP_CLR;
+	writel(val, base + USB_SUSP_CTRL);
+
+	return 0;
+}
+
+static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
+{
+	clk_disable(phy->clk);
+	return gpio_direction_output(phy->reset_gpio, 0);
+}
+
+static void tegra_usb_phy_close(struct tegra_usb_phy *phy)
+{
+	if (!IS_ERR(phy->vbus))
+		regulator_disable(phy->vbus);
+
+	if (!phy->is_ulpi_phy)
+		utmip_pad_close(phy);
+
+	clk_disable_unprepare(phy->pll_u);
+}
+
+static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
+{
+	if (phy->is_ulpi_phy)
+		return ulpi_phy_power_on(phy);
+	else
+		return utmi_phy_power_on(phy);
+}
+
+static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
+{
+	if (phy->is_ulpi_phy)
+		return ulpi_phy_power_off(phy);
+	else
+		return utmi_phy_power_off(phy);
+}
+
+static int	tegra_usb_phy_suspend(struct usb_phy *x, int suspend)
+{
+	struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+	if (suspend)
+		return tegra_usb_phy_power_off(phy);
+	else
+		return tegra_usb_phy_power_on(phy);
+}
+
+static int ulpi_open(struct tegra_usb_phy *phy)
+{
+	int err;
+
+	phy->clk = devm_clk_get(phy->u_phy.dev, "ulpi-link");
+	if (IS_ERR(phy->clk)) {
+		err = PTR_ERR(phy->clk);
+		dev_err(phy->u_phy.dev, "Failed to get ULPI clock: %d\n", err);
+		return err;
+	}
+
+	err = devm_gpio_request(phy->u_phy.dev, phy->reset_gpio,
+		"ulpi_phy_reset_b");
+	if (err < 0) {
+		dev_err(phy->u_phy.dev, "Request failed for GPIO %d: %d\n",
+			phy->reset_gpio, err);
+		return err;
+	}
+
+	err = gpio_direction_output(phy->reset_gpio, 0);
+	if (err < 0) {
+		dev_err(phy->u_phy.dev,
+			"GPIO %d direction not set to output: %d\n",
+			phy->reset_gpio, err);
+		return err;
+	}
+
+	phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
+	if (!phy->ulpi) {
+		dev_err(phy->u_phy.dev, "Failed to create ULPI OTG\n");
+		err = -ENOMEM;
+		return err;
+	}
+
+	phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT;
+	return 0;
+}
+
+static int tegra_usb_phy_init(struct tegra_usb_phy *phy)
+{
+	unsigned long parent_rate;
+	int i;
+	int err;
+
+	phy->pll_u = devm_clk_get(phy->u_phy.dev, "pll_u");
+	if (IS_ERR(phy->pll_u)) {
+		err = PTR_ERR(phy->pll_u);
+		dev_err(phy->u_phy.dev,
+			"Failed to get pll_u clock: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(phy->pll_u);
+	if (err)
+		return err;
+
+	parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
+	for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) {
+		if (tegra_freq_table[i].freq == parent_rate) {
+			phy->freq = &tegra_freq_table[i];
+			break;
+		}
+	}
+	if (!phy->freq) {
+		dev_err(phy->u_phy.dev, "Invalid pll_u parent rate %ld\n",
+			parent_rate);
+		err = -EINVAL;
+		goto fail;
+	}
+
+	if (!IS_ERR(phy->vbus)) {
+		err = regulator_enable(phy->vbus);
+		if (err) {
+			dev_err(phy->u_phy.dev,
+				"Failed to enable USB VBUS regulator: %d\n",
+				err);
+			goto fail;
+		}
+	}
+
+	if (phy->is_ulpi_phy)
+		err = ulpi_open(phy);
+	else
+		err = utmip_pad_open(phy);
+	if (err < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	clk_disable_unprepare(phy->pll_u);
+	return err;
+}
+
+void tegra_usb_phy_preresume(struct usb_phy *x)
+{
+	struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+
+	if (!phy->is_ulpi_phy)
+		utmi_phy_preresume(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume);
+
+void tegra_usb_phy_postresume(struct usb_phy *x)
+{
+	struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+
+	if (!phy->is_ulpi_phy)
+		utmi_phy_postresume(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume);
+
+void tegra_ehci_phy_restore_start(struct usb_phy *x,
+				 enum tegra_usb_phy_port_speed port_speed)
+{
+	struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+
+	if (!phy->is_ulpi_phy)
+		utmi_phy_restore_start(phy, port_speed);
+}
+EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start);
+
+void tegra_ehci_phy_restore_end(struct usb_phy *x)
+{
+	struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+
+	if (!phy->is_ulpi_phy)
+		utmi_phy_restore_end(phy);
+}
+EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end);
+
+static int read_utmi_param(struct platform_device *pdev, const char *param,
+			   u8 *dest)
+{
+	u32 value;
+	int err = of_property_read_u32(pdev->dev.of_node, param, &value);
+	*dest = (u8)value;
+	if (err < 0)
+		dev_err(&pdev->dev,
+			"Failed to read USB UTMI parameter %s: %d\n",
+			param, err);
+	return err;
+}
+
+static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
+			  struct platform_device *pdev)
+{
+	struct resource *res;
+	int err;
+	struct tegra_utmip_config *config;
+
+	tegra_phy->is_ulpi_phy = false;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get UTMI pad regs\n");
+		return  -ENXIO;
+	}
+
+	tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start,
+		resource_size(res));
+	if (!tegra_phy->pad_regs) {
+		dev_err(&pdev->dev, "Failed to remap UTMI pad regs\n");
+		return -ENOMEM;
+	}
+
+	tegra_phy->config = devm_kzalloc(&pdev->dev, sizeof(*config),
+					 GFP_KERNEL);
+	if (!tegra_phy->config)
+		return -ENOMEM;
+
+	config = tegra_phy->config;
+
+	err = read_utmi_param(pdev, "nvidia,hssync-start-delay",
+		&config->hssync_start_delay);
+	if (err < 0)
+		return err;
+
+	err = read_utmi_param(pdev, "nvidia,elastic-limit",
+		&config->elastic_limit);
+	if (err < 0)
+		return err;
+
+	err = read_utmi_param(pdev, "nvidia,idle-wait-delay",
+		&config->idle_wait_delay);
+	if (err < 0)
+		return err;
+
+	err = read_utmi_param(pdev, "nvidia,term-range-adj",
+		&config->term_range_adj);
+	if (err < 0)
+		return err;
+
+	err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew",
+		&config->xcvr_lsfslew);
+	if (err < 0)
+		return err;
+
+	err = read_utmi_param(pdev, "nvidia,xcvr-lsrslew",
+		&config->xcvr_lsrslew);
+	if (err < 0)
+		return err;
+
+	if (tegra_phy->soc_config->requires_extra_tuning_parameters) {
+		err = read_utmi_param(pdev, "nvidia,xcvr-hsslew",
+			&config->xcvr_hsslew);
+		if (err < 0)
+			return err;
+
+		err = read_utmi_param(pdev, "nvidia,hssquelch-level",
+			&config->hssquelch_level);
+		if (err < 0)
+			return err;
+
+		err = read_utmi_param(pdev, "nvidia,hsdiscon-level",
+			&config->hsdiscon_level);
+		if (err < 0)
+			return err;
+	}
+
+	config->xcvr_setup_use_fuses = of_property_read_bool(
+		pdev->dev.of_node, "nvidia,xcvr-setup-use-fuses");
+
+	if (!config->xcvr_setup_use_fuses) {
+		err = read_utmi_param(pdev, "nvidia,xcvr-setup",
+			&config->xcvr_setup);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static const struct tegra_phy_soc_config tegra20_soc_config = {
+	.utmi_pll_config_in_car_module = false,
+	.has_hostpc = false,
+	.requires_usbmode_setup = false,
+	.requires_extra_tuning_parameters = false,
+};
+
+static const struct tegra_phy_soc_config tegra30_soc_config = {
+	.utmi_pll_config_in_car_module = true,
+	.has_hostpc = true,
+	.requires_usbmode_setup = true,
+	.requires_extra_tuning_parameters = true,
+};
+
+static const struct of_device_id tegra_usb_phy_id_table[] = {
+	{ .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config },
+	{ .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table);
+
+static int tegra_usb_phy_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct resource *res;
+	struct tegra_usb_phy *tegra_phy = NULL;
+	struct device_node *np = pdev->dev.of_node;
+	enum usb_phy_interface phy_type;
+	int err;
+
+	tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL);
+	if (!tegra_phy)
+		return -ENOMEM;
+
+	match = of_match_device(tegra_usb_phy_id_table, &pdev->dev);
+	if (!match) {
+		dev_err(&pdev->dev, "Error: No device match found\n");
+		return -ENODEV;
+	}
+	tegra_phy->soc_config = match->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get I/O memory\n");
+		return  -ENXIO;
+	}
+
+	tegra_phy->regs = devm_ioremap(&pdev->dev, res->start,
+		resource_size(res));
+	if (!tegra_phy->regs) {
+		dev_err(&pdev->dev, "Failed to remap I/O memory\n");
+		return -ENOMEM;
+	}
+
+	tegra_phy->is_legacy_phy =
+		of_property_read_bool(np, "nvidia,has-legacy-mode");
+
+	phy_type = of_usb_get_phy_mode(np);
+	switch (phy_type) {
+	case USBPHY_INTERFACE_MODE_UTMI:
+		err = utmi_phy_probe(tegra_phy, pdev);
+		if (err < 0)
+			return err;
+		break;
+
+	case USBPHY_INTERFACE_MODE_ULPI:
+		tegra_phy->is_ulpi_phy = true;
+
+		tegra_phy->reset_gpio =
+			of_get_named_gpio(np, "nvidia,phy-reset-gpio", 0);
+		if (!gpio_is_valid(tegra_phy->reset_gpio)) {
+			dev_err(&pdev->dev,
+				"Invalid GPIO: %d\n", tegra_phy->reset_gpio);
+			return tegra_phy->reset_gpio;
+		}
+		tegra_phy->config = NULL;
+		break;
+
+	default:
+		dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
+			phy_type);
+		return -EINVAL;
+	}
+
+	if (of_find_property(np, "dr_mode", NULL))
+		tegra_phy->mode = usb_get_dr_mode(&pdev->dev);
+	else
+		tegra_phy->mode = USB_DR_MODE_HOST;
+
+	if (tegra_phy->mode == USB_DR_MODE_UNKNOWN) {
+		dev_err(&pdev->dev, "dr_mode is invalid\n");
+		return -EINVAL;
+	}
+
+	/* On some boards, the VBUS regulator doesn't need to be controlled */
+	if (of_find_property(np, "vbus-supply", NULL)) {
+		tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus");
+		if (IS_ERR(tegra_phy->vbus))
+			return PTR_ERR(tegra_phy->vbus);
+	} else {
+		dev_notice(&pdev->dev, "no vbus regulator");
+		tegra_phy->vbus = ERR_PTR(-ENODEV);
+	}
+
+	tegra_phy->u_phy.dev = &pdev->dev;
+	err = tegra_usb_phy_init(tegra_phy);
+	if (err < 0)
+		return err;
+
+	tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend;
+
+	platform_set_drvdata(pdev, tegra_phy);
+
+	err = usb_add_phy_dev(&tegra_phy->u_phy);
+	if (err < 0) {
+		tegra_usb_phy_close(tegra_phy);
+		return err;
+	}
+
+	return 0;
+}
+
+static int tegra_usb_phy_remove(struct platform_device *pdev)
+{
+	struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev);
+
+	usb_remove_phy(&tegra_phy->u_phy);
+	tegra_usb_phy_close(tegra_phy);
+
+	return 0;
+}
+
+static struct platform_driver tegra_usb_phy_driver = {
+	.probe		= tegra_usb_phy_probe,
+	.remove		= tegra_usb_phy_remove,
+	.driver		= {
+		.name	= "tegra-phy",
+		.of_match_table = tegra_usb_phy_id_table,
+	},
+};
+module_platform_driver(tegra_usb_phy_driver);
+
+MODULE_DESCRIPTION("Tegra USB PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/marvell/linux/drivers/usb/phy/phy-twl6030-usb.c b/marvell/linux/drivers/usb/phy/phy-twl6030-usb.c
new file mode 100644
index 0000000..9337c30
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-twl6030-usb.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver.
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Hema HK <hemahk@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/usb/musb.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/phy/omap_usb.h>
+#include <linux/mfd/twl.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+
+/* usb register definitions */
+#define USB_VENDOR_ID_LSB		0x00
+#define USB_VENDOR_ID_MSB		0x01
+#define USB_PRODUCT_ID_LSB		0x02
+#define USB_PRODUCT_ID_MSB		0x03
+#define USB_VBUS_CTRL_SET		0x04
+#define USB_VBUS_CTRL_CLR		0x05
+#define USB_ID_CTRL_SET			0x06
+#define USB_ID_CTRL_CLR			0x07
+#define USB_VBUS_INT_SRC		0x08
+#define USB_VBUS_INT_LATCH_SET		0x09
+#define USB_VBUS_INT_LATCH_CLR		0x0A
+#define USB_VBUS_INT_EN_LO_SET		0x0B
+#define USB_VBUS_INT_EN_LO_CLR		0x0C
+#define USB_VBUS_INT_EN_HI_SET		0x0D
+#define USB_VBUS_INT_EN_HI_CLR		0x0E
+#define USB_ID_INT_SRC			0x0F
+#define USB_ID_INT_LATCH_SET		0x10
+#define USB_ID_INT_LATCH_CLR		0x11
+
+#define USB_ID_INT_EN_LO_SET		0x12
+#define USB_ID_INT_EN_LO_CLR		0x13
+#define USB_ID_INT_EN_HI_SET		0x14
+#define USB_ID_INT_EN_HI_CLR		0x15
+#define USB_OTG_ADP_CTRL		0x16
+#define USB_OTG_ADP_HIGH		0x17
+#define USB_OTG_ADP_LOW			0x18
+#define USB_OTG_ADP_RISE		0x19
+#define USB_OTG_REVISION		0x1A
+
+/* to be moved to LDO */
+#define TWL6030_MISC2			0xE5
+#define TWL6030_CFG_LDO_PD2		0xF5
+#define TWL6030_BACKUP_REG		0xFA
+
+#define STS_HW_CONDITIONS		0x21
+
+/* In module TWL6030_MODULE_PM_MASTER */
+#define STS_HW_CONDITIONS		0x21
+#define STS_USB_ID			BIT(2)
+
+/* In module TWL6030_MODULE_PM_RECEIVER */
+#define VUSB_CFG_TRANS			0x71
+#define VUSB_CFG_STATE			0x72
+#define VUSB_CFG_VOLTAGE		0x73
+
+/* in module TWL6030_MODULE_MAIN_CHARGE */
+
+#define CHARGERUSB_CTRL1		0x8
+
+#define CONTROLLER_STAT1		0x03
+#define	VBUS_DET			BIT(2)
+
+struct twl6030_usb {
+	struct phy_companion	comparator;
+	struct device		*dev;
+
+	/* for vbus reporting with irqs disabled */
+	spinlock_t		lock;
+
+	struct regulator		*usb3v3;
+
+	/* used to check initial cable status after probe */
+	struct delayed_work	get_status_work;
+
+	/* used to set vbus, in atomic path */
+	struct work_struct	set_vbus_work;
+
+	int			irq1;
+	int			irq2;
+	enum musb_vbus_id_status linkstat;
+	u8			asleep;
+	bool			vbus_enable;
+};
+
+#define	comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator)
+
+/*-------------------------------------------------------------------------*/
+
+static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module,
+						u8 data, u8 address)
+{
+	int ret = 0;
+
+	ret = twl_i2c_write_u8(module, data, address);
+	if (ret < 0)
+		dev_err(twl->dev,
+			"Write[0x%x] Error %d\n", address, ret);
+	return ret;
+}
+
+static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
+{
+	u8 data;
+	int ret;
+
+	ret = twl_i2c_read_u8(module, &data, address);
+	if (ret >= 0)
+		ret = data;
+	else
+		dev_err(twl->dev,
+			"readb[0x%x,0x%x] Error %d\n",
+					module, address, ret);
+	return ret;
+}
+
+static int twl6030_start_srp(struct phy_companion *comparator)
+{
+	struct twl6030_usb *twl = comparator_to_twl(comparator);
+
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET);
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET);
+
+	mdelay(100);
+	twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR);
+
+	return 0;
+}
+
+static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
+{
+	/* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */
+	twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x1, TWL6030_BACKUP_REG);
+
+	/* Program CFG_LDO_PD2 register and set VUSB bit */
+	twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x1, TWL6030_CFG_LDO_PD2);
+
+	/* Program MISC2 register and set bit VUSB_IN_VBAT */
+	twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x10, TWL6030_MISC2);
+
+	twl->usb3v3 = regulator_get(twl->dev, "usb");
+	if (IS_ERR(twl->usb3v3))
+		return -ENODEV;
+
+	/* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET);
+
+	/*
+	 * Program the USB_ID_CTRL_SET register to enable GND drive
+	 * and the ID comparators
+	 */
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET);
+
+	return 0;
+}
+
+static ssize_t vbus_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct twl6030_usb *twl = dev_get_drvdata(dev);
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	spin_lock_irqsave(&twl->lock, flags);
+
+	switch (twl->linkstat) {
+	case MUSB_VBUS_VALID:
+	       ret = snprintf(buf, PAGE_SIZE, "vbus\n");
+	       break;
+	case MUSB_ID_GROUND:
+	       ret = snprintf(buf, PAGE_SIZE, "id\n");
+	       break;
+	case MUSB_VBUS_OFF:
+	       ret = snprintf(buf, PAGE_SIZE, "none\n");
+	       break;
+	default:
+	       ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
+	}
+	spin_unlock_irqrestore(&twl->lock, flags);
+
+	return ret;
+}
+static DEVICE_ATTR_RO(vbus);
+
+static struct attribute *twl6030_attrs[] = {
+	&dev_attr_vbus.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(twl6030);
+
+static irqreturn_t twl6030_usb_irq(int irq, void *_twl)
+{
+	struct twl6030_usb *twl = _twl;
+	enum musb_vbus_id_status status = MUSB_UNKNOWN;
+	u8 vbus_state, hw_state;
+	int ret;
+
+	hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
+
+	vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE,
+						CONTROLLER_STAT1);
+	if (!(hw_state & STS_USB_ID)) {
+		if (vbus_state & VBUS_DET) {
+			ret = regulator_enable(twl->usb3v3);
+			if (ret)
+				dev_err(twl->dev, "Failed to enable usb3v3\n");
+
+			twl->asleep = 1;
+			status = MUSB_VBUS_VALID;
+			twl->linkstat = status;
+			ret = musb_mailbox(status);
+			if (ret)
+				twl->linkstat = MUSB_UNKNOWN;
+		} else {
+			if (twl->linkstat != MUSB_UNKNOWN) {
+				status = MUSB_VBUS_OFF;
+				twl->linkstat = status;
+				ret = musb_mailbox(status);
+				if (ret)
+					twl->linkstat = MUSB_UNKNOWN;
+				if (twl->asleep) {
+					regulator_disable(twl->usb3v3);
+					twl->asleep = 0;
+				}
+			}
+		}
+	}
+	sysfs_notify(&twl->dev->kobj, NULL, "vbus");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
+{
+	struct twl6030_usb *twl = _twl;
+	enum musb_vbus_id_status status = MUSB_UNKNOWN;
+	u8 hw_state;
+	int ret;
+
+	hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS);
+
+	if (hw_state & STS_USB_ID) {
+		ret = regulator_enable(twl->usb3v3);
+		if (ret)
+			dev_err(twl->dev, "Failed to enable usb3v3\n");
+
+		twl->asleep = 1;
+		twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR);
+		twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET);
+		status = MUSB_ID_GROUND;
+		twl->linkstat = status;
+		ret = musb_mailbox(status);
+		if (ret)
+			twl->linkstat = MUSB_UNKNOWN;
+	} else  {
+		twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR);
+		twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET);
+	}
+	twl6030_writeb(twl, TWL_MODULE_USB, status, USB_ID_INT_LATCH_CLR);
+
+	return IRQ_HANDLED;
+}
+
+static void twl6030_status_work(struct work_struct *work)
+{
+	struct twl6030_usb *twl = container_of(work, struct twl6030_usb,
+					       get_status_work.work);
+
+	twl6030_usb_irq(twl->irq2, twl);
+	twl6030_usbotg_irq(twl->irq1, twl);
+}
+
+static int twl6030_enable_irq(struct twl6030_usb *twl)
+{
+	twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET);
+	twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C);
+	twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C);
+
+	twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
+				REG_INT_MSK_LINE_C);
+	twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
+				REG_INT_MSK_STS_C);
+
+	return 0;
+}
+
+static void otg_set_vbus_work(struct work_struct *data)
+{
+	struct twl6030_usb *twl = container_of(data, struct twl6030_usb,
+								set_vbus_work);
+
+	/*
+	 * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1
+	 * register. This enables boost mode.
+	 */
+
+	if (twl->vbus_enable)
+		twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE, 0x40,
+							CHARGERUSB_CTRL1);
+	else
+		twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE, 0x00,
+							CHARGERUSB_CTRL1);
+}
+
+static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled)
+{
+	struct twl6030_usb *twl = comparator_to_twl(comparator);
+
+	twl->vbus_enable = enabled;
+	schedule_work(&twl->set_vbus_work);
+
+	return 0;
+}
+
+static int twl6030_usb_probe(struct platform_device *pdev)
+{
+	u32 ret;
+	struct twl6030_usb	*twl;
+	int			status, err;
+	struct device_node	*np = pdev->dev.of_node;
+	struct device		*dev = &pdev->dev;
+
+	if (!np) {
+		dev_err(dev, "no DT info\n");
+		return -EINVAL;
+	}
+
+	twl = devm_kzalloc(dev, sizeof(*twl), GFP_KERNEL);
+	if (!twl)
+		return -ENOMEM;
+
+	twl->dev		= &pdev->dev;
+	twl->irq1		= platform_get_irq(pdev, 0);
+	twl->irq2		= platform_get_irq(pdev, 1);
+	twl->linkstat		= MUSB_UNKNOWN;
+
+	if (twl->irq1 < 0)
+		return twl->irq1;
+	if (twl->irq2 < 0)
+		return twl->irq2;
+
+	twl->comparator.set_vbus	= twl6030_set_vbus;
+	twl->comparator.start_srp	= twl6030_start_srp;
+
+	ret = omap_usb2_set_comparator(&twl->comparator);
+	if (ret == -ENODEV) {
+		dev_info(&pdev->dev, "phy not ready, deferring probe");
+		return -EPROBE_DEFER;
+	}
+
+	/* init spinlock for workqueue */
+	spin_lock_init(&twl->lock);
+
+	err = twl6030_usb_ldo_init(twl);
+	if (err) {
+		dev_err(&pdev->dev, "ldo init failed\n");
+		return err;
+	}
+
+	platform_set_drvdata(pdev, twl);
+
+	INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work);
+	INIT_DELAYED_WORK(&twl->get_status_work, twl6030_status_work);
+
+	status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+			"twl6030_usb", twl);
+	if (status < 0) {
+		dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
+			twl->irq1, status);
+		goto err_put_regulator;
+	}
+
+	status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq,
+			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+			"twl6030_usb", twl);
+	if (status < 0) {
+		dev_err(&pdev->dev, "can't get IRQ %d, err %d\n",
+			twl->irq2, status);
+		goto err_free_irq1;
+	}
+
+	twl->asleep = 0;
+	twl6030_enable_irq(twl);
+	schedule_delayed_work(&twl->get_status_work, HZ);
+	dev_info(&pdev->dev, "Initialized TWL6030 USB module\n");
+
+	return 0;
+
+err_free_irq1:
+	free_irq(twl->irq1, twl);
+err_put_regulator:
+	regulator_put(twl->usb3v3);
+
+	return status;
+}
+
+static int twl6030_usb_remove(struct platform_device *pdev)
+{
+	struct twl6030_usb *twl = platform_get_drvdata(pdev);
+
+	cancel_delayed_work_sync(&twl->get_status_work);
+	twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
+		REG_INT_MSK_LINE_C);
+	twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
+			REG_INT_MSK_STS_C);
+	free_irq(twl->irq1, twl);
+	free_irq(twl->irq2, twl);
+	regulator_put(twl->usb3v3);
+	cancel_work_sync(&twl->set_vbus_work);
+
+	return 0;
+}
+
+static const struct of_device_id twl6030_usb_id_table[] = {
+	{ .compatible = "ti,twl6030-usb" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, twl6030_usb_id_table);
+
+static struct platform_driver twl6030_usb_driver = {
+	.probe		= twl6030_usb_probe,
+	.remove		= twl6030_usb_remove,
+	.driver		= {
+		.name	= "twl6030_usb",
+		.of_match_table = of_match_ptr(twl6030_usb_id_table),
+		.dev_groups = twl6030_groups,
+	},
+};
+
+static int __init twl6030_usb_init(void)
+{
+	return platform_driver_register(&twl6030_usb_driver);
+}
+subsys_initcall(twl6030_usb_init);
+
+static void __exit twl6030_usb_exit(void)
+{
+	platform_driver_unregister(&twl6030_usb_driver);
+}
+module_exit(twl6030_usb_exit);
+
+MODULE_ALIAS("platform:twl6030_usb");
+MODULE_AUTHOR("Hema HK <hemahk@ti.com>");
+MODULE_DESCRIPTION("TWL6030 USB transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/marvell/linux/drivers/usb/phy/phy-ulpi-viewport.c b/marvell/linux/drivers/usb/phy/phy-ulpi-viewport.c
new file mode 100644
index 0000000..7a14e0e
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-ulpi-viewport.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2011 Google, Inc.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+
+#define ULPI_VIEW_WAKEUP	(1 << 31)
+#define ULPI_VIEW_RUN		(1 << 30)
+#define ULPI_VIEW_WRITE		(1 << 29)
+#define ULPI_VIEW_READ		(0 << 29)
+#define ULPI_VIEW_ADDR(x)	(((x) & 0xff) << 16)
+#define ULPI_VIEW_DATA_READ(x)	(((x) >> 8) & 0xff)
+#define ULPI_VIEW_DATA_WRITE(x)	((x) & 0xff)
+
+static int ulpi_viewport_wait(void __iomem *view, u32 mask)
+{
+	unsigned long usec = 2000;
+
+	while (usec--) {
+		if (!(readl(view) & mask))
+			return 0;
+
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int ulpi_viewport_read(struct usb_phy *otg, u32 reg)
+{
+	int ret;
+	void __iomem *view = otg->io_priv;
+
+	writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view);
+	ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP);
+	if (ret)
+		return ret;
+
+	writel(ULPI_VIEW_RUN | ULPI_VIEW_READ | ULPI_VIEW_ADDR(reg), view);
+	ret = ulpi_viewport_wait(view, ULPI_VIEW_RUN);
+	if (ret)
+		return ret;
+
+	return ULPI_VIEW_DATA_READ(readl(view));
+}
+
+static int ulpi_viewport_write(struct usb_phy *otg, u32 val, u32 reg)
+{
+	int ret;
+	void __iomem *view = otg->io_priv;
+
+	writel(ULPI_VIEW_WAKEUP | ULPI_VIEW_WRITE, view);
+	ret = ulpi_viewport_wait(view, ULPI_VIEW_WAKEUP);
+	if (ret)
+		return ret;
+
+	writel(ULPI_VIEW_RUN | ULPI_VIEW_WRITE | ULPI_VIEW_DATA_WRITE(val) |
+						 ULPI_VIEW_ADDR(reg), view);
+
+	return ulpi_viewport_wait(view, ULPI_VIEW_RUN);
+}
+
+struct usb_phy_io_ops ulpi_viewport_access_ops = {
+	.read	= ulpi_viewport_read,
+	.write	= ulpi_viewport_write,
+};
+EXPORT_SYMBOL_GPL(ulpi_viewport_access_ops);
diff --git a/marvell/linux/drivers/usb/phy/phy-ulpi.c b/marvell/linux/drivers/usb/phy/phy-ulpi.c
new file mode 100644
index 0000000..a43c493
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy-ulpi.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Generic ULPI USB transceiver support
+ *
+ * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * Based on sources from
+ *
+ *   Sascha Hauer <s.hauer@pengutronix.de>
+ *   Freescale Semiconductors
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/usb.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/ulpi.h>
+
+
+struct ulpi_info {
+	unsigned int	id;
+	char		*name;
+};
+
+#define ULPI_ID(vendor, product) (((vendor) << 16) | (product))
+#define ULPI_INFO(_id, _name)		\
+	{				\
+		.id	= (_id),	\
+		.name	= (_name),	\
+	}
+
+/* ULPI hardcoded IDs, used for probing */
+static struct ulpi_info ulpi_ids[] = {
+	ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
+	ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
+	ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
+	ULPI_INFO(ULPI_ID(0x0424, 0x0009), "SMSC USB334x"),
+	ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
+};
+
+static int ulpi_set_otg_flags(struct usb_phy *phy)
+{
+	unsigned int flags = ULPI_OTG_CTRL_DP_PULLDOWN |
+			     ULPI_OTG_CTRL_DM_PULLDOWN;
+
+	if (phy->flags & ULPI_OTG_ID_PULLUP)
+		flags |= ULPI_OTG_CTRL_ID_PULLUP;
+
+	/*
+	 * ULPI Specification rev.1.1 default
+	 * for Dp/DmPulldown is enabled.
+	 */
+	if (phy->flags & ULPI_OTG_DP_PULLDOWN_DIS)
+		flags &= ~ULPI_OTG_CTRL_DP_PULLDOWN;
+
+	if (phy->flags & ULPI_OTG_DM_PULLDOWN_DIS)
+		flags &= ~ULPI_OTG_CTRL_DM_PULLDOWN;
+
+	if (phy->flags & ULPI_OTG_EXTVBUSIND)
+		flags |= ULPI_OTG_CTRL_EXTVBUSIND;
+
+	return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
+}
+
+static int ulpi_set_fc_flags(struct usb_phy *phy)
+{
+	unsigned int flags = 0;
+
+	/*
+	 * ULPI Specification rev.1.1 default
+	 * for XcvrSelect is Full Speed.
+	 */
+	if (phy->flags & ULPI_FC_HS)
+		flags |= ULPI_FUNC_CTRL_HIGH_SPEED;
+	else if (phy->flags & ULPI_FC_LS)
+		flags |= ULPI_FUNC_CTRL_LOW_SPEED;
+	else if (phy->flags & ULPI_FC_FS4LS)
+		flags |= ULPI_FUNC_CTRL_FS4LS;
+	else
+		flags |= ULPI_FUNC_CTRL_FULL_SPEED;
+
+	if (phy->flags & ULPI_FC_TERMSEL)
+		flags |= ULPI_FUNC_CTRL_TERMSELECT;
+
+	/*
+	 * ULPI Specification rev.1.1 default
+	 * for OpMode is Normal Operation.
+	 */
+	if (phy->flags & ULPI_FC_OP_NODRV)
+		flags |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
+	else if (phy->flags & ULPI_FC_OP_DIS_NRZI)
+		flags |= ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI;
+	else if (phy->flags & ULPI_FC_OP_NSYNC_NEOP)
+		flags |= ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP;
+	else
+		flags |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
+
+	/*
+	 * ULPI Specification rev.1.1 default
+	 * for SuspendM is Powered.
+	 */
+	flags |= ULPI_FUNC_CTRL_SUSPENDM;
+
+	return usb_phy_io_write(phy, flags, ULPI_FUNC_CTRL);
+}
+
+static int ulpi_set_ic_flags(struct usb_phy *phy)
+{
+	unsigned int flags = 0;
+
+	if (phy->flags & ULPI_IC_AUTORESUME)
+		flags |= ULPI_IFC_CTRL_AUTORESUME;
+
+	if (phy->flags & ULPI_IC_EXTVBUS_INDINV)
+		flags |= ULPI_IFC_CTRL_EXTERNAL_VBUS;
+
+	if (phy->flags & ULPI_IC_IND_PASSTHRU)
+		flags |= ULPI_IFC_CTRL_PASSTHRU;
+
+	if (phy->flags & ULPI_IC_PROTECT_DIS)
+		flags |= ULPI_IFC_CTRL_PROTECT_IFC_DISABLE;
+
+	return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
+}
+
+static int ulpi_set_flags(struct usb_phy *phy)
+{
+	int ret;
+
+	ret = ulpi_set_otg_flags(phy);
+	if (ret)
+		return ret;
+
+	ret = ulpi_set_ic_flags(phy);
+	if (ret)
+		return ret;
+
+	return ulpi_set_fc_flags(phy);
+}
+
+static int ulpi_check_integrity(struct usb_phy *phy)
+{
+	int ret, i;
+	unsigned int val = 0x55;
+
+	for (i = 0; i < 2; i++) {
+		ret = usb_phy_io_write(phy, val, ULPI_SCRATCH);
+		if (ret < 0)
+			return ret;
+
+		ret = usb_phy_io_read(phy, ULPI_SCRATCH);
+		if (ret < 0)
+			return ret;
+
+		if (ret != val) {
+			pr_err("ULPI integrity check: failed!");
+			return -ENODEV;
+		}
+		val = val << 1;
+	}
+
+	pr_info("ULPI integrity check: passed.\n");
+
+	return 0;
+}
+
+static int ulpi_init(struct usb_phy *phy)
+{
+	int i, vid, pid, ret;
+	u32 ulpi_id = 0;
+
+	for (i = 0; i < 4; i++) {
+		ret = usb_phy_io_read(phy, ULPI_PRODUCT_ID_HIGH - i);
+		if (ret < 0)
+			return ret;
+		ulpi_id = (ulpi_id << 8) | ret;
+	}
+	vid = ulpi_id & 0xffff;
+	pid = ulpi_id >> 16;
+
+	pr_info("ULPI transceiver vendor/product ID 0x%04x/0x%04x\n", vid, pid);
+
+	for (i = 0; i < ARRAY_SIZE(ulpi_ids); i++) {
+		if (ulpi_ids[i].id == ULPI_ID(vid, pid)) {
+			pr_info("Found %s ULPI transceiver.\n",
+				ulpi_ids[i].name);
+			break;
+		}
+	}
+
+	ret = ulpi_check_integrity(phy);
+	if (ret)
+		return ret;
+
+	return ulpi_set_flags(phy);
+}
+
+static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+	struct usb_phy *phy = otg->usb_phy;
+	unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
+
+	if (!host) {
+		otg->host = NULL;
+		return 0;
+	}
+
+	otg->host = host;
+
+	flags &= ~(ULPI_IFC_CTRL_6_PIN_SERIAL_MODE |
+		   ULPI_IFC_CTRL_3_PIN_SERIAL_MODE |
+		   ULPI_IFC_CTRL_CARKITMODE);
+
+	if (phy->flags & ULPI_IC_6PIN_SERIAL)
+		flags |= ULPI_IFC_CTRL_6_PIN_SERIAL_MODE;
+	else if (phy->flags & ULPI_IC_3PIN_SERIAL)
+		flags |= ULPI_IFC_CTRL_3_PIN_SERIAL_MODE;
+	else if (phy->flags & ULPI_IC_CARKIT)
+		flags |= ULPI_IFC_CTRL_CARKITMODE;
+
+	return usb_phy_io_write(phy, flags, ULPI_IFC_CTRL);
+}
+
+static int ulpi_set_vbus(struct usb_otg *otg, bool on)
+{
+	struct usb_phy *phy = otg->usb_phy;
+	unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
+
+	flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
+
+	if (on) {
+		if (phy->flags & ULPI_OTG_DRVVBUS)
+			flags |= ULPI_OTG_CTRL_DRVVBUS;
+
+		if (phy->flags & ULPI_OTG_DRVVBUS_EXT)
+			flags |= ULPI_OTG_CTRL_DRVVBUS_EXT;
+	}
+
+	return usb_phy_io_write(phy, flags, ULPI_OTG_CTRL);
+}
+
+struct usb_phy *
+otg_ulpi_create(struct usb_phy_io_ops *ops,
+		unsigned int flags)
+{
+	struct usb_phy *phy;
+	struct usb_otg *otg;
+
+	phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return NULL;
+
+	otg = kzalloc(sizeof(*otg), GFP_KERNEL);
+	if (!otg) {
+		kfree(phy);
+		return NULL;
+	}
+
+	phy->label	= "ULPI";
+	phy->flags	= flags;
+	phy->io_ops	= ops;
+	phy->otg	= otg;
+	phy->init	= ulpi_init;
+
+	otg->usb_phy	= phy;
+	otg->set_host	= ulpi_set_host;
+	otg->set_vbus	= ulpi_set_vbus;
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(otg_ulpi_create);
+
diff --git a/marvell/linux/drivers/usb/phy/phy.c b/marvell/linux/drivers/usb/phy/phy.c
new file mode 100644
index 0000000..f3e203a
--- /dev/null
+++ b/marvell/linux/drivers/usb/phy/phy.c
@@ -0,0 +1,442 @@
+/*
+ * phy.c -- USB phy handling
+ *
+ * Copyright (C) 2004-2013 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#include <linux/usb/phy.h>
+
+static LIST_HEAD(phy_list);
+static LIST_HEAD(phy_bind_list);
+static DEFINE_SPINLOCK(phy_lock);
+
+static struct usb_phy *__usb_find_phy(struct list_head *list,
+	enum usb_phy_type type)
+{
+	struct usb_phy  *phy = NULL;
+
+	list_for_each_entry(phy, list, head) {
+		if (phy->type != type)
+			continue;
+
+		return phy;
+	}
+
+	return ERR_PTR(-ENODEV);
+}
+
+static struct usb_phy *__usb_find_phy_dev(struct device *dev,
+	struct list_head *list, u8 index)
+{
+	struct usb_phy_bind *phy_bind = NULL;
+
+	list_for_each_entry(phy_bind, list, list) {
+		if (!(strcmp(phy_bind->dev_name, dev_name(dev))) &&
+				phy_bind->index == index) {
+			if (phy_bind->phy)
+				return phy_bind->phy;
+			else
+				return ERR_PTR(-EPROBE_DEFER);
+		}
+	}
+
+	return ERR_PTR(-ENODEV);
+}
+
+static struct usb_phy *__of_usb_find_phy(struct device_node *node)
+{
+	struct usb_phy  *phy;
+
+	list_for_each_entry(phy, &phy_list, head) {
+		if (node != phy->dev->of_node)
+			continue;
+
+		return phy;
+	}
+
+	return ERR_PTR(-ENODEV);
+}
+
+static void devm_usb_phy_release(struct device *dev, void *res)
+{
+	struct usb_phy *phy = *(struct usb_phy **)res;
+
+	usb_put_phy(phy);
+}
+
+static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
+{
+	return res == match_data;
+}
+
+/**
+ * devm_usb_get_phy - find the USB PHY
+ * @dev - device that requests this phy
+ * @type - the type of the phy the controller requires
+ *
+ * Gets the phy using usb_get_phy(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type)
+{
+	struct usb_phy **ptr, *phy;
+
+	ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return NULL;
+
+	phy = usb_get_phy(type);
+	if (!IS_ERR(phy)) {
+		*ptr = phy;
+		devres_add(dev, ptr);
+	} else
+		devres_free(ptr);
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(devm_usb_get_phy);
+
+/**
+ * usb_get_phy - find the USB PHY
+ * @type - the type of the phy the controller requires
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy.  The caller is responsible for
+ * calling usb_put_phy() to release that count.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *usb_get_phy(enum usb_phy_type type)
+{
+	struct usb_phy	*phy = NULL;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&phy_lock, flags);
+
+	phy = __usb_find_phy(&phy_list, type);
+	if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
+		pr_err("unable to find transceiver of type %s\n",
+			usb_phy_type_string(type));
+		goto err0;
+	}
+
+	get_device(phy->dev);
+
+err0:
+	spin_unlock_irqrestore(&phy_lock, flags);
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(usb_get_phy);
+
+ /**
+ * devm_usb_get_phy_by_phandle - find the USB PHY by phandle
+ * @dev - device that requests this phy
+ * @phandle - name of the property holding the phy phandle value
+ * @index - the index of the phy
+ *
+ * Returns the phy driver associated with the given phandle value,
+ * after getting a refcount to it, -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * not yet loaded. While at that, it also associates the device with
+ * the phy using devres. On driver detach, release function is invoked
+ * on the devres data, then, devres data is freed.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
+	const char *phandle, u8 index)
+{
+	struct usb_phy	*phy = ERR_PTR(-ENOMEM), **ptr;
+	unsigned long	flags;
+	struct device_node *node;
+
+	if (!dev->of_node) {
+		dev_dbg(dev, "device does not have a device node entry\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	node = of_parse_phandle(dev->of_node, phandle, index);
+	if (!node) {
+		dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle,
+			dev->of_node->full_name);
+		return ERR_PTR(-ENODEV);
+	}
+
+	ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr) {
+		dev_dbg(dev, "failed to allocate memory for devres\n");
+		goto err0;
+	}
+
+	spin_lock_irqsave(&phy_lock, flags);
+
+	phy = __of_usb_find_phy(node);
+	if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
+		phy = ERR_PTR(-EPROBE_DEFER);
+		devres_free(ptr);
+		goto err1;
+	}
+
+	*ptr = phy;
+	devres_add(dev, ptr);
+
+	get_device(phy->dev);
+
+err1:
+	spin_unlock_irqrestore(&phy_lock, flags);
+
+err0:
+	of_node_put(node);
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle);
+
+/**
+ * usb_get_phy_dev - find the USB PHY
+ * @dev - device that requests this phy
+ * @index - the index of the phy
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy.  The caller is responsible for
+ * calling usb_put_phy() to release that count.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index)
+{
+	struct usb_phy	*phy = NULL;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&phy_lock, flags);
+
+	phy = __usb_find_phy_dev(dev, &phy_bind_list, index);
+	if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
+		pr_err("unable to find transceiver\n");
+		goto err0;
+	}
+
+	get_device(phy->dev);
+
+err0:
+	spin_unlock_irqrestore(&phy_lock, flags);
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(usb_get_phy_dev);
+
+/**
+ * devm_usb_get_phy_dev - find the USB PHY using device ptr and index
+ * @dev - device that requests this phy
+ * @index - the index of the phy
+ *
+ * Gets the phy using usb_get_phy_dev(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index)
+{
+	struct usb_phy **ptr, *phy;
+
+	ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return NULL;
+
+	phy = usb_get_phy_dev(dev, index);
+	if (!IS_ERR(phy)) {
+		*ptr = phy;
+		devres_add(dev, ptr);
+	} else
+		devres_free(ptr);
+
+	return phy;
+}
+EXPORT_SYMBOL_GPL(devm_usb_get_phy_dev);
+
+/**
+ * devm_usb_put_phy - release the USB PHY
+ * @dev - device that wants to release this phy
+ * @phy - the phy returned by devm_usb_get_phy()
+ *
+ * destroys the devres associated with this phy and invokes usb_put_phy
+ * to release the phy.
+ *
+ * For use by USB host and peripheral drivers.
+ */
+void devm_usb_put_phy(struct device *dev, struct usb_phy *phy)
+{
+	int r;
+
+	r = devres_release(dev, devm_usb_phy_release, devm_usb_phy_match, phy);
+	dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_usb_put_phy);
+
+/**
+ * usb_put_phy - release the USB PHY
+ * @x: the phy returned by usb_get_phy()
+ *
+ * Releases a refcount the caller received from usb_get_phy().
+ *
+ * For use by USB host and peripheral drivers.
+ */
+void usb_put_phy(struct usb_phy *x)
+{
+	if (x) {
+		struct module *owner = x->dev->driver->owner;
+
+		put_device(x->dev);
+		module_put(owner);
+	}
+}
+EXPORT_SYMBOL_GPL(usb_put_phy);
+
+/**
+ * usb_add_phy - declare the USB PHY
+ * @x: the USB phy to be used; or NULL
+ * @type - the type of this PHY
+ *
+ * This call is exclusively for use by phy drivers, which
+ * coordinate the activities of drivers for host and peripheral
+ * controllers, and in some cases for VBUS current regulation.
+ */
+int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
+{
+	int		ret = 0;
+	unsigned long	flags;
+	struct usb_phy	*phy;
+
+	if (x->type != USB_PHY_TYPE_UNDEFINED) {
+		dev_err(x->dev, "not accepting initialized PHY %s\n", x->label);
+		return -EINVAL;
+	}
+
+	x->refcount = 0;
+
+	spin_lock_irqsave(&phy_lock, flags);
+
+	list_for_each_entry(phy, &phy_list, head) {
+		if (phy->type == type) {
+			ret = -EBUSY;
+			dev_err(x->dev, "transceiver type %s already exists\n",
+						usb_phy_type_string(type));
+			goto out;
+		}
+	}
+
+	x->type = type;
+	list_add_tail(&x->head, &phy_list);
+
+out:
+	spin_unlock_irqrestore(&phy_lock, flags);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_phy);
+
+/**
+ * usb_add_phy_dev - declare the USB PHY
+ * @x: the USB phy to be used; or NULL
+ *
+ * This call is exclusively for use by phy drivers, which
+ * coordinate the activities of drivers for host and peripheral
+ * controllers, and in some cases for VBUS current regulation.
+ */
+int usb_add_phy_dev(struct usb_phy *x)
+{
+	struct usb_phy_bind *phy_bind;
+	unsigned long flags;
+
+	if (!x->dev) {
+		dev_err(x->dev, "no device provided for PHY\n");
+		return -EINVAL;
+	}
+
+	x->refcount = 0;
+
+	spin_lock_irqsave(&phy_lock, flags);
+	list_for_each_entry(phy_bind, &phy_bind_list, list)
+		if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev))))
+			phy_bind->phy = x;
+
+	list_add_tail(&x->head, &phy_list);
+
+	spin_unlock_irqrestore(&phy_lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_add_phy_dev);
+
+/**
+ * usb_remove_phy - remove the OTG PHY
+ * @x: the USB OTG PHY to be removed;
+ *
+ * This reverts the effects of usb_add_phy
+ */
+void usb_remove_phy(struct usb_phy *x)
+{
+	unsigned long	flags;
+	struct usb_phy_bind *phy_bind;
+
+	spin_lock_irqsave(&phy_lock, flags);
+	if (x) {
+		list_for_each_entry(phy_bind, &phy_bind_list, list)
+			if (phy_bind->phy == x)
+				phy_bind->phy = NULL;
+		list_del(&x->head);
+	}
+	spin_unlock_irqrestore(&phy_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_remove_phy);
+
+/**
+ * usb_bind_phy - bind the phy and the controller that uses the phy
+ * @dev_name: the device name of the device that will bind to the phy
+ * @index: index to specify the port number
+ * @phy_dev_name: the device name of the phy
+ *
+ * Fills the phy_bind structure with the dev_name and phy_dev_name. This will
+ * be used when the phy driver registers the phy and when the controller
+ * requests this phy.
+ *
+ * To be used by platform specific initialization code.
+ */
+int usb_bind_phy(const char *dev_name, u8 index,
+				const char *phy_dev_name)
+{
+	struct usb_phy_bind *phy_bind;
+	unsigned long flags;
+
+	phy_bind = kzalloc(sizeof(*phy_bind), GFP_KERNEL);
+	if (!phy_bind) {
+		pr_err("phy_bind(): No memory for phy_bind");
+		return -ENOMEM;
+	}
+
+	phy_bind->dev_name = dev_name;
+	phy_bind->phy_dev_name = phy_dev_name;
+	phy_bind->index = index;
+
+	spin_lock_irqsave(&phy_lock, flags);
+	list_add_tail(&phy_bind->list, &phy_bind_list);
+	spin_unlock_irqrestore(&phy_lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_bind_phy);