162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2012-2014 Freescale Semiconductor, Inc. 462306a36Sopenharmony_ci * Copyright (C) 2012 Marek Vasut <marex@denx.de> 562306a36Sopenharmony_ci * on behalf of DENX Software Engineering GmbH 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/usb/otg.h> 1362306a36Sopenharmony_ci#include <linux/stmp_device.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2062306a36Sopenharmony_ci#include <linux/iopoll.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define DRIVER_NAME "mxs_phy" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Register Macro */ 2562306a36Sopenharmony_ci#define HW_USBPHY_PWD 0x00 2662306a36Sopenharmony_ci#define HW_USBPHY_TX 0x10 2762306a36Sopenharmony_ci#define HW_USBPHY_CTRL 0x30 2862306a36Sopenharmony_ci#define HW_USBPHY_CTRL_SET 0x34 2962306a36Sopenharmony_ci#define HW_USBPHY_CTRL_CLR 0x38 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define HW_USBPHY_DEBUG_SET 0x54 3262306a36Sopenharmony_ci#define HW_USBPHY_DEBUG_CLR 0x58 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define HW_USBPHY_IP 0x90 3562306a36Sopenharmony_ci#define HW_USBPHY_IP_SET 0x94 3662306a36Sopenharmony_ci#define HW_USBPHY_IP_CLR 0x98 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define GM_USBPHY_TX_TXCAL45DP(x) (((x) & 0xf) << 16) 3962306a36Sopenharmony_ci#define GM_USBPHY_TX_TXCAL45DN(x) (((x) & 0xf) << 8) 4062306a36Sopenharmony_ci#define GM_USBPHY_TX_D_CAL(x) (((x) & 0xf) << 0) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* imx7ulp */ 4362306a36Sopenharmony_ci#define HW_USBPHY_PLL_SIC 0xa0 4462306a36Sopenharmony_ci#define HW_USBPHY_PLL_SIC_SET 0xa4 4562306a36Sopenharmony_ci#define HW_USBPHY_PLL_SIC_CLR 0xa8 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define BM_USBPHY_CTRL_SFTRST BIT(31) 4862306a36Sopenharmony_ci#define BM_USBPHY_CTRL_CLKGATE BIT(30) 4962306a36Sopenharmony_ci#define BM_USBPHY_CTRL_OTG_ID_VALUE BIT(27) 5062306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS BIT(26) 5162306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE BIT(25) 5262306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP BIT(23) 5362306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENIDCHG_WKUP BIT(22) 5462306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP BIT(21) 5562306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD BIT(20) 5662306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE BIT(19) 5762306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL BIT(18) 5862306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENUTMILEVEL3 BIT(15) 5962306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14) 6062306a36Sopenharmony_ci#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define BM_USBPHY_IP_FIX (BIT(17) | BIT(18)) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define BM_USBPHY_DEBUG_CLKGATE BIT(30) 6562306a36Sopenharmony_ci/* imx7ulp */ 6662306a36Sopenharmony_ci#define BM_USBPHY_PLL_LOCK BIT(31) 6762306a36Sopenharmony_ci#define BM_USBPHY_PLL_REG_ENABLE BIT(21) 6862306a36Sopenharmony_ci#define BM_USBPHY_PLL_BYPASS BIT(16) 6962306a36Sopenharmony_ci#define BM_USBPHY_PLL_POWER BIT(12) 7062306a36Sopenharmony_ci#define BM_USBPHY_PLL_EN_USB_CLKS BIT(6) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Anatop Registers */ 7362306a36Sopenharmony_ci#define ANADIG_ANA_MISC0 0x150 7462306a36Sopenharmony_ci#define ANADIG_ANA_MISC0_SET 0x154 7562306a36Sopenharmony_ci#define ANADIG_ANA_MISC0_CLR 0x158 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DETECT_SET 0x1b4 7862306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DETECT_CLR 0x1b8 7962306a36Sopenharmony_ci#define ANADIG_USB2_CHRG_DETECT_SET 0x214 8062306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DETECT_EN_B BIT(20) 8162306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B BIT(19) 8262306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DETECT_CHK_CONTACT BIT(18) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define ANADIG_USB1_VBUS_DET_STAT 0x1c0 8562306a36Sopenharmony_ci#define ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DET_STAT 0x1d0 8862306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DET_STAT_DM_STATE BIT(2) 8962306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED BIT(1) 9062306a36Sopenharmony_ci#define ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT BIT(0) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define ANADIG_USB2_VBUS_DET_STAT 0x220 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define ANADIG_USB1_LOOPBACK_SET 0x1e4 9562306a36Sopenharmony_ci#define ANADIG_USB1_LOOPBACK_CLR 0x1e8 9662306a36Sopenharmony_ci#define ANADIG_USB1_LOOPBACK_UTMI_TESTSTART BIT(0) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define ANADIG_USB2_LOOPBACK_SET 0x244 9962306a36Sopenharmony_ci#define ANADIG_USB2_LOOPBACK_CLR 0x248 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define ANADIG_USB1_MISC 0x1f0 10262306a36Sopenharmony_ci#define ANADIG_USB2_MISC 0x250 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG BIT(12) 10562306a36Sopenharmony_ci#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID BIT(3) 10862306a36Sopenharmony_ci#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID BIT(3) 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 BIT(2) 11162306a36Sopenharmony_ci#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN BIT(5) 11262306a36Sopenharmony_ci#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 BIT(2) 11362306a36Sopenharmony_ci#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN BIT(5) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define BM_ANADIG_USB1_MISC_RX_VPIN_FS BIT(29) 11662306a36Sopenharmony_ci#define BM_ANADIG_USB1_MISC_RX_VMIN_FS BIT(28) 11762306a36Sopenharmony_ci#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29) 11862306a36Sopenharmony_ci#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* Do disconnection between PHY and controller without vbus */ 12362306a36Sopenharmony_ci#define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS BIT(0) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * The PHY will be in messy if there is a wakeup after putting 12762306a36Sopenharmony_ci * bus to suspend (set portsc.suspendM) but before setting PHY to low 12862306a36Sopenharmony_ci * power mode (set portsc.phcd). 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci#define MXS_PHY_ABNORMAL_IN_SUSPEND BIT(1) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * The SOF sends too fast after resuming, it will cause disconnection 13462306a36Sopenharmony_ci * between host and high speed device. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci#define MXS_PHY_SENDING_SOF_TOO_FAST BIT(2) 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/* 13962306a36Sopenharmony_ci * IC has bug fixes logic, they include 14062306a36Sopenharmony_ci * MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST 14162306a36Sopenharmony_ci * which are described at above flags, the RTL will handle it 14262306a36Sopenharmony_ci * according to different versions. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci#define MXS_PHY_NEED_IP_FIX BIT(3) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* Minimum and maximum values for device tree entries */ 14762306a36Sopenharmony_ci#define MXS_PHY_TX_CAL45_MIN 35 14862306a36Sopenharmony_ci#define MXS_PHY_TX_CAL45_MAX 54 14962306a36Sopenharmony_ci#define MXS_PHY_TX_D_CAL_MIN 79 15062306a36Sopenharmony_ci#define MXS_PHY_TX_D_CAL_MAX 119 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistruct mxs_phy_data { 15362306a36Sopenharmony_ci unsigned int flags; 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic const struct mxs_phy_data imx23_phy_data = { 15762306a36Sopenharmony_ci .flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST, 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic const struct mxs_phy_data imx6q_phy_data = { 16162306a36Sopenharmony_ci .flags = MXS_PHY_SENDING_SOF_TOO_FAST | 16262306a36Sopenharmony_ci MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | 16362306a36Sopenharmony_ci MXS_PHY_NEED_IP_FIX, 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const struct mxs_phy_data imx6sl_phy_data = { 16762306a36Sopenharmony_ci .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | 16862306a36Sopenharmony_ci MXS_PHY_NEED_IP_FIX, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic const struct mxs_phy_data vf610_phy_data = { 17262306a36Sopenharmony_ci .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS | 17362306a36Sopenharmony_ci MXS_PHY_NEED_IP_FIX, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct mxs_phy_data imx6sx_phy_data = { 17762306a36Sopenharmony_ci .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic const struct mxs_phy_data imx6ul_phy_data = { 18162306a36Sopenharmony_ci .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic const struct mxs_phy_data imx7ulp_phy_data = { 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic const struct of_device_id mxs_phy_dt_ids[] = { 18862306a36Sopenharmony_ci { .compatible = "fsl,imx6sx-usbphy", .data = &imx6sx_phy_data, }, 18962306a36Sopenharmony_ci { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, }, 19062306a36Sopenharmony_ci { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, }, 19162306a36Sopenharmony_ci { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, }, 19262306a36Sopenharmony_ci { .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, }, 19362306a36Sopenharmony_ci { .compatible = "fsl,imx6ul-usbphy", .data = &imx6ul_phy_data, }, 19462306a36Sopenharmony_ci { .compatible = "fsl,imx7ulp-usbphy", .data = &imx7ulp_phy_data, }, 19562306a36Sopenharmony_ci { /* sentinel */ } 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxs_phy_dt_ids); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistruct mxs_phy { 20062306a36Sopenharmony_ci struct usb_phy phy; 20162306a36Sopenharmony_ci struct clk *clk; 20262306a36Sopenharmony_ci const struct mxs_phy_data *data; 20362306a36Sopenharmony_ci struct regmap *regmap_anatop; 20462306a36Sopenharmony_ci int port_id; 20562306a36Sopenharmony_ci u32 tx_reg_set; 20662306a36Sopenharmony_ci u32 tx_reg_mask; 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline bool is_imx6q_phy(struct mxs_phy *mxs_phy) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci return mxs_phy->data == &imx6q_phy_data; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci return mxs_phy->data == &imx6sl_phy_data; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci return mxs_phy->data == &imx7ulp_phy_data; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* 22562306a36Sopenharmony_ci * PHY needs some 32K cycles to switch from 32K clock to 22662306a36Sopenharmony_ci * bus (such as AHB/AXI, etc) clock. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic void mxs_phy_clock_switch_delay(void) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci usleep_range(300, 400); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void mxs_phy_tx_init(struct mxs_phy *mxs_phy) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci void __iomem *base = mxs_phy->phy.io_priv; 23662306a36Sopenharmony_ci u32 phytx; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* Update TX register if there is anything to write */ 23962306a36Sopenharmony_ci if (mxs_phy->tx_reg_mask) { 24062306a36Sopenharmony_ci phytx = readl(base + HW_USBPHY_TX); 24162306a36Sopenharmony_ci phytx &= ~mxs_phy->tx_reg_mask; 24262306a36Sopenharmony_ci phytx |= mxs_phy->tx_reg_set; 24362306a36Sopenharmony_ci writel(phytx, base + HW_USBPHY_TX); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int mxs_phy_pll_enable(void __iomem *base, bool enable) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci int ret = 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (enable) { 25262306a36Sopenharmony_ci u32 value; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci writel(BM_USBPHY_PLL_REG_ENABLE, base + HW_USBPHY_PLL_SIC_SET); 25562306a36Sopenharmony_ci writel(BM_USBPHY_PLL_BYPASS, base + HW_USBPHY_PLL_SIC_CLR); 25662306a36Sopenharmony_ci writel(BM_USBPHY_PLL_POWER, base + HW_USBPHY_PLL_SIC_SET); 25762306a36Sopenharmony_ci ret = readl_poll_timeout(base + HW_USBPHY_PLL_SIC, 25862306a36Sopenharmony_ci value, (value & BM_USBPHY_PLL_LOCK) != 0, 25962306a36Sopenharmony_ci 100, 10000); 26062306a36Sopenharmony_ci if (ret) 26162306a36Sopenharmony_ci return ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci writel(BM_USBPHY_PLL_EN_USB_CLKS, base + 26462306a36Sopenharmony_ci HW_USBPHY_PLL_SIC_SET); 26562306a36Sopenharmony_ci } else { 26662306a36Sopenharmony_ci writel(BM_USBPHY_PLL_EN_USB_CLKS, base + 26762306a36Sopenharmony_ci HW_USBPHY_PLL_SIC_CLR); 26862306a36Sopenharmony_ci writel(BM_USBPHY_PLL_POWER, base + HW_USBPHY_PLL_SIC_CLR); 26962306a36Sopenharmony_ci writel(BM_USBPHY_PLL_BYPASS, base + HW_USBPHY_PLL_SIC_SET); 27062306a36Sopenharmony_ci writel(BM_USBPHY_PLL_REG_ENABLE, base + HW_USBPHY_PLL_SIC_CLR); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return ret; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int mxs_phy_hw_init(struct mxs_phy *mxs_phy) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci int ret; 27962306a36Sopenharmony_ci void __iomem *base = mxs_phy->phy.io_priv; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (is_imx7ulp_phy(mxs_phy)) { 28262306a36Sopenharmony_ci ret = mxs_phy_pll_enable(base, true); 28362306a36Sopenharmony_ci if (ret) 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ret = stmp_reset_block(base + HW_USBPHY_CTRL); 28862306a36Sopenharmony_ci if (ret) 28962306a36Sopenharmony_ci goto disable_pll; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Power up the PHY */ 29262306a36Sopenharmony_ci writel(0, base + HW_USBPHY_PWD); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * USB PHY Ctrl Setting 29662306a36Sopenharmony_ci * - Auto clock/power on 29762306a36Sopenharmony_ci * - Enable full/low speed support 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | 30062306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | 30162306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | 30262306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | 30362306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTO_PWRON_PLL | 30462306a36Sopenharmony_ci BM_USBPHY_CTRL_ENUTMILEVEL2 | 30562306a36Sopenharmony_ci BM_USBPHY_CTRL_ENUTMILEVEL3, 30662306a36Sopenharmony_ci base + HW_USBPHY_CTRL_SET); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX) 30962306a36Sopenharmony_ci writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (mxs_phy->regmap_anatop) { 31262306a36Sopenharmony_ci unsigned int reg = mxs_phy->port_id ? 31362306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_SET : 31462306a36Sopenharmony_ci ANADIG_USB2_CHRG_DETECT_SET; 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * The external charger detector needs to be disabled, 31762306a36Sopenharmony_ci * or the signal at DP will be poor 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci regmap_write(mxs_phy->regmap_anatop, reg, 32062306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_EN_B | 32162306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci mxs_phy_tx_init(mxs_phy); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cidisable_pll: 32962306a36Sopenharmony_ci if (is_imx7ulp_phy(mxs_phy)) 33062306a36Sopenharmony_ci mxs_phy_pll_enable(base, false); 33162306a36Sopenharmony_ci return ret; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/* Return true if the vbus is there */ 33562306a36Sopenharmony_cistatic bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci unsigned int vbus_value = 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (!mxs_phy->regmap_anatop) 34062306a36Sopenharmony_ci return false; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (mxs_phy->port_id == 0) 34362306a36Sopenharmony_ci regmap_read(mxs_phy->regmap_anatop, 34462306a36Sopenharmony_ci ANADIG_USB1_VBUS_DET_STAT, 34562306a36Sopenharmony_ci &vbus_value); 34662306a36Sopenharmony_ci else if (mxs_phy->port_id == 1) 34762306a36Sopenharmony_ci regmap_read(mxs_phy->regmap_anatop, 34862306a36Sopenharmony_ci ANADIG_USB2_VBUS_DET_STAT, 34962306a36Sopenharmony_ci &vbus_value); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID) 35262306a36Sopenharmony_ci return true; 35362306a36Sopenharmony_ci else 35462306a36Sopenharmony_ci return false; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci void __iomem *base = mxs_phy->phy.io_priv; 36062306a36Sopenharmony_ci u32 reg; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (disconnect) 36362306a36Sopenharmony_ci writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, 36462306a36Sopenharmony_ci base + HW_USBPHY_DEBUG_CLR); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (mxs_phy->port_id == 0) { 36762306a36Sopenharmony_ci reg = disconnect ? ANADIG_USB1_LOOPBACK_SET 36862306a36Sopenharmony_ci : ANADIG_USB1_LOOPBACK_CLR; 36962306a36Sopenharmony_ci regmap_write(mxs_phy->regmap_anatop, reg, 37062306a36Sopenharmony_ci BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 | 37162306a36Sopenharmony_ci BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN); 37262306a36Sopenharmony_ci } else if (mxs_phy->port_id == 1) { 37362306a36Sopenharmony_ci reg = disconnect ? ANADIG_USB2_LOOPBACK_SET 37462306a36Sopenharmony_ci : ANADIG_USB2_LOOPBACK_CLR; 37562306a36Sopenharmony_ci regmap_write(mxs_phy->regmap_anatop, reg, 37662306a36Sopenharmony_ci BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 | 37762306a36Sopenharmony_ci BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (!disconnect) 38162306a36Sopenharmony_ci writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, 38262306a36Sopenharmony_ci base + HW_USBPHY_DEBUG_SET); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Delay some time, and let Linestate be SE0 for controller */ 38562306a36Sopenharmony_ci if (disconnect) 38662306a36Sopenharmony_ci usleep_range(500, 1000); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci return mxs_phy->phy.last_event == USB_EVENT_ID; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci bool vbus_is_on = false; 39762306a36Sopenharmony_ci enum usb_phy_events last_event = mxs_phy->phy.last_event; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* If the SoCs don't need to disconnect line without vbus, quit */ 40062306a36Sopenharmony_ci if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS)) 40162306a36Sopenharmony_ci return; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* If the SoCs don't have anatop, quit */ 40462306a36Sopenharmony_ci if (!mxs_phy->regmap_anatop) 40562306a36Sopenharmony_ci return; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (on && ((!vbus_is_on && !mxs_phy_is_otg_host(mxs_phy)) 41062306a36Sopenharmony_ci || (last_event == USB_EVENT_VBUS))) 41162306a36Sopenharmony_ci __mxs_phy_disconnect_line(mxs_phy, true); 41262306a36Sopenharmony_ci else 41362306a36Sopenharmony_ci __mxs_phy_disconnect_line(mxs_phy, false); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int mxs_phy_init(struct usb_phy *phy) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci int ret; 42062306a36Sopenharmony_ci struct mxs_phy *mxs_phy = to_mxs_phy(phy); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci mxs_phy_clock_switch_delay(); 42362306a36Sopenharmony_ci ret = clk_prepare_enable(mxs_phy->clk); 42462306a36Sopenharmony_ci if (ret) 42562306a36Sopenharmony_ci return ret; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return mxs_phy_hw_init(mxs_phy); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic void mxs_phy_shutdown(struct usb_phy *phy) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct mxs_phy *mxs_phy = to_mxs_phy(phy); 43362306a36Sopenharmony_ci u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | 43462306a36Sopenharmony_ci BM_USBPHY_CTRL_ENDPDMCHG_WKUP | 43562306a36Sopenharmony_ci BM_USBPHY_CTRL_ENIDCHG_WKUP | 43662306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTOSET_USBCLKS | 43762306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE | 43862306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD | 43962306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE | 44062306a36Sopenharmony_ci BM_USBPHY_CTRL_ENAUTO_PWRON_PLL; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR); 44362306a36Sopenharmony_ci writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci writel(BM_USBPHY_CTRL_CLKGATE, 44662306a36Sopenharmony_ci phy->io_priv + HW_USBPHY_CTRL_SET); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (is_imx7ulp_phy(mxs_phy)) 44962306a36Sopenharmony_ci mxs_phy_pll_enable(phy->io_priv, false); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci clk_disable_unprepare(mxs_phy->clk); 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic bool mxs_phy_is_low_speed_connection(struct mxs_phy *mxs_phy) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci unsigned int line_state; 45762306a36Sopenharmony_ci /* bit definition is the same for all controllers */ 45862306a36Sopenharmony_ci unsigned int dp_bit = BM_ANADIG_USB1_MISC_RX_VPIN_FS, 45962306a36Sopenharmony_ci dm_bit = BM_ANADIG_USB1_MISC_RX_VMIN_FS; 46062306a36Sopenharmony_ci unsigned int reg = ANADIG_USB1_MISC; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* If the SoCs don't have anatop, quit */ 46362306a36Sopenharmony_ci if (!mxs_phy->regmap_anatop) 46462306a36Sopenharmony_ci return false; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (mxs_phy->port_id == 0) 46762306a36Sopenharmony_ci reg = ANADIG_USB1_MISC; 46862306a36Sopenharmony_ci else if (mxs_phy->port_id == 1) 46962306a36Sopenharmony_ci reg = ANADIG_USB2_MISC; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci regmap_read(mxs_phy->regmap_anatop, reg, &line_state); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if ((line_state & (dp_bit | dm_bit)) == dm_bit) 47462306a36Sopenharmony_ci return true; 47562306a36Sopenharmony_ci else 47662306a36Sopenharmony_ci return false; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int mxs_phy_suspend(struct usb_phy *x, int suspend) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci int ret; 48262306a36Sopenharmony_ci struct mxs_phy *mxs_phy = to_mxs_phy(x); 48362306a36Sopenharmony_ci bool low_speed_connection, vbus_is_on; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci low_speed_connection = mxs_phy_is_low_speed_connection(mxs_phy); 48662306a36Sopenharmony_ci vbus_is_on = mxs_phy_get_vbus_status(mxs_phy); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (suspend) { 48962306a36Sopenharmony_ci /* 49062306a36Sopenharmony_ci * FIXME: Do not power down RXPWD1PT1 bit for low speed 49162306a36Sopenharmony_ci * connect. The low speed connection will have problem at 49262306a36Sopenharmony_ci * very rare cases during usb suspend and resume process. 49362306a36Sopenharmony_ci */ 49462306a36Sopenharmony_ci if (low_speed_connection & vbus_is_on) { 49562306a36Sopenharmony_ci /* 49662306a36Sopenharmony_ci * If value to be set as pwd value is not 0xffffffff, 49762306a36Sopenharmony_ci * several 32Khz cycles are needed. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci mxs_phy_clock_switch_delay(); 50062306a36Sopenharmony_ci writel(0xffbfffff, x->io_priv + HW_USBPHY_PWD); 50162306a36Sopenharmony_ci } else { 50262306a36Sopenharmony_ci writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci writel(BM_USBPHY_CTRL_CLKGATE, 50562306a36Sopenharmony_ci x->io_priv + HW_USBPHY_CTRL_SET); 50662306a36Sopenharmony_ci clk_disable_unprepare(mxs_phy->clk); 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci mxs_phy_clock_switch_delay(); 50962306a36Sopenharmony_ci ret = clk_prepare_enable(mxs_phy->clk); 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci writel(BM_USBPHY_CTRL_CLKGATE, 51362306a36Sopenharmony_ci x->io_priv + HW_USBPHY_CTRL_CLR); 51462306a36Sopenharmony_ci writel(0, x->io_priv + HW_USBPHY_PWD); 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct mxs_phy *mxs_phy = to_mxs_phy(x); 52362306a36Sopenharmony_ci u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP | 52462306a36Sopenharmony_ci BM_USBPHY_CTRL_ENDPDMCHG_WKUP | 52562306a36Sopenharmony_ci BM_USBPHY_CTRL_ENIDCHG_WKUP; 52662306a36Sopenharmony_ci if (enabled) { 52762306a36Sopenharmony_ci mxs_phy_disconnect_line(mxs_phy, true); 52862306a36Sopenharmony_ci writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET); 52962306a36Sopenharmony_ci } else { 53062306a36Sopenharmony_ci writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR); 53162306a36Sopenharmony_ci mxs_phy_disconnect_line(mxs_phy, false); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistatic int mxs_phy_on_connect(struct usb_phy *phy, 53862306a36Sopenharmony_ci enum usb_device_speed speed) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci dev_dbg(phy->dev, "%s device has connected\n", 54162306a36Sopenharmony_ci (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (speed == USB_SPEED_HIGH) 54462306a36Sopenharmony_ci writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, 54562306a36Sopenharmony_ci phy->io_priv + HW_USBPHY_CTRL_SET); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic int mxs_phy_on_disconnect(struct usb_phy *phy, 55162306a36Sopenharmony_ci enum usb_device_speed speed) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci dev_dbg(phy->dev, "%s device has disconnected\n", 55462306a36Sopenharmony_ci (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS"); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* Sometimes, the speed is not high speed when the error occurs */ 55762306a36Sopenharmony_ci if (readl(phy->io_priv + HW_USBPHY_CTRL) & 55862306a36Sopenharmony_ci BM_USBPHY_CTRL_ENHOSTDISCONDETECT) 55962306a36Sopenharmony_ci writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT, 56062306a36Sopenharmony_ci phy->io_priv + HW_USBPHY_CTRL_CLR); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci#define MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT 100 56662306a36Sopenharmony_cistatic int mxs_charger_data_contact_detect(struct mxs_phy *x) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct regmap *regmap = x->regmap_anatop; 56962306a36Sopenharmony_ci int i, stable_contact_count = 0; 57062306a36Sopenharmony_ci u32 val; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Check if vbus is valid */ 57362306a36Sopenharmony_ci regmap_read(regmap, ANADIG_USB1_VBUS_DET_STAT, &val); 57462306a36Sopenharmony_ci if (!(val & ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)) { 57562306a36Sopenharmony_ci dev_err(x->phy.dev, "vbus is not valid\n"); 57662306a36Sopenharmony_ci return -EINVAL; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Enable charger detector */ 58062306a36Sopenharmony_ci regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_CLR, 58162306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_EN_B); 58262306a36Sopenharmony_ci /* 58362306a36Sopenharmony_ci * - Do not check whether a charger is connected to the USB port 58462306a36Sopenharmony_ci * - Check whether the USB plug has been in contact with each other 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_ci regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, 58762306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_CHK_CONTACT | 58862306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Check if plug is connected */ 59162306a36Sopenharmony_ci for (i = 0; i < MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT; i++) { 59262306a36Sopenharmony_ci regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); 59362306a36Sopenharmony_ci if (val & ANADIG_USB1_CHRG_DET_STAT_PLUG_CONTACT) { 59462306a36Sopenharmony_ci stable_contact_count++; 59562306a36Sopenharmony_ci if (stable_contact_count > 5) 59662306a36Sopenharmony_ci /* Data pin makes contact */ 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci else 59962306a36Sopenharmony_ci usleep_range(5000, 10000); 60062306a36Sopenharmony_ci } else { 60162306a36Sopenharmony_ci stable_contact_count = 0; 60262306a36Sopenharmony_ci usleep_range(5000, 6000); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (i == MXS_USB_CHARGER_DATA_CONTACT_TIMEOUT) { 60762306a36Sopenharmony_ci dev_err(x->phy.dev, 60862306a36Sopenharmony_ci "Data pin can't make good contact.\n"); 60962306a36Sopenharmony_ci /* Disable charger detector */ 61062306a36Sopenharmony_ci regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, 61162306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_EN_B | 61262306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); 61362306a36Sopenharmony_ci return -ENXIO; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct regmap *regmap = x->regmap_anatop; 62262306a36Sopenharmony_ci enum usb_charger_type chgr_type = UNKNOWN_TYPE; 62362306a36Sopenharmony_ci u32 val; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * - Do check whether a charger is connected to the USB port 62762306a36Sopenharmony_ci * - Do not Check whether the USB plug has been in contact with 62862306a36Sopenharmony_ci * each other 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_CLR, 63162306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_CHK_CONTACT | 63262306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci msleep(100); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Check if it is a charger */ 63762306a36Sopenharmony_ci regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); 63862306a36Sopenharmony_ci if (!(val & ANADIG_USB1_CHRG_DET_STAT_CHRG_DETECTED)) { 63962306a36Sopenharmony_ci chgr_type = SDP_TYPE; 64062306a36Sopenharmony_ci dev_dbg(x->phy.dev, "It is a standard downstream port\n"); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* Disable charger detector */ 64462306a36Sopenharmony_ci regmap_write(regmap, ANADIG_USB1_CHRG_DETECT_SET, 64562306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_EN_B | 64662306a36Sopenharmony_ci ANADIG_USB1_CHRG_DETECT_CHK_CHRG_B); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return chgr_type; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/* 65262306a36Sopenharmony_ci * It must be called after DP is pulled up, which is used to 65362306a36Sopenharmony_ci * differentiate DCP and CDP. 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_cistatic enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct regmap *regmap = x->regmap_anatop; 65862306a36Sopenharmony_ci int val; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci msleep(80); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci regmap_read(regmap, ANADIG_USB1_CHRG_DET_STAT, &val); 66362306a36Sopenharmony_ci if (val & ANADIG_USB1_CHRG_DET_STAT_DM_STATE) { 66462306a36Sopenharmony_ci dev_dbg(x->phy.dev, "It is a dedicate charging port\n"); 66562306a36Sopenharmony_ci return DCP_TYPE; 66662306a36Sopenharmony_ci } else { 66762306a36Sopenharmony_ci dev_dbg(x->phy.dev, "It is a charging downstream port\n"); 66862306a36Sopenharmony_ci return CDP_TYPE; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct mxs_phy *mxs_phy = to_mxs_phy(phy); 67562306a36Sopenharmony_ci struct regmap *regmap = mxs_phy->regmap_anatop; 67662306a36Sopenharmony_ci void __iomem *base = phy->io_priv; 67762306a36Sopenharmony_ci enum usb_charger_type chgr_type = UNKNOWN_TYPE; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (!regmap) 68062306a36Sopenharmony_ci return UNKNOWN_TYPE; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (mxs_charger_data_contact_detect(mxs_phy)) 68362306a36Sopenharmony_ci return chgr_type; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci chgr_type = mxs_charger_primary_detection(mxs_phy); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (chgr_type != SDP_TYPE) { 68862306a36Sopenharmony_ci /* Pull up DP via test */ 68962306a36Sopenharmony_ci writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, 69062306a36Sopenharmony_ci base + HW_USBPHY_DEBUG_CLR); 69162306a36Sopenharmony_ci regmap_write(regmap, ANADIG_USB1_LOOPBACK_SET, 69262306a36Sopenharmony_ci ANADIG_USB1_LOOPBACK_UTMI_TESTSTART); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci chgr_type = mxs_charger_secondary_detection(mxs_phy); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Stop the test */ 69762306a36Sopenharmony_ci regmap_write(regmap, ANADIG_USB1_LOOPBACK_CLR, 69862306a36Sopenharmony_ci ANADIG_USB1_LOOPBACK_UTMI_TESTSTART); 69962306a36Sopenharmony_ci writel_relaxed(BM_USBPHY_DEBUG_CLKGATE, 70062306a36Sopenharmony_ci base + HW_USBPHY_DEBUG_SET); 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return chgr_type; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int mxs_phy_probe(struct platform_device *pdev) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci void __iomem *base; 70962306a36Sopenharmony_ci struct clk *clk; 71062306a36Sopenharmony_ci struct mxs_phy *mxs_phy; 71162306a36Sopenharmony_ci int ret; 71262306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 71362306a36Sopenharmony_ci u32 val; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 71662306a36Sopenharmony_ci if (IS_ERR(base)) 71762306a36Sopenharmony_ci return PTR_ERR(base); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci clk = devm_clk_get(&pdev->dev, NULL); 72062306a36Sopenharmony_ci if (IS_ERR(clk)) { 72162306a36Sopenharmony_ci dev_err(&pdev->dev, 72262306a36Sopenharmony_ci "can't get the clock, err=%ld", PTR_ERR(clk)); 72362306a36Sopenharmony_ci return PTR_ERR(clk); 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); 72762306a36Sopenharmony_ci if (!mxs_phy) 72862306a36Sopenharmony_ci return -ENOMEM; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Some SoCs don't have anatop registers */ 73162306a36Sopenharmony_ci if (of_property_present(np, "fsl,anatop")) { 73262306a36Sopenharmony_ci mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle 73362306a36Sopenharmony_ci (np, "fsl,anatop"); 73462306a36Sopenharmony_ci if (IS_ERR(mxs_phy->regmap_anatop)) { 73562306a36Sopenharmony_ci dev_dbg(&pdev->dev, 73662306a36Sopenharmony_ci "failed to find regmap for anatop\n"); 73762306a36Sopenharmony_ci return PTR_ERR(mxs_phy->regmap_anatop); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Precompute which bits of the TX register are to be updated, if any */ 74262306a36Sopenharmony_ci if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) && 74362306a36Sopenharmony_ci val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) { 74462306a36Sopenharmony_ci /* Scale to a 4-bit value */ 74562306a36Sopenharmony_ci val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF 74662306a36Sopenharmony_ci / (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN); 74762306a36Sopenharmony_ci mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DN(~0); 74862306a36Sopenharmony_ci mxs_phy->tx_reg_set |= GM_USBPHY_TX_TXCAL45DN(val); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (!of_property_read_u32(np, "fsl,tx-cal-45-dp-ohms", &val) && 75262306a36Sopenharmony_ci val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) { 75362306a36Sopenharmony_ci /* Scale to a 4-bit value. */ 75462306a36Sopenharmony_ci val = (MXS_PHY_TX_CAL45_MAX - val) * 0xF 75562306a36Sopenharmony_ci / (MXS_PHY_TX_CAL45_MAX - MXS_PHY_TX_CAL45_MIN); 75662306a36Sopenharmony_ci mxs_phy->tx_reg_mask |= GM_USBPHY_TX_TXCAL45DP(~0); 75762306a36Sopenharmony_ci mxs_phy->tx_reg_set |= GM_USBPHY_TX_TXCAL45DP(val); 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (!of_property_read_u32(np, "fsl,tx-d-cal", &val) && 76162306a36Sopenharmony_ci val >= MXS_PHY_TX_D_CAL_MIN && val <= MXS_PHY_TX_D_CAL_MAX) { 76262306a36Sopenharmony_ci /* Scale to a 4-bit value. Round up the values and heavily 76362306a36Sopenharmony_ci * weight the rounding by adding 2/3 of the denominator. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci val = ((MXS_PHY_TX_D_CAL_MAX - val) * 0xF 76662306a36Sopenharmony_ci + (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN) * 2/3) 76762306a36Sopenharmony_ci / (MXS_PHY_TX_D_CAL_MAX - MXS_PHY_TX_D_CAL_MIN); 76862306a36Sopenharmony_ci mxs_phy->tx_reg_mask |= GM_USBPHY_TX_D_CAL(~0); 76962306a36Sopenharmony_ci mxs_phy->tx_reg_set |= GM_USBPHY_TX_D_CAL(val); 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci ret = of_alias_get_id(np, "usbphy"); 77362306a36Sopenharmony_ci if (ret < 0) 77462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret); 77562306a36Sopenharmony_ci mxs_phy->port_id = ret; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mxs_phy->phy.io_priv = base; 77862306a36Sopenharmony_ci mxs_phy->phy.dev = &pdev->dev; 77962306a36Sopenharmony_ci mxs_phy->phy.label = DRIVER_NAME; 78062306a36Sopenharmony_ci mxs_phy->phy.init = mxs_phy_init; 78162306a36Sopenharmony_ci mxs_phy->phy.shutdown = mxs_phy_shutdown; 78262306a36Sopenharmony_ci mxs_phy->phy.set_suspend = mxs_phy_suspend; 78362306a36Sopenharmony_ci mxs_phy->phy.notify_connect = mxs_phy_on_connect; 78462306a36Sopenharmony_ci mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect; 78562306a36Sopenharmony_ci mxs_phy->phy.type = USB_PHY_TYPE_USB2; 78662306a36Sopenharmony_ci mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup; 78762306a36Sopenharmony_ci mxs_phy->phy.charger_detect = mxs_phy_charger_detect; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci mxs_phy->clk = clk; 79062306a36Sopenharmony_ci mxs_phy->data = of_device_get_match_data(&pdev->dev); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci platform_set_drvdata(pdev, mxs_phy); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci device_set_wakeup_capable(&pdev->dev, true); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return usb_add_phy_dev(&mxs_phy->phy); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic void mxs_phy_remove(struct platform_device *pdev) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct mxs_phy *mxs_phy = platform_get_drvdata(pdev); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci usb_remove_phy(&mxs_phy->phy); 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 80762306a36Sopenharmony_cistatic void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* If the SoCs don't have anatop, quit */ 81262306a36Sopenharmony_ci if (!mxs_phy->regmap_anatop) 81362306a36Sopenharmony_ci return; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (is_imx6q_phy(mxs_phy)) 81662306a36Sopenharmony_ci regmap_write(mxs_phy->regmap_anatop, reg, 81762306a36Sopenharmony_ci BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG); 81862306a36Sopenharmony_ci else if (is_imx6sl_phy(mxs_phy)) 81962306a36Sopenharmony_ci regmap_write(mxs_phy->regmap_anatop, 82062306a36Sopenharmony_ci reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL); 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic int mxs_phy_system_suspend(struct device *dev) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci struct mxs_phy *mxs_phy = dev_get_drvdata(dev); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (device_may_wakeup(dev)) 82862306a36Sopenharmony_ci mxs_phy_enable_ldo_in_suspend(mxs_phy, true); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int mxs_phy_system_resume(struct device *dev) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct mxs_phy *mxs_phy = dev_get_drvdata(dev); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (device_may_wakeup(dev)) 83862306a36Sopenharmony_ci mxs_phy_enable_ldo_in_suspend(mxs_phy, false); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend, 84562306a36Sopenharmony_ci mxs_phy_system_resume); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic struct platform_driver mxs_phy_driver = { 84862306a36Sopenharmony_ci .probe = mxs_phy_probe, 84962306a36Sopenharmony_ci .remove_new = mxs_phy_remove, 85062306a36Sopenharmony_ci .driver = { 85162306a36Sopenharmony_ci .name = DRIVER_NAME, 85262306a36Sopenharmony_ci .of_match_table = mxs_phy_dt_ids, 85362306a36Sopenharmony_ci .pm = &mxs_phy_pm, 85462306a36Sopenharmony_ci }, 85562306a36Sopenharmony_ci}; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int __init mxs_phy_module_init(void) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci return platform_driver_register(&mxs_phy_driver); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_cipostcore_initcall(mxs_phy_module_init); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic void __exit mxs_phy_module_exit(void) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci platform_driver_unregister(&mxs_phy_driver); 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_cimodule_exit(mxs_phy_module_exit); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ciMODULE_ALIAS("platform:mxs-usb-phy"); 87062306a36Sopenharmony_ciMODULE_AUTHOR("Marek Vasut <marex@denx.de>"); 87162306a36Sopenharmony_ciMODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>"); 87262306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale MXS USB PHY driver"); 87362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 874