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