162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com 662306a36Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_platform.h> 1762306a36Sopenharmony_ci#include <linux/phy/omap_control_phy.h> 1862306a36Sopenharmony_ci#include <linux/phy/omap_usb.h> 1962306a36Sopenharmony_ci#include <linux/phy/phy.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2262306a36Sopenharmony_ci#include <linux/regmap.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/sys_soc.h> 2562306a36Sopenharmony_ci#include <linux/usb/phy_companion.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define USB2PHY_ANA_CONFIG1 0x4c 2862306a36Sopenharmony_ci#define USB2PHY_DISCON_BYP_LATCH BIT(31) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define USB2PHY_CHRG_DET 0x14 3162306a36Sopenharmony_ci#define USB2PHY_CHRG_DET_USE_CHG_DET_REG BIT(29) 3262306a36Sopenharmony_ci#define USB2PHY_CHRG_DET_DIS_CHG_DET BIT(28) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* SoC Specific USB2_OTG register definitions */ 3562306a36Sopenharmony_ci#define AM654_USB2_OTG_PD BIT(8) 3662306a36Sopenharmony_ci#define AM654_USB2_VBUS_DET_EN BIT(5) 3762306a36Sopenharmony_ci#define AM654_USB2_VBUSVALID_DET_EN BIT(4) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define OMAP_DEV_PHY_PD BIT(0) 4062306a36Sopenharmony_ci#define OMAP_USB2_PHY_PD BIT(28) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define AM437X_USB2_PHY_PD BIT(0) 4362306a36Sopenharmony_ci#define AM437X_USB2_OTG_PD BIT(1) 4462306a36Sopenharmony_ci#define AM437X_USB2_OTGVDET_EN BIT(19) 4562306a36Sopenharmony_ci#define AM437X_USB2_OTGSESSEND_EN BIT(20) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Driver Flags */ 4862306a36Sopenharmony_ci#define OMAP_USB2_HAS_START_SRP BIT(0) 4962306a36Sopenharmony_ci#define OMAP_USB2_HAS_SET_VBUS BIT(1) 5062306a36Sopenharmony_ci#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT BIT(2) 5162306a36Sopenharmony_ci#define OMAP_USB2_DISABLE_CHRG_DET BIT(3) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct omap_usb { 5462306a36Sopenharmony_ci struct usb_phy phy; 5562306a36Sopenharmony_ci struct phy_companion *comparator; 5662306a36Sopenharmony_ci void __iomem *pll_ctrl_base; 5762306a36Sopenharmony_ci void __iomem *phy_base; 5862306a36Sopenharmony_ci struct device *dev; 5962306a36Sopenharmony_ci struct device *control_dev; 6062306a36Sopenharmony_ci struct clk *wkupclk; 6162306a36Sopenharmony_ci struct clk *optclk; 6262306a36Sopenharmony_ci u8 flags; 6362306a36Sopenharmony_ci struct regmap *syscon_phy_power; /* ctrl. reg. acces */ 6462306a36Sopenharmony_ci unsigned int power_reg; /* power reg. index within syscon */ 6562306a36Sopenharmony_ci u32 mask; 6662306a36Sopenharmony_ci u32 power_on; 6762306a36Sopenharmony_ci u32 power_off; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct usb_phy_data { 7362306a36Sopenharmony_ci const char *label; 7462306a36Sopenharmony_ci u8 flags; 7562306a36Sopenharmony_ci u32 mask; 7662306a36Sopenharmony_ci u32 power_on; 7762306a36Sopenharmony_ci u32 power_off; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci return __raw_readl(addr + offset); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic inline void omap_usb_writel(void __iomem *addr, unsigned int offset, 8662306a36Sopenharmony_ci u32 data) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci __raw_writel(data, addr + offset); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/** 9262306a36Sopenharmony_ci * omap_usb2_set_comparator() - links the comparator present in the system with this phy 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * @comparator: the companion phy(comparator) for this phy 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * The phy companion driver should call this API passing the phy_companion 9762306a36Sopenharmony_ci * filled with set_vbus and start_srp to be used by usb phy. 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * For use by phy companion driver 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ciint omap_usb2_set_comparator(struct phy_companion *comparator) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct omap_usb *phy; 10462306a36Sopenharmony_ci struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (IS_ERR(x)) 10762306a36Sopenharmony_ci return -ENODEV; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci phy = phy_to_omapusb(x); 11062306a36Sopenharmony_ci phy->comparator = comparator; 11162306a36Sopenharmony_ci return 0; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_usb2_set_comparator); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!phy->comparator || !phy->comparator->set_vbus) 12062306a36Sopenharmony_ci return -ENODEV; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return phy->comparator->set_vbus(phy->comparator, enabled); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int omap_usb_start_srp(struct usb_otg *otg) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (!phy->comparator || !phy->comparator->start_srp) 13062306a36Sopenharmony_ci return -ENODEV; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return phy->comparator->start_srp(phy->comparator); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci otg->host = host; 13862306a36Sopenharmony_ci if (!host) 13962306a36Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int omap_usb_set_peripheral(struct usb_otg *otg, 14562306a36Sopenharmony_ci struct usb_gadget *gadget) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci otg->gadget = gadget; 14862306a36Sopenharmony_ci if (!gadget) 14962306a36Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int omap_usb_phy_power(struct omap_usb *phy, int on) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci u32 val; 15762306a36Sopenharmony_ci int ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!phy->syscon_phy_power) { 16062306a36Sopenharmony_ci omap_control_phy_power(phy->control_dev, on); 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (on) 16562306a36Sopenharmony_ci val = phy->power_on; 16662306a36Sopenharmony_ci else 16762306a36Sopenharmony_ci val = phy->power_off; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg, 17062306a36Sopenharmony_ci phy->mask, val); 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int omap_usb_power_off(struct phy *x) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct omap_usb *phy = phy_get_drvdata(x); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return omap_usb_phy_power(phy, false); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int omap_usb_power_on(struct phy *x) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct omap_usb *phy = phy_get_drvdata(x); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return omap_usb_phy_power(phy, true); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int omap_usb2_disable_clocks(struct omap_usb *phy) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci clk_disable_unprepare(phy->wkupclk); 19162306a36Sopenharmony_ci if (!IS_ERR(phy->optclk)) 19262306a36Sopenharmony_ci clk_disable_unprepare(phy->optclk); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int omap_usb2_enable_clocks(struct omap_usb *phy) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = clk_prepare_enable(phy->wkupclk); 20262306a36Sopenharmony_ci if (ret < 0) { 20362306a36Sopenharmony_ci dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); 20462306a36Sopenharmony_ci goto err0; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!IS_ERR(phy->optclk)) { 20862306a36Sopenharmony_ci ret = clk_prepare_enable(phy->optclk); 20962306a36Sopenharmony_ci if (ret < 0) { 21062306a36Sopenharmony_ci dev_err(phy->dev, "Failed to enable optclk %d\n", ret); 21162306a36Sopenharmony_ci goto err1; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cierr1: 21862306a36Sopenharmony_ci clk_disable_unprepare(phy->wkupclk); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cierr0: 22162306a36Sopenharmony_ci return ret; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int omap_usb_init(struct phy *x) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct omap_usb *phy = phy_get_drvdata(x); 22762306a36Sopenharmony_ci u32 val; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci omap_usb2_enable_clocks(phy); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * Reduce the sensitivity of internal PHY by enabling the 23562306a36Sopenharmony_ci * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This 23662306a36Sopenharmony_ci * resolves issues with certain devices which can otherwise 23762306a36Sopenharmony_ci * be prone to false disconnects. 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1); 24162306a36Sopenharmony_ci val |= USB2PHY_DISCON_BYP_LATCH; 24262306a36Sopenharmony_ci omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (phy->flags & OMAP_USB2_DISABLE_CHRG_DET) { 24662306a36Sopenharmony_ci val = omap_usb_readl(phy->phy_base, USB2PHY_CHRG_DET); 24762306a36Sopenharmony_ci val |= USB2PHY_CHRG_DET_USE_CHG_DET_REG | 24862306a36Sopenharmony_ci USB2PHY_CHRG_DET_DIS_CHG_DET; 24962306a36Sopenharmony_ci omap_usb_writel(phy->phy_base, USB2PHY_CHRG_DET, val); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int omap_usb_exit(struct phy *x) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct omap_usb *phy = phy_get_drvdata(x); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return omap_usb2_disable_clocks(phy); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic const struct phy_ops ops = { 26362306a36Sopenharmony_ci .init = omap_usb_init, 26462306a36Sopenharmony_ci .exit = omap_usb_exit, 26562306a36Sopenharmony_ci .power_on = omap_usb_power_on, 26662306a36Sopenharmony_ci .power_off = omap_usb_power_off, 26762306a36Sopenharmony_ci .owner = THIS_MODULE, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic const struct usb_phy_data omap_usb2_data = { 27162306a36Sopenharmony_ci .label = "omap_usb2", 27262306a36Sopenharmony_ci .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS, 27362306a36Sopenharmony_ci .mask = OMAP_DEV_PHY_PD, 27462306a36Sopenharmony_ci .power_off = OMAP_DEV_PHY_PD, 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic const struct usb_phy_data omap5_usb2_data = { 27862306a36Sopenharmony_ci .label = "omap5_usb2", 27962306a36Sopenharmony_ci .flags = 0, 28062306a36Sopenharmony_ci .mask = OMAP_DEV_PHY_PD, 28162306a36Sopenharmony_ci .power_off = OMAP_DEV_PHY_PD, 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic const struct usb_phy_data dra7x_usb2_data = { 28562306a36Sopenharmony_ci .label = "dra7x_usb2", 28662306a36Sopenharmony_ci .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, 28762306a36Sopenharmony_ci .mask = OMAP_DEV_PHY_PD, 28862306a36Sopenharmony_ci .power_off = OMAP_DEV_PHY_PD, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const struct usb_phy_data dra7x_usb2_phy2_data = { 29262306a36Sopenharmony_ci .label = "dra7x_usb2_phy2", 29362306a36Sopenharmony_ci .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, 29462306a36Sopenharmony_ci .mask = OMAP_USB2_PHY_PD, 29562306a36Sopenharmony_ci .power_off = OMAP_USB2_PHY_PD, 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic const struct usb_phy_data am437x_usb2_data = { 29962306a36Sopenharmony_ci .label = "am437x_usb2", 30062306a36Sopenharmony_ci .flags = 0, 30162306a36Sopenharmony_ci .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD | 30262306a36Sopenharmony_ci AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, 30362306a36Sopenharmony_ci .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, 30462306a36Sopenharmony_ci .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic const struct usb_phy_data am654_usb2_data = { 30862306a36Sopenharmony_ci .label = "am654_usb2", 30962306a36Sopenharmony_ci .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, 31062306a36Sopenharmony_ci .mask = AM654_USB2_OTG_PD | AM654_USB2_VBUS_DET_EN | 31162306a36Sopenharmony_ci AM654_USB2_VBUSVALID_DET_EN, 31262306a36Sopenharmony_ci .power_on = AM654_USB2_VBUS_DET_EN | AM654_USB2_VBUSVALID_DET_EN, 31362306a36Sopenharmony_ci .power_off = AM654_USB2_OTG_PD, 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic const struct of_device_id omap_usb2_id_table[] = { 31762306a36Sopenharmony_ci { 31862306a36Sopenharmony_ci .compatible = "ti,omap-usb2", 31962306a36Sopenharmony_ci .data = &omap_usb2_data, 32062306a36Sopenharmony_ci }, 32162306a36Sopenharmony_ci { 32262306a36Sopenharmony_ci .compatible = "ti,omap5-usb2", 32362306a36Sopenharmony_ci .data = &omap5_usb2_data, 32462306a36Sopenharmony_ci }, 32562306a36Sopenharmony_ci { 32662306a36Sopenharmony_ci .compatible = "ti,dra7x-usb2", 32762306a36Sopenharmony_ci .data = &dra7x_usb2_data, 32862306a36Sopenharmony_ci }, 32962306a36Sopenharmony_ci { 33062306a36Sopenharmony_ci .compatible = "ti,dra7x-usb2-phy2", 33162306a36Sopenharmony_ci .data = &dra7x_usb2_phy2_data, 33262306a36Sopenharmony_ci }, 33362306a36Sopenharmony_ci { 33462306a36Sopenharmony_ci .compatible = "ti,am437x-usb2", 33562306a36Sopenharmony_ci .data = &am437x_usb2_data, 33662306a36Sopenharmony_ci }, 33762306a36Sopenharmony_ci { 33862306a36Sopenharmony_ci .compatible = "ti,am654-usb2", 33962306a36Sopenharmony_ci .data = &am654_usb2_data, 34062306a36Sopenharmony_ci }, 34162306a36Sopenharmony_ci {}, 34262306a36Sopenharmony_ci}; 34362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_usb2_id_table); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void omap_usb2_init_errata(struct omap_usb *phy) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci static const struct soc_device_attribute am65x_sr10_soc_devices[] = { 34862306a36Sopenharmony_ci { .family = "AM65X", .revision = "SR1.0" }, 34962306a36Sopenharmony_ci { /* sentinel */ } 35062306a36Sopenharmony_ci }; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* 35362306a36Sopenharmony_ci * Errata i2075: USB2PHY: USB2PHY Charger Detect is Enabled by 35462306a36Sopenharmony_ci * Default Without VBUS Presence. 35562306a36Sopenharmony_ci * 35662306a36Sopenharmony_ci * AM654x SR1.0 has a silicon bug due to which D+ is pulled high after 35762306a36Sopenharmony_ci * POR, which could cause enumeration failure with some USB hubs. 35862306a36Sopenharmony_ci * Disabling the USB2_PHY Charger Detect function will put D+ 35962306a36Sopenharmony_ci * into the normal state. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_ci if (soc_device_match(am65x_sr10_soc_devices)) 36262306a36Sopenharmony_ci phy->flags |= OMAP_USB2_DISABLE_CHRG_DET; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int omap_usb2_probe(struct platform_device *pdev) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct omap_usb *phy; 36862306a36Sopenharmony_ci struct phy *generic_phy; 36962306a36Sopenharmony_ci struct phy_provider *phy_provider; 37062306a36Sopenharmony_ci struct usb_otg *otg; 37162306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 37262306a36Sopenharmony_ci struct device_node *control_node; 37362306a36Sopenharmony_ci struct platform_device *control_pdev; 37462306a36Sopenharmony_ci const struct of_device_id *of_id; 37562306a36Sopenharmony_ci struct usb_phy_data *phy_data; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci of_id = of_match_device(omap_usb2_id_table, &pdev->dev); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!of_id) 38062306a36Sopenharmony_ci return -EINVAL; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci phy_data = (struct usb_phy_data *)of_id->data; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); 38562306a36Sopenharmony_ci if (!phy) 38662306a36Sopenharmony_ci return -ENOMEM; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); 38962306a36Sopenharmony_ci if (!otg) 39062306a36Sopenharmony_ci return -ENOMEM; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci phy->dev = &pdev->dev; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci phy->phy.dev = phy->dev; 39562306a36Sopenharmony_ci phy->phy.label = phy_data->label; 39662306a36Sopenharmony_ci phy->phy.otg = otg; 39762306a36Sopenharmony_ci phy->phy.type = USB_PHY_TYPE_USB2; 39862306a36Sopenharmony_ci phy->mask = phy_data->mask; 39962306a36Sopenharmony_ci phy->power_on = phy_data->power_on; 40062306a36Sopenharmony_ci phy->power_off = phy_data->power_off; 40162306a36Sopenharmony_ci phy->flags = phy_data->flags; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci omap_usb2_init_errata(phy); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci phy->phy_base = devm_platform_ioremap_resource(pdev, 0); 40662306a36Sopenharmony_ci if (IS_ERR(phy->phy_base)) 40762306a36Sopenharmony_ci return PTR_ERR(phy->phy_base); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node, 41062306a36Sopenharmony_ci "syscon-phy-power"); 41162306a36Sopenharmony_ci if (IS_ERR(phy->syscon_phy_power)) { 41262306a36Sopenharmony_ci dev_dbg(&pdev->dev, 41362306a36Sopenharmony_ci "can't get syscon-phy-power, using control device\n"); 41462306a36Sopenharmony_ci phy->syscon_phy_power = NULL; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci control_node = of_parse_phandle(node, "ctrl-module", 0); 41762306a36Sopenharmony_ci if (!control_node) { 41862306a36Sopenharmony_ci dev_err(&pdev->dev, 41962306a36Sopenharmony_ci "Failed to get control device phandle\n"); 42062306a36Sopenharmony_ci return -EINVAL; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci control_pdev = of_find_device_by_node(control_node); 42462306a36Sopenharmony_ci if (!control_pdev) { 42562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get control device\n"); 42662306a36Sopenharmony_ci return -EINVAL; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci phy->control_dev = &control_pdev->dev; 42962306a36Sopenharmony_ci } else { 43062306a36Sopenharmony_ci if (of_property_read_u32_index(node, 43162306a36Sopenharmony_ci "syscon-phy-power", 1, 43262306a36Sopenharmony_ci &phy->power_reg)) { 43362306a36Sopenharmony_ci dev_err(&pdev->dev, 43462306a36Sopenharmony_ci "couldn't get power reg. offset\n"); 43562306a36Sopenharmony_ci return -EINVAL; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); 44062306a36Sopenharmony_ci if (IS_ERR(phy->wkupclk)) { 44162306a36Sopenharmony_ci if (PTR_ERR(phy->wkupclk) == -EPROBE_DEFER) 44262306a36Sopenharmony_ci return -EPROBE_DEFER; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci dev_warn(&pdev->dev, "unable to get wkupclk %ld, trying old name\n", 44562306a36Sopenharmony_ci PTR_ERR(phy->wkupclk)); 44662306a36Sopenharmony_ci phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (IS_ERR(phy->wkupclk)) 44962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(phy->wkupclk), 45062306a36Sopenharmony_ci "unable to get usb_phy_cm_clk32k\n"); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci dev_warn(&pdev->dev, 45362306a36Sopenharmony_ci "found usb_phy_cm_clk32k, please fix DTS\n"); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci phy->optclk = devm_clk_get(phy->dev, "refclk"); 45762306a36Sopenharmony_ci if (IS_ERR(phy->optclk)) { 45862306a36Sopenharmony_ci if (PTR_ERR(phy->optclk) == -EPROBE_DEFER) 45962306a36Sopenharmony_ci return -EPROBE_DEFER; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n"); 46262306a36Sopenharmony_ci phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (IS_ERR(phy->optclk)) { 46562306a36Sopenharmony_ci if (PTR_ERR(phy->optclk) != -EPROBE_DEFER) { 46662306a36Sopenharmony_ci dev_dbg(&pdev->dev, 46762306a36Sopenharmony_ci "unable to get usb_otg_ss_refclk960m\n"); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci } else { 47062306a36Sopenharmony_ci dev_warn(&pdev->dev, 47162306a36Sopenharmony_ci "found usb_otg_ss_refclk960m, please fix DTS\n"); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci otg->set_host = omap_usb_set_host; 47662306a36Sopenharmony_ci otg->set_peripheral = omap_usb_set_peripheral; 47762306a36Sopenharmony_ci if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) 47862306a36Sopenharmony_ci otg->set_vbus = omap_usb_set_vbus; 47962306a36Sopenharmony_ci if (phy_data->flags & OMAP_USB2_HAS_START_SRP) 48062306a36Sopenharmony_ci otg->start_srp = omap_usb_start_srp; 48162306a36Sopenharmony_ci otg->usb_phy = &phy->phy; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci platform_set_drvdata(pdev, phy); 48462306a36Sopenharmony_ci pm_runtime_enable(phy->dev); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci generic_phy = devm_phy_create(phy->dev, NULL, &ops); 48762306a36Sopenharmony_ci if (IS_ERR(generic_phy)) { 48862306a36Sopenharmony_ci pm_runtime_disable(phy->dev); 48962306a36Sopenharmony_ci return PTR_ERR(generic_phy); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci phy_set_drvdata(generic_phy, phy); 49362306a36Sopenharmony_ci omap_usb_power_off(generic_phy); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(phy->dev, 49662306a36Sopenharmony_ci of_phy_simple_xlate); 49762306a36Sopenharmony_ci if (IS_ERR(phy_provider)) { 49862306a36Sopenharmony_ci pm_runtime_disable(phy->dev); 49962306a36Sopenharmony_ci return PTR_ERR(phy_provider); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci usb_add_phy_dev(&phy->phy); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic void omap_usb2_remove(struct platform_device *pdev) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct omap_usb *phy = platform_get_drvdata(pdev); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci usb_remove_phy(&phy->phy); 51262306a36Sopenharmony_ci pm_runtime_disable(phy->dev); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic struct platform_driver omap_usb2_driver = { 51662306a36Sopenharmony_ci .probe = omap_usb2_probe, 51762306a36Sopenharmony_ci .remove_new = omap_usb2_remove, 51862306a36Sopenharmony_ci .driver = { 51962306a36Sopenharmony_ci .name = "omap-usb2", 52062306a36Sopenharmony_ci .of_match_table = omap_usb2_id_table, 52162306a36Sopenharmony_ci }, 52262306a36Sopenharmony_ci}; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cimodule_platform_driver(omap_usb2_driver); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ciMODULE_ALIAS("platform:omap_usb2"); 52762306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc."); 52862306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP USB2 phy driver"); 52962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 530