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