1ef19b117SAnsuel Smith // SPDX-License-Identifier: GPL-2.0-only 2ef19b117SAnsuel Smith 3ef19b117SAnsuel Smith #include <linux/clk.h> 4ef19b117SAnsuel Smith #include <linux/err.h> 5ef19b117SAnsuel Smith #include <linux/io.h> 6ef19b117SAnsuel Smith #include <linux/module.h> 7ef19b117SAnsuel Smith #include <linux/of_device.h> 8ef19b117SAnsuel Smith #include <linux/phy/phy.h> 9ef19b117SAnsuel Smith #include <linux/platform_device.h> 10ef19b117SAnsuel Smith #include <linux/delay.h> 11ef19b117SAnsuel Smith #include <linux/regmap.h> 12ef19b117SAnsuel Smith #include <linux/mfd/syscon.h> 13ef19b117SAnsuel Smith 14ef19b117SAnsuel Smith /* USB QSCRATCH Hardware registers */ 15ef19b117SAnsuel Smith #define QSCRATCH_GENERAL_CFG (0x08) 16ef19b117SAnsuel Smith #define HSUSB_PHY_CTRL_REG (0x10) 17ef19b117SAnsuel Smith 18ef19b117SAnsuel Smith /* PHY_CTRL_REG */ 19ef19b117SAnsuel Smith #define HSUSB_CTRL_DMSEHV_CLAMP BIT(24) 20ef19b117SAnsuel Smith #define HSUSB_CTRL_USB2_SUSPEND BIT(23) 21ef19b117SAnsuel Smith #define HSUSB_CTRL_UTMI_CLK_EN BIT(21) 22ef19b117SAnsuel Smith #define HSUSB_CTRL_UTMI_OTG_VBUS_VALID BIT(20) 23ef19b117SAnsuel Smith #define HSUSB_CTRL_USE_CLKCORE BIT(18) 24ef19b117SAnsuel Smith #define HSUSB_CTRL_DPSEHV_CLAMP BIT(17) 25ef19b117SAnsuel Smith #define HSUSB_CTRL_COMMONONN BIT(11) 26ef19b117SAnsuel Smith #define HSUSB_CTRL_ID_HV_CLAMP BIT(9) 27ef19b117SAnsuel Smith #define HSUSB_CTRL_OTGSESSVLD_CLAMP BIT(8) 28ef19b117SAnsuel Smith #define HSUSB_CTRL_CLAMP_EN BIT(7) 29ef19b117SAnsuel Smith #define HSUSB_CTRL_RETENABLEN BIT(1) 30ef19b117SAnsuel Smith #define HSUSB_CTRL_POR BIT(0) 31ef19b117SAnsuel Smith 32ef19b117SAnsuel Smith /* QSCRATCH_GENERAL_CFG */ 33ef19b117SAnsuel Smith #define HSUSB_GCFG_XHCI_REV BIT(2) 34ef19b117SAnsuel Smith 35ef19b117SAnsuel Smith /* USB QSCRATCH Hardware registers */ 36ef19b117SAnsuel Smith #define SSUSB_PHY_CTRL_REG (0x00) 37ef19b117SAnsuel Smith #define SSUSB_PHY_PARAM_CTRL_1 (0x04) 38ef19b117SAnsuel Smith #define SSUSB_PHY_PARAM_CTRL_2 (0x08) 39ef19b117SAnsuel Smith #define CR_PROTOCOL_DATA_IN_REG (0x0c) 40ef19b117SAnsuel Smith #define CR_PROTOCOL_DATA_OUT_REG (0x10) 41ef19b117SAnsuel Smith #define CR_PROTOCOL_CAP_ADDR_REG (0x14) 42ef19b117SAnsuel Smith #define CR_PROTOCOL_CAP_DATA_REG (0x18) 43ef19b117SAnsuel Smith #define CR_PROTOCOL_READ_REG (0x1c) 44ef19b117SAnsuel Smith #define CR_PROTOCOL_WRITE_REG (0x20) 45ef19b117SAnsuel Smith 46ef19b117SAnsuel Smith /* PHY_CTRL_REG */ 47ef19b117SAnsuel Smith #define SSUSB_CTRL_REF_USE_PAD BIT(28) 48ef19b117SAnsuel Smith #define SSUSB_CTRL_TEST_POWERDOWN BIT(27) 49ef19b117SAnsuel Smith #define SSUSB_CTRL_LANE0_PWR_PRESENT BIT(24) 50ef19b117SAnsuel Smith #define SSUSB_CTRL_SS_PHY_EN BIT(8) 51ef19b117SAnsuel Smith #define SSUSB_CTRL_SS_PHY_RESET BIT(7) 52ef19b117SAnsuel Smith 53ef19b117SAnsuel Smith /* SSPHY control registers - Does this need 0x30? */ 54ef19b117SAnsuel Smith #define SSPHY_CTRL_RX_OVRD_IN_HI(lane) (0x1006 + 0x100 * (lane)) 55ef19b117SAnsuel Smith #define SSPHY_CTRL_TX_OVRD_DRV_LO(lane) (0x1002 + 0x100 * (lane)) 56ef19b117SAnsuel Smith 57ef19b117SAnsuel Smith /* SSPHY SoC version specific values */ 58ef19b117SAnsuel Smith #define SSPHY_RX_EQ_VALUE 4 /* Override value for rx_eq */ 59ef19b117SAnsuel Smith /* Override value for transmit preemphasis */ 60ef19b117SAnsuel Smith #define SSPHY_TX_DEEMPH_3_5DB 23 61ef19b117SAnsuel Smith /* Override value for mpll */ 62ef19b117SAnsuel Smith #define SSPHY_MPLL_VALUE 0 63ef19b117SAnsuel Smith 64ef19b117SAnsuel Smith /* QSCRATCH PHY_PARAM_CTRL1 fields */ 65ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_TX_FULL_SWING_MASK GENMASK(26, 19) 66ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK GENMASK(19, 13) 67ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK GENMASK(13, 7) 68ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_LOS_BIAS_MASK GENMASK(7, 2) 69ef19b117SAnsuel Smith 70ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_MASK \ 71ef19b117SAnsuel Smith (PHY_PARAM_CTRL1_TX_FULL_SWING_MASK | \ 72ef19b117SAnsuel Smith PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK | \ 73ef19b117SAnsuel Smith PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK | \ 74ef19b117SAnsuel Smith PHY_PARAM_CTRL1_LOS_BIAS_MASK) 75ef19b117SAnsuel Smith 76ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_TX_FULL_SWING(x) \ 77ef19b117SAnsuel Smith (((x) << 20) & PHY_PARAM_CTRL1_TX_FULL_SWING_MASK) 78ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_TX_DEEMPH_6DB(x) \ 79ef19b117SAnsuel Smith (((x) << 14) & PHY_PARAM_CTRL1_TX_DEEMPH_6DB_MASK) 80ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(x) \ 81ef19b117SAnsuel Smith (((x) << 8) & PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB_MASK) 82ef19b117SAnsuel Smith #define PHY_PARAM_CTRL1_LOS_BIAS(x) \ 83ef19b117SAnsuel Smith (((x) << 3) & PHY_PARAM_CTRL1_LOS_BIAS_MASK) 84ef19b117SAnsuel Smith 85ef19b117SAnsuel Smith /* RX OVRD IN HI bits */ 86ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_RESET_OVRD BIT(13) 87ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_RX_RESET BIT(12) 88ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_EQ_OVRD BIT(11) 89ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_EQ_MASK GENMASK(10, 7) 90ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_EQ(x) ((x) << 8) 91ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_EQ_EN_OVRD BIT(7) 92ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_EQ_EN BIT(6) 93ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_LOS_FILTER_OVRD BIT(5) 94ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_LOS_FILTER_MASK GENMASK(4, 2) 95ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_RATE_OVRD BIT(2) 96ef19b117SAnsuel Smith #define RX_OVRD_IN_HI_RX_RATE_MASK GENMASK(2, 0) 97ef19b117SAnsuel Smith 98ef19b117SAnsuel Smith /* TX OVRD DRV LO register bits */ 99ef19b117SAnsuel Smith #define TX_OVRD_DRV_LO_AMPLITUDE_MASK GENMASK(6, 0) 100ef19b117SAnsuel Smith #define TX_OVRD_DRV_LO_PREEMPH_MASK GENMASK(13, 6) 101ef19b117SAnsuel Smith #define TX_OVRD_DRV_LO_PREEMPH(x) ((x) << 7) 102ef19b117SAnsuel Smith #define TX_OVRD_DRV_LO_EN BIT(14) 103ef19b117SAnsuel Smith 104ef19b117SAnsuel Smith /* MPLL bits */ 105ef19b117SAnsuel Smith #define SSPHY_MPLL_MASK GENMASK(8, 5) 106ef19b117SAnsuel Smith #define SSPHY_MPLL(x) ((x) << 5) 107ef19b117SAnsuel Smith 108ef19b117SAnsuel Smith /* SS CAP register bits */ 109ef19b117SAnsuel Smith #define SS_CR_CAP_ADDR_REG BIT(0) 110ef19b117SAnsuel Smith #define SS_CR_CAP_DATA_REG BIT(0) 111ef19b117SAnsuel Smith #define SS_CR_READ_REG BIT(0) 112ef19b117SAnsuel Smith #define SS_CR_WRITE_REG BIT(0) 113ef19b117SAnsuel Smith 114ef19b117SAnsuel Smith struct usb_phy { 115ef19b117SAnsuel Smith void __iomem *base; 116ef19b117SAnsuel Smith struct device *dev; 117ef19b117SAnsuel Smith struct clk *xo_clk; 118ef19b117SAnsuel Smith struct clk *ref_clk; 119ef19b117SAnsuel Smith u32 rx_eq; 120ef19b117SAnsuel Smith u32 tx_deamp_3_5db; 121ef19b117SAnsuel Smith u32 mpll; 122ef19b117SAnsuel Smith }; 123ef19b117SAnsuel Smith 124ef19b117SAnsuel Smith struct phy_drvdata { 125ef19b117SAnsuel Smith struct phy_ops ops; 126ef19b117SAnsuel Smith u32 clk_rate; 127ef19b117SAnsuel Smith }; 128ef19b117SAnsuel Smith 129ef19b117SAnsuel Smith /** 130ef19b117SAnsuel Smith * Write register and read back masked value to confirm it is written 131ef19b117SAnsuel Smith * 132ef19b117SAnsuel Smith * @base - QCOM DWC3 PHY base virtual address. 133ef19b117SAnsuel Smith * @offset - register offset. 134ef19b117SAnsuel Smith * @mask - register bitmask specifying what should be updated 135ef19b117SAnsuel Smith * @val - value to write. 136ef19b117SAnsuel Smith */ 137ef19b117SAnsuel Smith static inline void usb_phy_write_readback(struct usb_phy *phy_dwc3, 138ef19b117SAnsuel Smith u32 offset, 139ef19b117SAnsuel Smith const u32 mask, u32 val) 140ef19b117SAnsuel Smith { 141ef19b117SAnsuel Smith u32 write_val, tmp = readl(phy_dwc3->base + offset); 142ef19b117SAnsuel Smith 143ef19b117SAnsuel Smith tmp &= ~mask; /* retain other bits */ 144ef19b117SAnsuel Smith write_val = tmp | val; 145ef19b117SAnsuel Smith 146ef19b117SAnsuel Smith writel(write_val, phy_dwc3->base + offset); 147ef19b117SAnsuel Smith 148ef19b117SAnsuel Smith /* Read back to see if val was written */ 149ef19b117SAnsuel Smith tmp = readl(phy_dwc3->base + offset); 150ef19b117SAnsuel Smith tmp &= mask; /* clear other bits */ 151ef19b117SAnsuel Smith 152ef19b117SAnsuel Smith if (tmp != val) 153ef19b117SAnsuel Smith dev_err(phy_dwc3->dev, "write: %x to QSCRATCH: %x FAILED\n", val, offset); 154ef19b117SAnsuel Smith } 155ef19b117SAnsuel Smith 156ef19b117SAnsuel Smith static int wait_for_latch(void __iomem *addr) 157ef19b117SAnsuel Smith { 158ef19b117SAnsuel Smith u32 retry = 10; 159ef19b117SAnsuel Smith 160ef19b117SAnsuel Smith while (true) { 161ef19b117SAnsuel Smith if (!readl(addr)) 162ef19b117SAnsuel Smith break; 163ef19b117SAnsuel Smith 164ef19b117SAnsuel Smith if (--retry == 0) 165ef19b117SAnsuel Smith return -ETIMEDOUT; 166ef19b117SAnsuel Smith 167ef19b117SAnsuel Smith usleep_range(10, 20); 168ef19b117SAnsuel Smith } 169ef19b117SAnsuel Smith 170ef19b117SAnsuel Smith return 0; 171ef19b117SAnsuel Smith } 172ef19b117SAnsuel Smith 173ef19b117SAnsuel Smith /** 174ef19b117SAnsuel Smith * Write SSPHY register 175ef19b117SAnsuel Smith * 176ef19b117SAnsuel Smith * @base - QCOM DWC3 PHY base virtual address. 177ef19b117SAnsuel Smith * @addr - SSPHY address to write. 178ef19b117SAnsuel Smith * @val - value to write. 179ef19b117SAnsuel Smith */ 180ef19b117SAnsuel Smith static int usb_ss_write_phycreg(struct usb_phy *phy_dwc3, 181ef19b117SAnsuel Smith u32 addr, u32 val) 182ef19b117SAnsuel Smith { 183ef19b117SAnsuel Smith int ret; 184ef19b117SAnsuel Smith 185ef19b117SAnsuel Smith writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); 186ef19b117SAnsuel Smith writel(SS_CR_CAP_ADDR_REG, 187ef19b117SAnsuel Smith phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); 188ef19b117SAnsuel Smith 189ef19b117SAnsuel Smith ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); 190ef19b117SAnsuel Smith if (ret) 191ef19b117SAnsuel Smith goto err_wait; 192ef19b117SAnsuel Smith 193ef19b117SAnsuel Smith writel(val, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); 194ef19b117SAnsuel Smith writel(SS_CR_CAP_DATA_REG, 195ef19b117SAnsuel Smith phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); 196ef19b117SAnsuel Smith 197ef19b117SAnsuel Smith ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_DATA_REG); 198ef19b117SAnsuel Smith if (ret) 199ef19b117SAnsuel Smith goto err_wait; 200ef19b117SAnsuel Smith 201ef19b117SAnsuel Smith writel(SS_CR_WRITE_REG, phy_dwc3->base + CR_PROTOCOL_WRITE_REG); 202ef19b117SAnsuel Smith 203ef19b117SAnsuel Smith ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_WRITE_REG); 204ef19b117SAnsuel Smith 205ef19b117SAnsuel Smith err_wait: 206ef19b117SAnsuel Smith if (ret) 207ef19b117SAnsuel Smith dev_err(phy_dwc3->dev, "timeout waiting for latch\n"); 208ef19b117SAnsuel Smith return ret; 209ef19b117SAnsuel Smith } 210ef19b117SAnsuel Smith 211ef19b117SAnsuel Smith /** 212ef19b117SAnsuel Smith * Read SSPHY register. 213ef19b117SAnsuel Smith * 214ef19b117SAnsuel Smith * @base - QCOM DWC3 PHY base virtual address. 215ef19b117SAnsuel Smith * @addr - SSPHY address to read. 216ef19b117SAnsuel Smith */ 217ef19b117SAnsuel Smith static int usb_ss_read_phycreg(struct usb_phy *phy_dwc3, 218ef19b117SAnsuel Smith u32 addr, u32 *val) 219ef19b117SAnsuel Smith { 220ef19b117SAnsuel Smith int ret; 221ef19b117SAnsuel Smith 222ef19b117SAnsuel Smith writel(addr, phy_dwc3->base + CR_PROTOCOL_DATA_IN_REG); 223ef19b117SAnsuel Smith writel(SS_CR_CAP_ADDR_REG, 224ef19b117SAnsuel Smith phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); 225ef19b117SAnsuel Smith 226ef19b117SAnsuel Smith ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_CAP_ADDR_REG); 227ef19b117SAnsuel Smith if (ret) 228ef19b117SAnsuel Smith goto err_wait; 229ef19b117SAnsuel Smith 230ef19b117SAnsuel Smith /* 231ef19b117SAnsuel Smith * Due to hardware bug, first read of SSPHY register might be 232ef19b117SAnsuel Smith * incorrect. Hence as workaround, SW should perform SSPHY register 233ef19b117SAnsuel Smith * read twice, but use only second read and ignore first read. 234ef19b117SAnsuel Smith */ 235ef19b117SAnsuel Smith writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG); 236ef19b117SAnsuel Smith 237ef19b117SAnsuel Smith ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG); 238ef19b117SAnsuel Smith if (ret) 239ef19b117SAnsuel Smith goto err_wait; 240ef19b117SAnsuel Smith 241ef19b117SAnsuel Smith /* throwaway read */ 242ef19b117SAnsuel Smith readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG); 243ef19b117SAnsuel Smith 244ef19b117SAnsuel Smith writel(SS_CR_READ_REG, phy_dwc3->base + CR_PROTOCOL_READ_REG); 245ef19b117SAnsuel Smith 246ef19b117SAnsuel Smith ret = wait_for_latch(phy_dwc3->base + CR_PROTOCOL_READ_REG); 247ef19b117SAnsuel Smith if (ret) 248ef19b117SAnsuel Smith goto err_wait; 249ef19b117SAnsuel Smith 250ef19b117SAnsuel Smith *val = readl(phy_dwc3->base + CR_PROTOCOL_DATA_OUT_REG); 251ef19b117SAnsuel Smith 252ef19b117SAnsuel Smith err_wait: 253ef19b117SAnsuel Smith return ret; 254ef19b117SAnsuel Smith } 255ef19b117SAnsuel Smith 256ef19b117SAnsuel Smith static int qcom_ipq806x_usb_hs_phy_init(struct phy *phy) 257ef19b117SAnsuel Smith { 258ef19b117SAnsuel Smith struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); 259ef19b117SAnsuel Smith int ret; 260ef19b117SAnsuel Smith u32 val; 261ef19b117SAnsuel Smith 262ef19b117SAnsuel Smith ret = clk_prepare_enable(phy_dwc3->xo_clk); 263ef19b117SAnsuel Smith if (ret) 264ef19b117SAnsuel Smith return ret; 265ef19b117SAnsuel Smith 266ef19b117SAnsuel Smith ret = clk_prepare_enable(phy_dwc3->ref_clk); 267ef19b117SAnsuel Smith if (ret) { 268ef19b117SAnsuel Smith clk_disable_unprepare(phy_dwc3->xo_clk); 269ef19b117SAnsuel Smith return ret; 270ef19b117SAnsuel Smith } 271ef19b117SAnsuel Smith 272ef19b117SAnsuel Smith /* 273ef19b117SAnsuel Smith * HSPHY Initialization: Enable UTMI clock, select 19.2MHz fsel 274ef19b117SAnsuel Smith * enable clamping, and disable RETENTION (power-on default is ENABLED) 275ef19b117SAnsuel Smith */ 276ef19b117SAnsuel Smith val = HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_DMSEHV_CLAMP | 277ef19b117SAnsuel Smith HSUSB_CTRL_RETENABLEN | HSUSB_CTRL_COMMONONN | 278ef19b117SAnsuel Smith HSUSB_CTRL_OTGSESSVLD_CLAMP | HSUSB_CTRL_ID_HV_CLAMP | 279ef19b117SAnsuel Smith HSUSB_CTRL_DPSEHV_CLAMP | HSUSB_CTRL_UTMI_OTG_VBUS_VALID | 280ef19b117SAnsuel Smith HSUSB_CTRL_UTMI_CLK_EN | HSUSB_CTRL_CLAMP_EN | 0x70; 281ef19b117SAnsuel Smith 282ef19b117SAnsuel Smith /* use core clock if external reference is not present */ 283ef19b117SAnsuel Smith if (!phy_dwc3->xo_clk) 284ef19b117SAnsuel Smith val |= HSUSB_CTRL_USE_CLKCORE; 285ef19b117SAnsuel Smith 286ef19b117SAnsuel Smith writel(val, phy_dwc3->base + HSUSB_PHY_CTRL_REG); 287ef19b117SAnsuel Smith usleep_range(2000, 2200); 288ef19b117SAnsuel Smith 289ef19b117SAnsuel Smith /* Disable (bypass) VBUS and ID filters */ 290ef19b117SAnsuel Smith writel(HSUSB_GCFG_XHCI_REV, phy_dwc3->base + QSCRATCH_GENERAL_CFG); 291ef19b117SAnsuel Smith 292ef19b117SAnsuel Smith return 0; 293ef19b117SAnsuel Smith } 294ef19b117SAnsuel Smith 295ef19b117SAnsuel Smith static int qcom_ipq806x_usb_hs_phy_exit(struct phy *phy) 296ef19b117SAnsuel Smith { 297ef19b117SAnsuel Smith struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); 298ef19b117SAnsuel Smith 299ef19b117SAnsuel Smith clk_disable_unprepare(phy_dwc3->ref_clk); 300ef19b117SAnsuel Smith clk_disable_unprepare(phy_dwc3->xo_clk); 301ef19b117SAnsuel Smith 302ef19b117SAnsuel Smith return 0; 303ef19b117SAnsuel Smith } 304ef19b117SAnsuel Smith 305ef19b117SAnsuel Smith static int qcom_ipq806x_usb_ss_phy_init(struct phy *phy) 306ef19b117SAnsuel Smith { 307ef19b117SAnsuel Smith struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); 308ef19b117SAnsuel Smith int ret; 309ef19b117SAnsuel Smith u32 data; 310ef19b117SAnsuel Smith 311ef19b117SAnsuel Smith ret = clk_prepare_enable(phy_dwc3->xo_clk); 312ef19b117SAnsuel Smith if (ret) 313ef19b117SAnsuel Smith return ret; 314ef19b117SAnsuel Smith 315ef19b117SAnsuel Smith ret = clk_prepare_enable(phy_dwc3->ref_clk); 316ef19b117SAnsuel Smith if (ret) { 317ef19b117SAnsuel Smith clk_disable_unprepare(phy_dwc3->xo_clk); 318ef19b117SAnsuel Smith return ret; 319ef19b117SAnsuel Smith } 320ef19b117SAnsuel Smith 321ef19b117SAnsuel Smith /* reset phy */ 322ef19b117SAnsuel Smith data = readl(phy_dwc3->base + SSUSB_PHY_CTRL_REG); 323ef19b117SAnsuel Smith writel(data | SSUSB_CTRL_SS_PHY_RESET, 324ef19b117SAnsuel Smith phy_dwc3->base + SSUSB_PHY_CTRL_REG); 325ef19b117SAnsuel Smith usleep_range(2000, 2200); 326ef19b117SAnsuel Smith writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); 327ef19b117SAnsuel Smith 328ef19b117SAnsuel Smith /* clear REF_PAD if we don't have XO clk */ 329ef19b117SAnsuel Smith if (!phy_dwc3->xo_clk) 330ef19b117SAnsuel Smith data &= ~SSUSB_CTRL_REF_USE_PAD; 331ef19b117SAnsuel Smith else 332ef19b117SAnsuel Smith data |= SSUSB_CTRL_REF_USE_PAD; 333ef19b117SAnsuel Smith 334ef19b117SAnsuel Smith writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); 335ef19b117SAnsuel Smith 336ef19b117SAnsuel Smith /* wait for ref clk to become stable, this can take up to 30ms */ 337ef19b117SAnsuel Smith msleep(30); 338ef19b117SAnsuel Smith 339ef19b117SAnsuel Smith data |= SSUSB_CTRL_SS_PHY_EN | SSUSB_CTRL_LANE0_PWR_PRESENT; 340ef19b117SAnsuel Smith writel(data, phy_dwc3->base + SSUSB_PHY_CTRL_REG); 341ef19b117SAnsuel Smith 342ef19b117SAnsuel Smith /* 343ef19b117SAnsuel Smith * WORKAROUND: There is SSPHY suspend bug due to which USB enumerates 344ef19b117SAnsuel Smith * in HS mode instead of SS mode. Workaround it by asserting 345ef19b117SAnsuel Smith * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus mode 346ef19b117SAnsuel Smith */ 347ef19b117SAnsuel Smith ret = usb_ss_read_phycreg(phy_dwc3, 0x102D, &data); 348ef19b117SAnsuel Smith if (ret) 349ef19b117SAnsuel Smith goto err_phy_trans; 350ef19b117SAnsuel Smith 351ef19b117SAnsuel Smith data |= (1 << 7); 352ef19b117SAnsuel Smith ret = usb_ss_write_phycreg(phy_dwc3, 0x102D, data); 353ef19b117SAnsuel Smith if (ret) 354ef19b117SAnsuel Smith goto err_phy_trans; 355ef19b117SAnsuel Smith 356ef19b117SAnsuel Smith ret = usb_ss_read_phycreg(phy_dwc3, 0x1010, &data); 357ef19b117SAnsuel Smith if (ret) 358ef19b117SAnsuel Smith goto err_phy_trans; 359ef19b117SAnsuel Smith 360ef19b117SAnsuel Smith data &= ~0xff0; 361ef19b117SAnsuel Smith data |= 0x20; 362ef19b117SAnsuel Smith ret = usb_ss_write_phycreg(phy_dwc3, 0x1010, data); 363ef19b117SAnsuel Smith if (ret) 364ef19b117SAnsuel Smith goto err_phy_trans; 365ef19b117SAnsuel Smith 366ef19b117SAnsuel Smith /* 367ef19b117SAnsuel Smith * Fix RX Equalization setting as follows 368ef19b117SAnsuel Smith * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0 369ef19b117SAnsuel Smith * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1 370ef19b117SAnsuel Smith * LANE0.RX_OVRD_IN_HI.RX_EQ set based on SoC version 371ef19b117SAnsuel Smith * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1 372ef19b117SAnsuel Smith */ 373ef19b117SAnsuel Smith ret = usb_ss_read_phycreg(phy_dwc3, SSPHY_CTRL_RX_OVRD_IN_HI(0), &data); 374ef19b117SAnsuel Smith if (ret) 375ef19b117SAnsuel Smith goto err_phy_trans; 376ef19b117SAnsuel Smith 377ef19b117SAnsuel Smith data &= ~RX_OVRD_IN_HI_RX_EQ_EN; 378ef19b117SAnsuel Smith data |= RX_OVRD_IN_HI_RX_EQ_EN_OVRD; 379ef19b117SAnsuel Smith data &= ~RX_OVRD_IN_HI_RX_EQ_MASK; 380ef19b117SAnsuel Smith data |= RX_OVRD_IN_HI_RX_EQ(phy_dwc3->rx_eq); 381ef19b117SAnsuel Smith data |= RX_OVRD_IN_HI_RX_EQ_OVRD; 382ef19b117SAnsuel Smith ret = usb_ss_write_phycreg(phy_dwc3, 383ef19b117SAnsuel Smith SSPHY_CTRL_RX_OVRD_IN_HI(0), data); 384ef19b117SAnsuel Smith if (ret) 385ef19b117SAnsuel Smith goto err_phy_trans; 386ef19b117SAnsuel Smith 387ef19b117SAnsuel Smith /* 388ef19b117SAnsuel Smith * Set EQ and TX launch amplitudes as follows 389ef19b117SAnsuel Smith * LANE0.TX_OVRD_DRV_LO.PREEMPH set based on SoC version 390ef19b117SAnsuel Smith * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 110 391ef19b117SAnsuel Smith * LANE0.TX_OVRD_DRV_LO.EN set to 1. 392ef19b117SAnsuel Smith */ 393ef19b117SAnsuel Smith ret = usb_ss_read_phycreg(phy_dwc3, 394ef19b117SAnsuel Smith SSPHY_CTRL_TX_OVRD_DRV_LO(0), &data); 395ef19b117SAnsuel Smith if (ret) 396ef19b117SAnsuel Smith goto err_phy_trans; 397ef19b117SAnsuel Smith 398ef19b117SAnsuel Smith data &= ~TX_OVRD_DRV_LO_PREEMPH_MASK; 399ef19b117SAnsuel Smith data |= TX_OVRD_DRV_LO_PREEMPH(phy_dwc3->tx_deamp_3_5db); 400ef19b117SAnsuel Smith data &= ~TX_OVRD_DRV_LO_AMPLITUDE_MASK; 401ef19b117SAnsuel Smith data |= 0x6E; 402ef19b117SAnsuel Smith data |= TX_OVRD_DRV_LO_EN; 403ef19b117SAnsuel Smith ret = usb_ss_write_phycreg(phy_dwc3, 404ef19b117SAnsuel Smith SSPHY_CTRL_TX_OVRD_DRV_LO(0), data); 405ef19b117SAnsuel Smith if (ret) 406ef19b117SAnsuel Smith goto err_phy_trans; 407ef19b117SAnsuel Smith 408ef19b117SAnsuel Smith data = 0; 409ef19b117SAnsuel Smith data &= ~SSPHY_MPLL_MASK; 410ef19b117SAnsuel Smith data |= SSPHY_MPLL(phy_dwc3->mpll); 411ef19b117SAnsuel Smith usb_ss_write_phycreg(phy_dwc3, 0x30, data); 412ef19b117SAnsuel Smith 413ef19b117SAnsuel Smith /* 414ef19b117SAnsuel Smith * Set the QSCRATCH PHY_PARAM_CTRL1 parameters as follows 415ef19b117SAnsuel Smith * TX_FULL_SWING [26:20] amplitude to 110 416ef19b117SAnsuel Smith * TX_DEEMPH_6DB [19:14] to 32 417ef19b117SAnsuel Smith * TX_DEEMPH_3_5DB [13:8] set based on SoC version 418ef19b117SAnsuel Smith * LOS_BIAS [7:3] to 9 419ef19b117SAnsuel Smith */ 420ef19b117SAnsuel Smith data = readl(phy_dwc3->base + SSUSB_PHY_PARAM_CTRL_1); 421ef19b117SAnsuel Smith 422ef19b117SAnsuel Smith data &= ~PHY_PARAM_CTRL1_MASK; 423ef19b117SAnsuel Smith 424ef19b117SAnsuel Smith data |= PHY_PARAM_CTRL1_TX_FULL_SWING(0x6e) | 425ef19b117SAnsuel Smith PHY_PARAM_CTRL1_TX_DEEMPH_6DB(0x20) | 426ef19b117SAnsuel Smith PHY_PARAM_CTRL1_TX_DEEMPH_3_5DB(phy_dwc3->tx_deamp_3_5db) | 427ef19b117SAnsuel Smith PHY_PARAM_CTRL1_LOS_BIAS(0x9); 428ef19b117SAnsuel Smith 429ef19b117SAnsuel Smith usb_phy_write_readback(phy_dwc3, SSUSB_PHY_PARAM_CTRL_1, 430ef19b117SAnsuel Smith PHY_PARAM_CTRL1_MASK, data); 431ef19b117SAnsuel Smith 432ef19b117SAnsuel Smith err_phy_trans: 433ef19b117SAnsuel Smith return ret; 434ef19b117SAnsuel Smith } 435ef19b117SAnsuel Smith 436ef19b117SAnsuel Smith static int qcom_ipq806x_usb_ss_phy_exit(struct phy *phy) 437ef19b117SAnsuel Smith { 438ef19b117SAnsuel Smith struct usb_phy *phy_dwc3 = phy_get_drvdata(phy); 439ef19b117SAnsuel Smith 440ef19b117SAnsuel Smith /* Sequence to put SSPHY in low power state: 441ef19b117SAnsuel Smith * 1. Clear REF_PHY_EN in PHY_CTRL_REG 442ef19b117SAnsuel Smith * 2. Clear REF_USE_PAD in PHY_CTRL_REG 443ef19b117SAnsuel Smith * 3. Set TEST_POWERED_DOWN in PHY_CTRL_REG to enable PHY retention 444ef19b117SAnsuel Smith */ 445ef19b117SAnsuel Smith usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, 446ef19b117SAnsuel Smith SSUSB_CTRL_SS_PHY_EN, 0x0); 447ef19b117SAnsuel Smith usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, 448ef19b117SAnsuel Smith SSUSB_CTRL_REF_USE_PAD, 0x0); 449ef19b117SAnsuel Smith usb_phy_write_readback(phy_dwc3, SSUSB_PHY_CTRL_REG, 450ef19b117SAnsuel Smith SSUSB_CTRL_TEST_POWERDOWN, 0x0); 451ef19b117SAnsuel Smith 452ef19b117SAnsuel Smith clk_disable_unprepare(phy_dwc3->ref_clk); 453ef19b117SAnsuel Smith clk_disable_unprepare(phy_dwc3->xo_clk); 454ef19b117SAnsuel Smith 455ef19b117SAnsuel Smith return 0; 456ef19b117SAnsuel Smith } 457ef19b117SAnsuel Smith 458ef19b117SAnsuel Smith static const struct phy_drvdata qcom_ipq806x_usb_hs_drvdata = { 459ef19b117SAnsuel Smith .ops = { 460ef19b117SAnsuel Smith .init = qcom_ipq806x_usb_hs_phy_init, 461ef19b117SAnsuel Smith .exit = qcom_ipq806x_usb_hs_phy_exit, 462ef19b117SAnsuel Smith .owner = THIS_MODULE, 463ef19b117SAnsuel Smith }, 464ef19b117SAnsuel Smith .clk_rate = 60000000, 465ef19b117SAnsuel Smith }; 466ef19b117SAnsuel Smith 467ef19b117SAnsuel Smith static const struct phy_drvdata qcom_ipq806x_usb_ss_drvdata = { 468ef19b117SAnsuel Smith .ops = { 469ef19b117SAnsuel Smith .init = qcom_ipq806x_usb_ss_phy_init, 470ef19b117SAnsuel Smith .exit = qcom_ipq806x_usb_ss_phy_exit, 471ef19b117SAnsuel Smith .owner = THIS_MODULE, 472ef19b117SAnsuel Smith }, 473ef19b117SAnsuel Smith .clk_rate = 125000000, 474ef19b117SAnsuel Smith }; 475ef19b117SAnsuel Smith 476ef19b117SAnsuel Smith static const struct of_device_id qcom_ipq806x_usb_phy_table[] = { 477ef19b117SAnsuel Smith { .compatible = "qcom,ipq806x-usb-phy-hs", 478ef19b117SAnsuel Smith .data = &qcom_ipq806x_usb_hs_drvdata }, 479ef19b117SAnsuel Smith { .compatible = "qcom,ipq806x-usb-phy-ss", 480ef19b117SAnsuel Smith .data = &qcom_ipq806x_usb_ss_drvdata }, 481ef19b117SAnsuel Smith { /* Sentinel */ } 482ef19b117SAnsuel Smith }; 483ef19b117SAnsuel Smith MODULE_DEVICE_TABLE(of, qcom_ipq806x_usb_phy_table); 484ef19b117SAnsuel Smith 485ef19b117SAnsuel Smith static int qcom_ipq806x_usb_phy_probe(struct platform_device *pdev) 486ef19b117SAnsuel Smith { 487ef19b117SAnsuel Smith struct resource *res; 488ef19b117SAnsuel Smith resource_size_t size; 489ef19b117SAnsuel Smith struct phy *generic_phy; 490ef19b117SAnsuel Smith struct usb_phy *phy_dwc3; 491ef19b117SAnsuel Smith const struct phy_drvdata *data; 492ef19b117SAnsuel Smith struct phy_provider *phy_provider; 493ef19b117SAnsuel Smith 494ef19b117SAnsuel Smith phy_dwc3 = devm_kzalloc(&pdev->dev, sizeof(*phy_dwc3), GFP_KERNEL); 495ef19b117SAnsuel Smith if (!phy_dwc3) 496ef19b117SAnsuel Smith return -ENOMEM; 497ef19b117SAnsuel Smith 498ef19b117SAnsuel Smith data = of_device_get_match_data(&pdev->dev); 499ef19b117SAnsuel Smith 500ef19b117SAnsuel Smith phy_dwc3->dev = &pdev->dev; 501ef19b117SAnsuel Smith 502ef19b117SAnsuel Smith res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 503ef19b117SAnsuel Smith if (!res) 504ef19b117SAnsuel Smith return -EINVAL; 505ef19b117SAnsuel Smith size = resource_size(res); 506ef19b117SAnsuel Smith phy_dwc3->base = devm_ioremap(phy_dwc3->dev, res->start, size); 507ef19b117SAnsuel Smith 50804db2304SWei Yongjun if (!phy_dwc3->base) { 509ef19b117SAnsuel Smith dev_err(phy_dwc3->dev, "failed to map reg\n"); 51004db2304SWei Yongjun return -ENOMEM; 511ef19b117SAnsuel Smith } 512ef19b117SAnsuel Smith 513ef19b117SAnsuel Smith phy_dwc3->ref_clk = devm_clk_get(phy_dwc3->dev, "ref"); 514ef19b117SAnsuel Smith if (IS_ERR(phy_dwc3->ref_clk)) { 515ef19b117SAnsuel Smith dev_dbg(phy_dwc3->dev, "cannot get reference clock\n"); 516ef19b117SAnsuel Smith return PTR_ERR(phy_dwc3->ref_clk); 517ef19b117SAnsuel Smith } 518ef19b117SAnsuel Smith 519ef19b117SAnsuel Smith clk_set_rate(phy_dwc3->ref_clk, data->clk_rate); 520ef19b117SAnsuel Smith 521ef19b117SAnsuel Smith phy_dwc3->xo_clk = devm_clk_get(phy_dwc3->dev, "xo"); 522ef19b117SAnsuel Smith if (IS_ERR(phy_dwc3->xo_clk)) { 523ef19b117SAnsuel Smith dev_dbg(phy_dwc3->dev, "cannot get TCXO clock\n"); 524ef19b117SAnsuel Smith phy_dwc3->xo_clk = NULL; 525ef19b117SAnsuel Smith } 526ef19b117SAnsuel Smith 527ef19b117SAnsuel Smith /* Parse device node to probe HSIO settings */ 528ef19b117SAnsuel Smith if (device_property_read_u32(&pdev->dev, "qcom,rx-eq", 529ef19b117SAnsuel Smith &phy_dwc3->rx_eq)) 530ef19b117SAnsuel Smith phy_dwc3->rx_eq = SSPHY_RX_EQ_VALUE; 531ef19b117SAnsuel Smith 532ef19b117SAnsuel Smith if (device_property_read_u32(&pdev->dev, "qcom,tx-deamp_3_5db", 533ef19b117SAnsuel Smith &phy_dwc3->tx_deamp_3_5db)) 5343d7b0ca5SColin Ian King phy_dwc3->tx_deamp_3_5db = SSPHY_TX_DEEMPH_3_5DB; 535ef19b117SAnsuel Smith 536ef19b117SAnsuel Smith if (device_property_read_u32(&pdev->dev, "qcom,mpll", &phy_dwc3->mpll)) 537ef19b117SAnsuel Smith phy_dwc3->mpll = SSPHY_MPLL_VALUE; 538ef19b117SAnsuel Smith 539ef19b117SAnsuel Smith generic_phy = devm_phy_create(phy_dwc3->dev, pdev->dev.of_node, &data->ops); 540ef19b117SAnsuel Smith 541ef19b117SAnsuel Smith if (IS_ERR(generic_phy)) 542ef19b117SAnsuel Smith return PTR_ERR(generic_phy); 543ef19b117SAnsuel Smith 544ef19b117SAnsuel Smith phy_set_drvdata(generic_phy, phy_dwc3); 545ef19b117SAnsuel Smith platform_set_drvdata(pdev, phy_dwc3); 546ef19b117SAnsuel Smith 547ef19b117SAnsuel Smith phy_provider = devm_of_phy_provider_register(phy_dwc3->dev, 548ef19b117SAnsuel Smith of_phy_simple_xlate); 549ef19b117SAnsuel Smith 550ef19b117SAnsuel Smith if (IS_ERR(phy_provider)) 551ef19b117SAnsuel Smith return PTR_ERR(phy_provider); 552ef19b117SAnsuel Smith 553ef19b117SAnsuel Smith return 0; 554ef19b117SAnsuel Smith } 555ef19b117SAnsuel Smith 556ef19b117SAnsuel Smith static struct platform_driver qcom_ipq806x_usb_phy_driver = { 557ef19b117SAnsuel Smith .probe = qcom_ipq806x_usb_phy_probe, 558ef19b117SAnsuel Smith .driver = { 559ef19b117SAnsuel Smith .name = "qcom-ipq806x-usb-phy", 560ef19b117SAnsuel Smith .of_match_table = qcom_ipq806x_usb_phy_table, 561ef19b117SAnsuel Smith }, 562ef19b117SAnsuel Smith }; 563ef19b117SAnsuel Smith 564ef19b117SAnsuel Smith module_platform_driver(qcom_ipq806x_usb_phy_driver); 565ef19b117SAnsuel Smith 566ef19b117SAnsuel Smith MODULE_ALIAS("platform:phy-qcom-ipq806x-usb"); 567ef19b117SAnsuel Smith MODULE_LICENSE("GPL v2"); 568ef19b117SAnsuel Smith MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); 569ef19b117SAnsuel Smith MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); 570ef19b117SAnsuel Smith MODULE_DESCRIPTION("DesignWare USB3 QCOM PHY driver"); 571