18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com
68c2ecf20Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/clk.h>
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
178c2ecf20Sopenharmony_ci#include <linux/phy/omap_control_phy.h>
188c2ecf20Sopenharmony_ci#include <linux/phy/omap_usb.h>
198c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
228c2ecf20Sopenharmony_ci#include <linux/regmap.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci#include <linux/sys_soc.h>
258c2ecf20Sopenharmony_ci#include <linux/usb/phy_companion.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define USB2PHY_ANA_CONFIG1		0x4c
288c2ecf20Sopenharmony_ci#define USB2PHY_DISCON_BYP_LATCH	BIT(31)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define USB2PHY_CHRG_DET			0x14
318c2ecf20Sopenharmony_ci#define USB2PHY_CHRG_DET_USE_CHG_DET_REG	BIT(29)
328c2ecf20Sopenharmony_ci#define USB2PHY_CHRG_DET_DIS_CHG_DET		BIT(28)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* SoC Specific USB2_OTG register definitions */
358c2ecf20Sopenharmony_ci#define AM654_USB2_OTG_PD		BIT(8)
368c2ecf20Sopenharmony_ci#define AM654_USB2_VBUS_DET_EN		BIT(5)
378c2ecf20Sopenharmony_ci#define AM654_USB2_VBUSVALID_DET_EN	BIT(4)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define OMAP_DEV_PHY_PD		BIT(0)
408c2ecf20Sopenharmony_ci#define OMAP_USB2_PHY_PD	BIT(28)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define AM437X_USB2_PHY_PD		BIT(0)
438c2ecf20Sopenharmony_ci#define AM437X_USB2_OTG_PD		BIT(1)
448c2ecf20Sopenharmony_ci#define AM437X_USB2_OTGVDET_EN		BIT(19)
458c2ecf20Sopenharmony_ci#define AM437X_USB2_OTGSESSEND_EN	BIT(20)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Driver Flags */
488c2ecf20Sopenharmony_ci#define OMAP_USB2_HAS_START_SRP			BIT(0)
498c2ecf20Sopenharmony_ci#define OMAP_USB2_HAS_SET_VBUS			BIT(1)
508c2ecf20Sopenharmony_ci#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT	BIT(2)
518c2ecf20Sopenharmony_ci#define OMAP_USB2_DISABLE_CHRG_DET		BIT(3)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct omap_usb {
548c2ecf20Sopenharmony_ci	struct usb_phy		phy;
558c2ecf20Sopenharmony_ci	struct phy_companion	*comparator;
568c2ecf20Sopenharmony_ci	void __iomem		*pll_ctrl_base;
578c2ecf20Sopenharmony_ci	void __iomem		*phy_base;
588c2ecf20Sopenharmony_ci	struct device		*dev;
598c2ecf20Sopenharmony_ci	struct device		*control_dev;
608c2ecf20Sopenharmony_ci	struct clk		*wkupclk;
618c2ecf20Sopenharmony_ci	struct clk		*optclk;
628c2ecf20Sopenharmony_ci	u8			flags;
638c2ecf20Sopenharmony_ci	struct regmap		*syscon_phy_power; /* ctrl. reg. acces */
648c2ecf20Sopenharmony_ci	unsigned int		power_reg; /* power reg. index within syscon */
658c2ecf20Sopenharmony_ci	u32			mask;
668c2ecf20Sopenharmony_ci	u32			power_on;
678c2ecf20Sopenharmony_ci	u32			power_off;
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define	phy_to_omapusb(x)	container_of((x), struct omap_usb, phy)
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct usb_phy_data {
738c2ecf20Sopenharmony_ci	const char *label;
748c2ecf20Sopenharmony_ci	u8 flags;
758c2ecf20Sopenharmony_ci	u32 mask;
768c2ecf20Sopenharmony_ci	u32 power_on;
778c2ecf20Sopenharmony_ci	u32 power_off;
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	return __raw_readl(addr + offset);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic inline void omap_usb_writel(void __iomem *addr, unsigned int offset,
868c2ecf20Sopenharmony_ci				   u32 data)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	__raw_writel(data, addr + offset);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/**
928c2ecf20Sopenharmony_ci * omap_usb2_set_comparator - links the comparator present in the system with
938c2ecf20Sopenharmony_ci *	this phy
948c2ecf20Sopenharmony_ci * @comparator - the companion phy(comparator) for this phy
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci * The phy companion driver should call this API passing the phy_companion
978c2ecf20Sopenharmony_ci * filled with set_vbus and start_srp to be used by usb phy.
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci * For use by phy companion driver
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ciint omap_usb2_set_comparator(struct phy_companion *comparator)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct omap_usb	*phy;
1048c2ecf20Sopenharmony_ci	struct usb_phy	*x = usb_get_phy(USB_PHY_TYPE_USB2);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (IS_ERR(x))
1078c2ecf20Sopenharmony_ci		return -ENODEV;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	phy = phy_to_omapusb(x);
1108c2ecf20Sopenharmony_ci	phy->comparator = comparator;
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (!phy->comparator || !phy->comparator->set_vbus)
1208c2ecf20Sopenharmony_ci		return -ENODEV;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return phy->comparator->set_vbus(phy->comparator, enabled);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int omap_usb_start_srp(struct usb_otg *otg)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (!phy->comparator || !phy->comparator->start_srp)
1308c2ecf20Sopenharmony_ci		return -ENODEV;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return phy->comparator->start_srp(phy->comparator);
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	otg->host = host;
1388c2ecf20Sopenharmony_ci	if (!host)
1398c2ecf20Sopenharmony_ci		otg->state = OTG_STATE_UNDEFINED;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int omap_usb_set_peripheral(struct usb_otg *otg,
1458c2ecf20Sopenharmony_ci				   struct usb_gadget *gadget)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	otg->gadget = gadget;
1488c2ecf20Sopenharmony_ci	if (!gadget)
1498c2ecf20Sopenharmony_ci		otg->state = OTG_STATE_UNDEFINED;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int omap_usb_phy_power(struct omap_usb *phy, int on)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	u32 val;
1578c2ecf20Sopenharmony_ci	int ret;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (!phy->syscon_phy_power) {
1608c2ecf20Sopenharmony_ci		omap_control_phy_power(phy->control_dev, on);
1618c2ecf20Sopenharmony_ci		return 0;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (on)
1658c2ecf20Sopenharmony_ci		val = phy->power_on;
1668c2ecf20Sopenharmony_ci	else
1678c2ecf20Sopenharmony_ci		val = phy->power_off;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg,
1708c2ecf20Sopenharmony_ci				 phy->mask, val);
1718c2ecf20Sopenharmony_ci	return ret;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic int omap_usb_power_off(struct phy *x)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct omap_usb *phy = phy_get_drvdata(x);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return omap_usb_phy_power(phy, false);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic int omap_usb_power_on(struct phy *x)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct omap_usb *phy = phy_get_drvdata(x);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return omap_usb_phy_power(phy, true);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int omap_usb2_disable_clocks(struct omap_usb *phy)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	clk_disable_unprepare(phy->wkupclk);
1918c2ecf20Sopenharmony_ci	if (!IS_ERR(phy->optclk))
1928c2ecf20Sopenharmony_ci		clk_disable_unprepare(phy->optclk);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int omap_usb2_enable_clocks(struct omap_usb *phy)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	int ret;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(phy->wkupclk);
2028c2ecf20Sopenharmony_ci	if (ret < 0) {
2038c2ecf20Sopenharmony_ci		dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
2048c2ecf20Sopenharmony_ci		goto err0;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (!IS_ERR(phy->optclk)) {
2088c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(phy->optclk);
2098c2ecf20Sopenharmony_ci		if (ret < 0) {
2108c2ecf20Sopenharmony_ci			dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
2118c2ecf20Sopenharmony_ci			goto err1;
2128c2ecf20Sopenharmony_ci		}
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cierr1:
2188c2ecf20Sopenharmony_ci	clk_disable_unprepare(phy->wkupclk);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cierr0:
2218c2ecf20Sopenharmony_ci	return ret;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int omap_usb_init(struct phy *x)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct omap_usb *phy = phy_get_drvdata(x);
2278c2ecf20Sopenharmony_ci	u32 val;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	omap_usb2_enable_clocks(phy);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
2328c2ecf20Sopenharmony_ci		/*
2338c2ecf20Sopenharmony_ci		 *
2348c2ecf20Sopenharmony_ci		 * Reduce the sensitivity of internal PHY by enabling the
2358c2ecf20Sopenharmony_ci		 * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
2368c2ecf20Sopenharmony_ci		 * resolves issues with certain devices which can otherwise
2378c2ecf20Sopenharmony_ci		 * be prone to false disconnects.
2388c2ecf20Sopenharmony_ci		 *
2398c2ecf20Sopenharmony_ci		 */
2408c2ecf20Sopenharmony_ci		val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1);
2418c2ecf20Sopenharmony_ci		val |= USB2PHY_DISCON_BYP_LATCH;
2428c2ecf20Sopenharmony_ci		omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (phy->flags & OMAP_USB2_DISABLE_CHRG_DET) {
2468c2ecf20Sopenharmony_ci		val = omap_usb_readl(phy->phy_base, USB2PHY_CHRG_DET);
2478c2ecf20Sopenharmony_ci		val |= USB2PHY_CHRG_DET_USE_CHG_DET_REG |
2488c2ecf20Sopenharmony_ci		       USB2PHY_CHRG_DET_DIS_CHG_DET;
2498c2ecf20Sopenharmony_ci		omap_usb_writel(phy->phy_base, USB2PHY_CHRG_DET, val);
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return 0;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int omap_usb_exit(struct phy *x)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct omap_usb *phy = phy_get_drvdata(x);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return omap_usb2_disable_clocks(phy);
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic const struct phy_ops ops = {
2638c2ecf20Sopenharmony_ci	.init		= omap_usb_init,
2648c2ecf20Sopenharmony_ci	.exit		= omap_usb_exit,
2658c2ecf20Sopenharmony_ci	.power_on	= omap_usb_power_on,
2668c2ecf20Sopenharmony_ci	.power_off	= omap_usb_power_off,
2678c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2688c2ecf20Sopenharmony_ci};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic const struct usb_phy_data omap_usb2_data = {
2718c2ecf20Sopenharmony_ci	.label = "omap_usb2",
2728c2ecf20Sopenharmony_ci	.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
2738c2ecf20Sopenharmony_ci	.mask = OMAP_DEV_PHY_PD,
2748c2ecf20Sopenharmony_ci	.power_off = OMAP_DEV_PHY_PD,
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic const struct usb_phy_data omap5_usb2_data = {
2788c2ecf20Sopenharmony_ci	.label = "omap5_usb2",
2798c2ecf20Sopenharmony_ci	.flags = 0,
2808c2ecf20Sopenharmony_ci	.mask = OMAP_DEV_PHY_PD,
2818c2ecf20Sopenharmony_ci	.power_off = OMAP_DEV_PHY_PD,
2828c2ecf20Sopenharmony_ci};
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic const struct usb_phy_data dra7x_usb2_data = {
2858c2ecf20Sopenharmony_ci	.label = "dra7x_usb2",
2868c2ecf20Sopenharmony_ci	.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
2878c2ecf20Sopenharmony_ci	.mask = OMAP_DEV_PHY_PD,
2888c2ecf20Sopenharmony_ci	.power_off = OMAP_DEV_PHY_PD,
2898c2ecf20Sopenharmony_ci};
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic const struct usb_phy_data dra7x_usb2_phy2_data = {
2928c2ecf20Sopenharmony_ci	.label = "dra7x_usb2_phy2",
2938c2ecf20Sopenharmony_ci	.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
2948c2ecf20Sopenharmony_ci	.mask = OMAP_USB2_PHY_PD,
2958c2ecf20Sopenharmony_ci	.power_off = OMAP_USB2_PHY_PD,
2968c2ecf20Sopenharmony_ci};
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic const struct usb_phy_data am437x_usb2_data = {
2998c2ecf20Sopenharmony_ci	.label = "am437x_usb2",
3008c2ecf20Sopenharmony_ci	.flags =  0,
3018c2ecf20Sopenharmony_ci	.mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD |
3028c2ecf20Sopenharmony_ci		AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
3038c2ecf20Sopenharmony_ci	.power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN,
3048c2ecf20Sopenharmony_ci	.power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD,
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic const struct usb_phy_data am654_usb2_data = {
3088c2ecf20Sopenharmony_ci	.label = "am654_usb2",
3098c2ecf20Sopenharmony_ci	.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
3108c2ecf20Sopenharmony_ci	.mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN |
3118c2ecf20Sopenharmony_ci		AM654_USB2_VBUSVALID_DET_EN,
3128c2ecf20Sopenharmony_ci	.power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN,
3138c2ecf20Sopenharmony_ci	.power_off = AM654_USB2_OTG_PD,
3148c2ecf20Sopenharmony_ci};
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic const struct of_device_id omap_usb2_id_table[] = {
3178c2ecf20Sopenharmony_ci	{
3188c2ecf20Sopenharmony_ci		.compatible = "ti,omap-usb2",
3198c2ecf20Sopenharmony_ci		.data = &omap_usb2_data,
3208c2ecf20Sopenharmony_ci	},
3218c2ecf20Sopenharmony_ci	{
3228c2ecf20Sopenharmony_ci		.compatible = "ti,omap5-usb2",
3238c2ecf20Sopenharmony_ci		.data = &omap5_usb2_data,
3248c2ecf20Sopenharmony_ci	},
3258c2ecf20Sopenharmony_ci	{
3268c2ecf20Sopenharmony_ci		.compatible = "ti,dra7x-usb2",
3278c2ecf20Sopenharmony_ci		.data = &dra7x_usb2_data,
3288c2ecf20Sopenharmony_ci	},
3298c2ecf20Sopenharmony_ci	{
3308c2ecf20Sopenharmony_ci		.compatible = "ti,dra7x-usb2-phy2",
3318c2ecf20Sopenharmony_ci		.data = &dra7x_usb2_phy2_data,
3328c2ecf20Sopenharmony_ci	},
3338c2ecf20Sopenharmony_ci	{
3348c2ecf20Sopenharmony_ci		.compatible = "ti,am437x-usb2",
3358c2ecf20Sopenharmony_ci		.data = &am437x_usb2_data,
3368c2ecf20Sopenharmony_ci	},
3378c2ecf20Sopenharmony_ci	{
3388c2ecf20Sopenharmony_ci		.compatible = "ti,am654-usb2",
3398c2ecf20Sopenharmony_ci		.data = &am654_usb2_data,
3408c2ecf20Sopenharmony_ci	},
3418c2ecf20Sopenharmony_ci	{},
3428c2ecf20Sopenharmony_ci};
3438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_usb2_id_table);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic void omap_usb2_init_errata(struct omap_usb *phy)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	static const struct soc_device_attribute am65x_sr10_soc_devices[] = {
3488c2ecf20Sopenharmony_ci		{ .family = "AM65X", .revision = "SR1.0" },
3498c2ecf20Sopenharmony_ci		{ /* sentinel */ }
3508c2ecf20Sopenharmony_ci	};
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/*
3538c2ecf20Sopenharmony_ci	 * Errata i2075: USB2PHY: USB2PHY Charger Detect is Enabled by
3548c2ecf20Sopenharmony_ci	 * Default Without VBUS Presence.
3558c2ecf20Sopenharmony_ci	 *
3568c2ecf20Sopenharmony_ci	 * AM654x SR1.0 has a silicon bug due to which D+ is pulled high after
3578c2ecf20Sopenharmony_ci	 * POR, which could cause enumeration failure with some USB hubs.
3588c2ecf20Sopenharmony_ci	 * Disabling the USB2_PHY Charger Detect function will put D+
3598c2ecf20Sopenharmony_ci	 * into the normal state.
3608c2ecf20Sopenharmony_ci	 */
3618c2ecf20Sopenharmony_ci	if (soc_device_match(am65x_sr10_soc_devices))
3628c2ecf20Sopenharmony_ci		phy->flags |= OMAP_USB2_DISABLE_CHRG_DET;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int omap_usb2_probe(struct platform_device *pdev)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct omap_usb	*phy;
3688c2ecf20Sopenharmony_ci	struct phy *generic_phy;
3698c2ecf20Sopenharmony_ci	struct resource *res;
3708c2ecf20Sopenharmony_ci	struct phy_provider *phy_provider;
3718c2ecf20Sopenharmony_ci	struct usb_otg *otg;
3728c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
3738c2ecf20Sopenharmony_ci	struct device_node *control_node;
3748c2ecf20Sopenharmony_ci	struct platform_device *control_pdev;
3758c2ecf20Sopenharmony_ci	const struct of_device_id *of_id;
3768c2ecf20Sopenharmony_ci	struct usb_phy_data *phy_data;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	of_id = of_match_device(omap_usb2_id_table, &pdev->dev);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (!of_id)
3818c2ecf20Sopenharmony_ci		return -EINVAL;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	phy_data = (struct usb_phy_data *)of_id->data;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
3868c2ecf20Sopenharmony_ci	if (!phy)
3878c2ecf20Sopenharmony_ci		return -ENOMEM;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
3908c2ecf20Sopenharmony_ci	if (!otg)
3918c2ecf20Sopenharmony_ci		return -ENOMEM;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	phy->dev		= &pdev->dev;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	phy->phy.dev		= phy->dev;
3968c2ecf20Sopenharmony_ci	phy->phy.label		= phy_data->label;
3978c2ecf20Sopenharmony_ci	phy->phy.otg		= otg;
3988c2ecf20Sopenharmony_ci	phy->phy.type		= USB_PHY_TYPE_USB2;
3998c2ecf20Sopenharmony_ci	phy->mask		= phy_data->mask;
4008c2ecf20Sopenharmony_ci	phy->power_on		= phy_data->power_on;
4018c2ecf20Sopenharmony_ci	phy->power_off		= phy_data->power_off;
4028c2ecf20Sopenharmony_ci	phy->flags		= phy_data->flags;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	omap_usb2_init_errata(phy);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4078c2ecf20Sopenharmony_ci	phy->phy_base = devm_ioremap_resource(&pdev->dev, res);
4088c2ecf20Sopenharmony_ci	if (IS_ERR(phy->phy_base))
4098c2ecf20Sopenharmony_ci		return PTR_ERR(phy->phy_base);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node,
4128c2ecf20Sopenharmony_ci								"syscon-phy-power");
4138c2ecf20Sopenharmony_ci	if (IS_ERR(phy->syscon_phy_power)) {
4148c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev,
4158c2ecf20Sopenharmony_ci			"can't get syscon-phy-power, using control device\n");
4168c2ecf20Sopenharmony_ci		phy->syscon_phy_power = NULL;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		control_node = of_parse_phandle(node, "ctrl-module", 0);
4198c2ecf20Sopenharmony_ci		if (!control_node) {
4208c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
4218c2ecf20Sopenharmony_ci				"Failed to get control device phandle\n");
4228c2ecf20Sopenharmony_ci			return -EINVAL;
4238c2ecf20Sopenharmony_ci		}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		control_pdev = of_find_device_by_node(control_node);
4268c2ecf20Sopenharmony_ci		if (!control_pdev) {
4278c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Failed to get control device\n");
4288c2ecf20Sopenharmony_ci			return -EINVAL;
4298c2ecf20Sopenharmony_ci		}
4308c2ecf20Sopenharmony_ci		phy->control_dev = &control_pdev->dev;
4318c2ecf20Sopenharmony_ci	} else {
4328c2ecf20Sopenharmony_ci		if (of_property_read_u32_index(node,
4338c2ecf20Sopenharmony_ci					       "syscon-phy-power", 1,
4348c2ecf20Sopenharmony_ci					       &phy->power_reg)) {
4358c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
4368c2ecf20Sopenharmony_ci				"couldn't get power reg. offset\n");
4378c2ecf20Sopenharmony_ci			return -EINVAL;
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
4428c2ecf20Sopenharmony_ci	if (IS_ERR(phy->wkupclk)) {
4438c2ecf20Sopenharmony_ci		if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER)
4448c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "unable to get wkupclk %ld, trying old name\n",
4478c2ecf20Sopenharmony_ci			 PTR_ERR(phy->wkupclk));
4488c2ecf20Sopenharmony_ci		phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		if (IS_ERR(phy->wkupclk)) {
4518c2ecf20Sopenharmony_ci			if (PTR_ERR(phy->wkupclk) != -EPROBE_DEFER)
4528c2ecf20Sopenharmony_ci				dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
4538c2ecf20Sopenharmony_ci			return PTR_ERR(phy->wkupclk);
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev,
4578c2ecf20Sopenharmony_ci			 "found usb_phy_cm_clk32k, please fix DTS\n");
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	phy->optclk = devm_clk_get(phy->dev, "refclk");
4618c2ecf20Sopenharmony_ci	if (IS_ERR(phy->optclk)) {
4628c2ecf20Sopenharmony_ci		if (PTR_ERR(phy->optclk) == -EPROBE_DEFER)
4638c2ecf20Sopenharmony_ci			return -EPROBE_DEFER;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n");
4668c2ecf20Sopenharmony_ci		phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		if (IS_ERR(phy->optclk)) {
4698c2ecf20Sopenharmony_ci			if (PTR_ERR(phy->optclk) != -EPROBE_DEFER) {
4708c2ecf20Sopenharmony_ci				dev_dbg(&pdev->dev,
4718c2ecf20Sopenharmony_ci					"unable to get usb_otg_ss_refclk960m\n");
4728c2ecf20Sopenharmony_ci			}
4738c2ecf20Sopenharmony_ci		} else {
4748c2ecf20Sopenharmony_ci			dev_warn(&pdev->dev,
4758c2ecf20Sopenharmony_ci				 "found usb_otg_ss_refclk960m, please fix DTS\n");
4768c2ecf20Sopenharmony_ci		}
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	otg->set_host = omap_usb_set_host;
4808c2ecf20Sopenharmony_ci	otg->set_peripheral = omap_usb_set_peripheral;
4818c2ecf20Sopenharmony_ci	if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
4828c2ecf20Sopenharmony_ci		otg->set_vbus = omap_usb_set_vbus;
4838c2ecf20Sopenharmony_ci	if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
4848c2ecf20Sopenharmony_ci		otg->start_srp = omap_usb_start_srp;
4858c2ecf20Sopenharmony_ci	otg->usb_phy = &phy->phy;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, phy);
4888c2ecf20Sopenharmony_ci	pm_runtime_enable(phy->dev);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	generic_phy = devm_phy_create(phy->dev, NULL, &ops);
4918c2ecf20Sopenharmony_ci	if (IS_ERR(generic_phy)) {
4928c2ecf20Sopenharmony_ci		pm_runtime_disable(phy->dev);
4938c2ecf20Sopenharmony_ci		return PTR_ERR(generic_phy);
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	phy_set_drvdata(generic_phy, phy);
4978c2ecf20Sopenharmony_ci	omap_usb_power_off(generic_phy);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(phy->dev,
5008c2ecf20Sopenharmony_ci						     of_phy_simple_xlate);
5018c2ecf20Sopenharmony_ci	if (IS_ERR(phy_provider)) {
5028c2ecf20Sopenharmony_ci		pm_runtime_disable(phy->dev);
5038c2ecf20Sopenharmony_ci		return PTR_ERR(phy_provider);
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	usb_add_phy_dev(&phy->phy);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	return 0;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic int omap_usb2_remove(struct platform_device *pdev)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	struct omap_usb	*phy = platform_get_drvdata(pdev);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	usb_remove_phy(&phy->phy);
5168c2ecf20Sopenharmony_ci	pm_runtime_disable(phy->dev);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	return 0;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic struct platform_driver omap_usb2_driver = {
5228c2ecf20Sopenharmony_ci	.probe		= omap_usb2_probe,
5238c2ecf20Sopenharmony_ci	.remove		= omap_usb2_remove,
5248c2ecf20Sopenharmony_ci	.driver		= {
5258c2ecf20Sopenharmony_ci		.name	= "omap-usb2",
5268c2ecf20Sopenharmony_ci		.of_match_table = omap_usb2_id_table,
5278c2ecf20Sopenharmony_ci	},
5288c2ecf20Sopenharmony_ci};
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cimodule_platform_driver(omap_usb2_driver);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:omap_usb2");
5338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc.");
5348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OMAP USB2 phy driver");
5358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
536