151e8114fSWesley Cheng // SPDX-License-Identifier: GPL-2.0
251e8114fSWesley Cheng /*
351e8114fSWesley Cheng * Copyright (c) 2020, The Linux Foundation. All rights reserved.
451e8114fSWesley Cheng */
551e8114fSWesley Cheng
651e8114fSWesley Cheng #include <linux/clk.h>
751e8114fSWesley Cheng #include <linux/delay.h>
851e8114fSWesley Cheng #include <linux/err.h>
951e8114fSWesley Cheng #include <linux/io.h>
1051e8114fSWesley Cheng #include <linux/kernel.h>
1151e8114fSWesley Cheng #include <linux/module.h>
1251e8114fSWesley Cheng #include <linux/of.h>
1351e8114fSWesley Cheng #include <linux/phy/phy.h>
1451e8114fSWesley Cheng #include <linux/platform_device.h>
1551e8114fSWesley Cheng #include <linux/regmap.h>
1651e8114fSWesley Cheng #include <linux/regulator/consumer.h>
1751e8114fSWesley Cheng #include <linux/reset.h>
1851e8114fSWesley Cheng #include <linux/slab.h>
1951e8114fSWesley Cheng
2051e8114fSWesley Cheng #define USB2_PHY_USB_PHY_UTMI_CTRL0 (0x3c)
2151e8114fSWesley Cheng #define SLEEPM BIT(0)
2251e8114fSWesley Cheng #define OPMODE_MASK GENMASK(4, 3)
2351e8114fSWesley Cheng #define OPMODE_NORMAL (0x00)
2451e8114fSWesley Cheng #define OPMODE_NONDRIVING BIT(3)
2551e8114fSWesley Cheng #define TERMSEL BIT(5)
2651e8114fSWesley Cheng
2751e8114fSWesley Cheng #define USB2_PHY_USB_PHY_UTMI_CTRL1 (0x40)
2851e8114fSWesley Cheng #define XCVRSEL BIT(0)
2951e8114fSWesley Cheng
3051e8114fSWesley Cheng #define USB2_PHY_USB_PHY_UTMI_CTRL5 (0x50)
3151e8114fSWesley Cheng #define POR BIT(1)
3251e8114fSWesley Cheng
3351e8114fSWesley Cheng #define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0 (0x54)
347addff40SBjorn Andersson #define SIDDQ BIT(2)
3551e8114fSWesley Cheng #define RETENABLEN BIT(3)
36b475bf0eSSandeep Maheswaram #define FSEL_MASK GENMASK(6, 4)
3751e8114fSWesley Cheng #define FSEL_DEFAULT (0x3 << 4)
3851e8114fSWesley Cheng
3951e8114fSWesley Cheng #define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1 (0x58)
4051e8114fSWesley Cheng #define VBUSVLDEXTSEL0 BIT(4)
4151e8114fSWesley Cheng #define PLLBTUNE BIT(5)
4251e8114fSWesley Cheng
4351e8114fSWesley Cheng #define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2 (0x5c)
4451e8114fSWesley Cheng #define VREGBYPASS BIT(0)
4551e8114fSWesley Cheng
4651e8114fSWesley Cheng #define USB2_PHY_USB_PHY_HS_PHY_CTRL1 (0x60)
4751e8114fSWesley Cheng #define VBUSVLDEXT0 BIT(0)
4851e8114fSWesley Cheng
4951e8114fSWesley Cheng #define USB2_PHY_USB_PHY_HS_PHY_CTRL2 (0x64)
5051e8114fSWesley Cheng #define USB2_AUTO_RESUME BIT(0)
5151e8114fSWesley Cheng #define USB2_SUSPEND_N BIT(2)
5251e8114fSWesley Cheng #define USB2_SUSPEND_N_SEL BIT(3)
5351e8114fSWesley Cheng
54df2217ffSKrishna Kurapati #define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0 (0x6c)
55df2217ffSKrishna Kurapati #define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1 (0x70)
56df2217ffSKrishna Kurapati #define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2 (0x74)
57df2217ffSKrishna Kurapati #define USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3 (0x78)
58df2217ffSKrishna Kurapati #define PARAM_OVRD_MASK 0xFF
59df2217ffSKrishna Kurapati
6051e8114fSWesley Cheng #define USB2_PHY_USB_PHY_CFG0 (0x94)
6151e8114fSWesley Cheng #define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN BIT(0)
6251e8114fSWesley Cheng #define UTMI_PHY_CMN_CTRL_OVERRIDE_EN BIT(1)
6351e8114fSWesley Cheng
6451e8114fSWesley Cheng #define USB2_PHY_USB_PHY_REFCLK_CTRL (0xa0)
6551e8114fSWesley Cheng #define REFCLK_SEL_MASK GENMASK(1, 0)
6651e8114fSWesley Cheng #define REFCLK_SEL_DEFAULT (0x2 << 0)
6751e8114fSWesley Cheng
68df2217ffSKrishna Kurapati #define HS_DISCONNECT_MASK GENMASK(2, 0)
69df2217ffSKrishna Kurapati #define SQUELCH_DETECTOR_MASK GENMASK(7, 5)
70df2217ffSKrishna Kurapati
71df2217ffSKrishna Kurapati #define HS_AMPLITUDE_MASK GENMASK(3, 0)
72df2217ffSKrishna Kurapati #define PREEMPHASIS_DURATION_MASK BIT(5)
73df2217ffSKrishna Kurapati #define PREEMPHASIS_AMPLITUDE_MASK GENMASK(7, 6)
74df2217ffSKrishna Kurapati
75df2217ffSKrishna Kurapati #define HS_RISE_FALL_MASK GENMASK(1, 0)
76df2217ffSKrishna Kurapati #define HS_CROSSOVER_VOLTAGE_MASK GENMASK(3, 2)
77df2217ffSKrishna Kurapati #define HS_OUTPUT_IMPEDANCE_MASK GENMASK(5, 4)
78df2217ffSKrishna Kurapati
79df2217ffSKrishna Kurapati #define LS_FS_OUTPUT_IMPEDANCE_MASK GENMASK(3, 0)
80df2217ffSKrishna Kurapati
8151e8114fSWesley Cheng static const char * const qcom_snps_hsphy_vreg_names[] = {
8251e8114fSWesley Cheng "vdda-pll", "vdda33", "vdda18",
8351e8114fSWesley Cheng };
8451e8114fSWesley Cheng
8551e8114fSWesley Cheng #define SNPS_HS_NUM_VREGS ARRAY_SIZE(qcom_snps_hsphy_vreg_names)
8651e8114fSWesley Cheng
87df2217ffSKrishna Kurapati struct override_param {
88df2217ffSKrishna Kurapati s32 value;
89df2217ffSKrishna Kurapati u8 reg_val;
90df2217ffSKrishna Kurapati };
91df2217ffSKrishna Kurapati
92df2217ffSKrishna Kurapati struct override_param_map {
93df2217ffSKrishna Kurapati const char *prop_name;
94df2217ffSKrishna Kurapati const struct override_param *param_table;
95df2217ffSKrishna Kurapati u8 table_size;
96df2217ffSKrishna Kurapati u8 reg_offset;
97df2217ffSKrishna Kurapati u8 param_mask;
98df2217ffSKrishna Kurapati };
99df2217ffSKrishna Kurapati
100df2217ffSKrishna Kurapati struct phy_override_seq {
101df2217ffSKrishna Kurapati bool need_update;
102df2217ffSKrishna Kurapati u8 offset;
103df2217ffSKrishna Kurapati u8 value;
104df2217ffSKrishna Kurapati u8 mask;
105df2217ffSKrishna Kurapati };
106df2217ffSKrishna Kurapati
107df2217ffSKrishna Kurapati #define NUM_HSPHY_TUNING_PARAMS (9)
108df2217ffSKrishna Kurapati
10951e8114fSWesley Cheng /**
11051e8114fSWesley Cheng * struct qcom_snps_hsphy - snps hs phy attributes
11151e8114fSWesley Cheng *
1128a0eb8f9SAdrien Thierry * @dev: device structure
1138a0eb8f9SAdrien Thierry *
11451e8114fSWesley Cheng * @phy: generic phy
11551e8114fSWesley Cheng * @base: iomapped memory space for snps hs phy
11651e8114fSWesley Cheng *
1178a0eb8f9SAdrien Thierry * @num_clks: number of clocks
1188a0eb8f9SAdrien Thierry * @clks: array of clocks
11951e8114fSWesley Cheng * @phy_reset: phy reset control
12051e8114fSWesley Cheng * @vregs: regulator supplies bulk data
12151e8114fSWesley Cheng * @phy_initialized: if PHY has been initialized correctly
122dcbec046SWesley Cheng * @mode: contains the current mode the PHY is in
1232a881183SKrzysztof Kozlowski * @update_seq_cfg: tuning parameters for phy init
12451e8114fSWesley Cheng */
12551e8114fSWesley Cheng struct qcom_snps_hsphy {
1268a0eb8f9SAdrien Thierry struct device *dev;
1278a0eb8f9SAdrien Thierry
12851e8114fSWesley Cheng struct phy *phy;
12951e8114fSWesley Cheng void __iomem *base;
13051e8114fSWesley Cheng
1318a0eb8f9SAdrien Thierry int num_clks;
1328a0eb8f9SAdrien Thierry struct clk_bulk_data *clks;
13351e8114fSWesley Cheng struct reset_control *phy_reset;
13451e8114fSWesley Cheng struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS];
13551e8114fSWesley Cheng
13651e8114fSWesley Cheng bool phy_initialized;
137dcbec046SWesley Cheng enum phy_mode mode;
138df2217ffSKrishna Kurapati struct phy_override_seq update_seq_cfg[NUM_HSPHY_TUNING_PARAMS];
13951e8114fSWesley Cheng };
14051e8114fSWesley Cheng
qcom_snps_hsphy_clk_init(struct qcom_snps_hsphy * hsphy)1418a0eb8f9SAdrien Thierry static int qcom_snps_hsphy_clk_init(struct qcom_snps_hsphy *hsphy)
1428a0eb8f9SAdrien Thierry {
1438a0eb8f9SAdrien Thierry struct device *dev = hsphy->dev;
1448a0eb8f9SAdrien Thierry
1458a0eb8f9SAdrien Thierry hsphy->num_clks = 2;
1468a0eb8f9SAdrien Thierry hsphy->clks = devm_kcalloc(dev, hsphy->num_clks, sizeof(*hsphy->clks), GFP_KERNEL);
1478a0eb8f9SAdrien Thierry if (!hsphy->clks)
1488a0eb8f9SAdrien Thierry return -ENOMEM;
1498a0eb8f9SAdrien Thierry
1508a0eb8f9SAdrien Thierry /*
1518a0eb8f9SAdrien Thierry * TODO: Currently no device tree instantiation of the PHY is using the clock.
1528a0eb8f9SAdrien Thierry * This needs to be fixed in order for this code to be able to use devm_clk_bulk_get().
1538a0eb8f9SAdrien Thierry */
1548a0eb8f9SAdrien Thierry hsphy->clks[0].id = "cfg_ahb";
1558a0eb8f9SAdrien Thierry hsphy->clks[0].clk = devm_clk_get_optional(dev, "cfg_ahb");
1568a0eb8f9SAdrien Thierry if (IS_ERR(hsphy->clks[0].clk))
1578a0eb8f9SAdrien Thierry return dev_err_probe(dev, PTR_ERR(hsphy->clks[0].clk),
1588a0eb8f9SAdrien Thierry "failed to get cfg_ahb clk\n");
1598a0eb8f9SAdrien Thierry
1608a0eb8f9SAdrien Thierry hsphy->clks[1].id = "ref";
1618a0eb8f9SAdrien Thierry hsphy->clks[1].clk = devm_clk_get(dev, "ref");
1628a0eb8f9SAdrien Thierry if (IS_ERR(hsphy->clks[1].clk))
1638a0eb8f9SAdrien Thierry return dev_err_probe(dev, PTR_ERR(hsphy->clks[1].clk),
1648a0eb8f9SAdrien Thierry "failed to get ref clk\n");
1658a0eb8f9SAdrien Thierry
1668a0eb8f9SAdrien Thierry return 0;
1678a0eb8f9SAdrien Thierry }
1688a0eb8f9SAdrien Thierry
qcom_snps_hsphy_write_mask(void __iomem * base,u32 offset,u32 mask,u32 val)16951e8114fSWesley Cheng static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,
17051e8114fSWesley Cheng u32 mask, u32 val)
17151e8114fSWesley Cheng {
17251e8114fSWesley Cheng u32 reg;
17351e8114fSWesley Cheng
17451e8114fSWesley Cheng reg = readl_relaxed(base + offset);
17551e8114fSWesley Cheng reg &= ~mask;
17651e8114fSWesley Cheng reg |= val & mask;
17751e8114fSWesley Cheng writel_relaxed(reg, base + offset);
17851e8114fSWesley Cheng
17951e8114fSWesley Cheng /* Ensure above write is completed */
18051e8114fSWesley Cheng readl_relaxed(base + offset);
18151e8114fSWesley Cheng }
18251e8114fSWesley Cheng
qcom_snps_hsphy_suspend(struct qcom_snps_hsphy * hsphy)1830d75f508SWesley Cheng static int qcom_snps_hsphy_suspend(struct qcom_snps_hsphy *hsphy)
1840d75f508SWesley Cheng {
1850d75f508SWesley Cheng dev_dbg(&hsphy->phy->dev, "Suspend QCOM SNPS PHY\n");
1860d75f508SWesley Cheng
1870d75f508SWesley Cheng if (hsphy->mode == PHY_MODE_USB_HOST) {
1880d75f508SWesley Cheng /* Enable auto-resume to meet remote wakeup timing */
1890d75f508SWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base,
1900d75f508SWesley Cheng USB2_PHY_USB_PHY_HS_PHY_CTRL2,
1910d75f508SWesley Cheng USB2_AUTO_RESUME,
1920d75f508SWesley Cheng USB2_AUTO_RESUME);
1930d75f508SWesley Cheng usleep_range(500, 1000);
1940d75f508SWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base,
1950d75f508SWesley Cheng USB2_PHY_USB_PHY_HS_PHY_CTRL2,
1960d75f508SWesley Cheng 0, USB2_AUTO_RESUME);
1970d75f508SWesley Cheng }
1980d75f508SWesley Cheng
1990d75f508SWesley Cheng return 0;
2000d75f508SWesley Cheng }
2010d75f508SWesley Cheng
qcom_snps_hsphy_resume(struct qcom_snps_hsphy * hsphy)2020d75f508SWesley Cheng static int qcom_snps_hsphy_resume(struct qcom_snps_hsphy *hsphy)
2030d75f508SWesley Cheng {
2040d75f508SWesley Cheng dev_dbg(&hsphy->phy->dev, "Resume QCOM SNPS PHY, mode\n");
2050d75f508SWesley Cheng
2060d75f508SWesley Cheng return 0;
2070d75f508SWesley Cheng }
2080d75f508SWesley Cheng
qcom_snps_hsphy_runtime_suspend(struct device * dev)2090d75f508SWesley Cheng static int __maybe_unused qcom_snps_hsphy_runtime_suspend(struct device *dev)
2100d75f508SWesley Cheng {
2110d75f508SWesley Cheng struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);
2120d75f508SWesley Cheng
2130d75f508SWesley Cheng if (!hsphy->phy_initialized)
2140d75f508SWesley Cheng return 0;
2150d75f508SWesley Cheng
216*8932089bSAdrien Thierry return qcom_snps_hsphy_suspend(hsphy);
2170d75f508SWesley Cheng }
2180d75f508SWesley Cheng
qcom_snps_hsphy_runtime_resume(struct device * dev)2190d75f508SWesley Cheng static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev)
2200d75f508SWesley Cheng {
2210d75f508SWesley Cheng struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);
2220d75f508SWesley Cheng
2230d75f508SWesley Cheng if (!hsphy->phy_initialized)
2240d75f508SWesley Cheng return 0;
2250d75f508SWesley Cheng
226*8932089bSAdrien Thierry return qcom_snps_hsphy_resume(hsphy);
2270d75f508SWesley Cheng }
2280d75f508SWesley Cheng
qcom_snps_hsphy_set_mode(struct phy * phy,enum phy_mode mode,int submode)229dcbec046SWesley Cheng static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,
230dcbec046SWesley Cheng int submode)
231dcbec046SWesley Cheng {
232dcbec046SWesley Cheng struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
233dcbec046SWesley Cheng
234dcbec046SWesley Cheng hsphy->mode = mode;
235dcbec046SWesley Cheng return 0;
236dcbec046SWesley Cheng }
237dcbec046SWesley Cheng
238df2217ffSKrishna Kurapati static const struct override_param hs_disconnect_sc7280[] = {
239df2217ffSKrishna Kurapati { -272, 0 },
240df2217ffSKrishna Kurapati { 0, 1 },
241df2217ffSKrishna Kurapati { 317, 2 },
242df2217ffSKrishna Kurapati { 630, 3 },
243df2217ffSKrishna Kurapati { 973, 4 },
244df2217ffSKrishna Kurapati { 1332, 5 },
245df2217ffSKrishna Kurapati { 1743, 6 },
246df2217ffSKrishna Kurapati { 2156, 7 },
247df2217ffSKrishna Kurapati };
248df2217ffSKrishna Kurapati
249df2217ffSKrishna Kurapati static const struct override_param squelch_det_threshold_sc7280[] = {
250df2217ffSKrishna Kurapati { -2090, 7 },
251df2217ffSKrishna Kurapati { -1560, 6 },
252df2217ffSKrishna Kurapati { -1030, 5 },
253df2217ffSKrishna Kurapati { -530, 4 },
254df2217ffSKrishna Kurapati { 0, 3 },
255df2217ffSKrishna Kurapati { 530, 2 },
256df2217ffSKrishna Kurapati { 1060, 1 },
257df2217ffSKrishna Kurapati { 1590, 0 },
258df2217ffSKrishna Kurapati };
259df2217ffSKrishna Kurapati
260df2217ffSKrishna Kurapati static const struct override_param hs_amplitude_sc7280[] = {
261df2217ffSKrishna Kurapati { -660, 0 },
262df2217ffSKrishna Kurapati { -440, 1 },
263df2217ffSKrishna Kurapati { -220, 2 },
264df2217ffSKrishna Kurapati { 0, 3 },
265df2217ffSKrishna Kurapati { 230, 4 },
266df2217ffSKrishna Kurapati { 440, 5 },
267df2217ffSKrishna Kurapati { 650, 6 },
268df2217ffSKrishna Kurapati { 890, 7 },
269df2217ffSKrishna Kurapati { 1110, 8 },
270df2217ffSKrishna Kurapati { 1330, 9 },
271df2217ffSKrishna Kurapati { 1560, 10 },
272df2217ffSKrishna Kurapati { 1780, 11 },
273df2217ffSKrishna Kurapati { 2000, 12 },
274df2217ffSKrishna Kurapati { 2220, 13 },
275df2217ffSKrishna Kurapati { 2430, 14 },
276df2217ffSKrishna Kurapati { 2670, 15 },
277df2217ffSKrishna Kurapati };
278df2217ffSKrishna Kurapati
279df2217ffSKrishna Kurapati static const struct override_param preemphasis_duration_sc7280[] = {
280df2217ffSKrishna Kurapati { 10000, 1 },
281df2217ffSKrishna Kurapati { 20000, 0 },
282df2217ffSKrishna Kurapati };
283df2217ffSKrishna Kurapati
284df2217ffSKrishna Kurapati static const struct override_param preemphasis_amplitude_sc7280[] = {
285df2217ffSKrishna Kurapati { 10000, 1 },
286df2217ffSKrishna Kurapati { 20000, 2 },
287df2217ffSKrishna Kurapati { 30000, 3 },
288df2217ffSKrishna Kurapati { 40000, 0 },
289df2217ffSKrishna Kurapati };
290df2217ffSKrishna Kurapati
291df2217ffSKrishna Kurapati static const struct override_param hs_rise_fall_time_sc7280[] = {
292df2217ffSKrishna Kurapati { -4100, 3 },
293df2217ffSKrishna Kurapati { 0, 2 },
294df2217ffSKrishna Kurapati { 2810, 1 },
295df2217ffSKrishna Kurapati { 5430, 0 },
296df2217ffSKrishna Kurapati };
297df2217ffSKrishna Kurapati
298df2217ffSKrishna Kurapati static const struct override_param hs_crossover_voltage_sc7280[] = {
299df2217ffSKrishna Kurapati { -31000, 1 },
300df2217ffSKrishna Kurapati { 0, 3 },
301df2217ffSKrishna Kurapati { 28000, 2 },
302df2217ffSKrishna Kurapati };
303df2217ffSKrishna Kurapati
304df2217ffSKrishna Kurapati static const struct override_param hs_output_impedance_sc7280[] = {
305df2217ffSKrishna Kurapati { -2300000, 3 },
306df2217ffSKrishna Kurapati { 0, 2 },
307df2217ffSKrishna Kurapati { 2600000, 1 },
308df2217ffSKrishna Kurapati { 6100000, 0 },
309df2217ffSKrishna Kurapati };
310df2217ffSKrishna Kurapati
311df2217ffSKrishna Kurapati static const struct override_param ls_fs_output_impedance_sc7280[] = {
312df2217ffSKrishna Kurapati { -1053, 15 },
313df2217ffSKrishna Kurapati { -557, 7 },
314df2217ffSKrishna Kurapati { 0, 3 },
315df2217ffSKrishna Kurapati { 612, 1 },
316df2217ffSKrishna Kurapati { 1310, 0 },
317df2217ffSKrishna Kurapati };
318df2217ffSKrishna Kurapati
319df2217ffSKrishna Kurapati static const struct override_param_map sc7280_snps_7nm_phy[] = {
320df2217ffSKrishna Kurapati {
321df2217ffSKrishna Kurapati "qcom,hs-disconnect-bp",
322df2217ffSKrishna Kurapati hs_disconnect_sc7280,
323df2217ffSKrishna Kurapati ARRAY_SIZE(hs_disconnect_sc7280),
324df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0,
325df2217ffSKrishna Kurapati HS_DISCONNECT_MASK
326df2217ffSKrishna Kurapati },
327df2217ffSKrishna Kurapati {
328df2217ffSKrishna Kurapati "qcom,squelch-detector-bp",
329df2217ffSKrishna Kurapati squelch_det_threshold_sc7280,
330df2217ffSKrishna Kurapati ARRAY_SIZE(squelch_det_threshold_sc7280),
331df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X0,
332df2217ffSKrishna Kurapati SQUELCH_DETECTOR_MASK
333df2217ffSKrishna Kurapati },
334df2217ffSKrishna Kurapati {
335df2217ffSKrishna Kurapati "qcom,hs-amplitude-bp",
336df2217ffSKrishna Kurapati hs_amplitude_sc7280,
337df2217ffSKrishna Kurapati ARRAY_SIZE(hs_amplitude_sc7280),
338df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
339df2217ffSKrishna Kurapati HS_AMPLITUDE_MASK
340df2217ffSKrishna Kurapati },
341df2217ffSKrishna Kurapati {
342df2217ffSKrishna Kurapati "qcom,pre-emphasis-duration-bp",
343df2217ffSKrishna Kurapati preemphasis_duration_sc7280,
344df2217ffSKrishna Kurapati ARRAY_SIZE(preemphasis_duration_sc7280),
345df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
346df2217ffSKrishna Kurapati PREEMPHASIS_DURATION_MASK,
347df2217ffSKrishna Kurapati },
348df2217ffSKrishna Kurapati {
349df2217ffSKrishna Kurapati "qcom,pre-emphasis-amplitude-bp",
350df2217ffSKrishna Kurapati preemphasis_amplitude_sc7280,
351df2217ffSKrishna Kurapati ARRAY_SIZE(preemphasis_amplitude_sc7280),
352df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X1,
353df2217ffSKrishna Kurapati PREEMPHASIS_AMPLITUDE_MASK,
354df2217ffSKrishna Kurapati },
355df2217ffSKrishna Kurapati {
356df2217ffSKrishna Kurapati "qcom,hs-rise-fall-time-bp",
357df2217ffSKrishna Kurapati hs_rise_fall_time_sc7280,
358df2217ffSKrishna Kurapati ARRAY_SIZE(hs_rise_fall_time_sc7280),
359df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
360df2217ffSKrishna Kurapati HS_RISE_FALL_MASK
361df2217ffSKrishna Kurapati },
362df2217ffSKrishna Kurapati {
363df2217ffSKrishna Kurapati "qcom,hs-crossover-voltage-microvolt",
364df2217ffSKrishna Kurapati hs_crossover_voltage_sc7280,
365df2217ffSKrishna Kurapati ARRAY_SIZE(hs_crossover_voltage_sc7280),
366df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
367df2217ffSKrishna Kurapati HS_CROSSOVER_VOLTAGE_MASK
368df2217ffSKrishna Kurapati },
369df2217ffSKrishna Kurapati {
370df2217ffSKrishna Kurapati "qcom,hs-output-impedance-micro-ohms",
371df2217ffSKrishna Kurapati hs_output_impedance_sc7280,
372df2217ffSKrishna Kurapati ARRAY_SIZE(hs_output_impedance_sc7280),
373df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X2,
374df2217ffSKrishna Kurapati HS_OUTPUT_IMPEDANCE_MASK,
375df2217ffSKrishna Kurapati },
376df2217ffSKrishna Kurapati {
377df2217ffSKrishna Kurapati "qcom,ls-fs-output-impedance-bp",
378df2217ffSKrishna Kurapati ls_fs_output_impedance_sc7280,
379df2217ffSKrishna Kurapati ARRAY_SIZE(ls_fs_output_impedance_sc7280),
380df2217ffSKrishna Kurapati USB2_PHY_USB_PHY_HS_PHY_OVERRIDE_X3,
381df2217ffSKrishna Kurapati LS_FS_OUTPUT_IMPEDANCE_MASK,
382df2217ffSKrishna Kurapati },
383df2217ffSKrishna Kurapati {},
384df2217ffSKrishna Kurapati };
385df2217ffSKrishna Kurapati
qcom_snps_hsphy_init(struct phy * phy)38651e8114fSWesley Cheng static int qcom_snps_hsphy_init(struct phy *phy)
38751e8114fSWesley Cheng {
38851e8114fSWesley Cheng struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
389df2217ffSKrishna Kurapati int ret, i;
39051e8114fSWesley Cheng
39151e8114fSWesley Cheng dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__);
39251e8114fSWesley Cheng
39351e8114fSWesley Cheng ret = regulator_bulk_enable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
39451e8114fSWesley Cheng if (ret)
39551e8114fSWesley Cheng return ret;
39651e8114fSWesley Cheng
3978a0eb8f9SAdrien Thierry ret = clk_bulk_prepare_enable(hsphy->num_clks, hsphy->clks);
39851e8114fSWesley Cheng if (ret) {
3998a0eb8f9SAdrien Thierry dev_err(&phy->dev, "failed to enable clocks, %d\n", ret);
40051e8114fSWesley Cheng goto poweroff_phy;
40151e8114fSWesley Cheng }
40251e8114fSWesley Cheng
40351e8114fSWesley Cheng ret = reset_control_assert(hsphy->phy_reset);
40451e8114fSWesley Cheng if (ret) {
40551e8114fSWesley Cheng dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
4068a0eb8f9SAdrien Thierry goto disable_clks;
40751e8114fSWesley Cheng }
40851e8114fSWesley Cheng
40951e8114fSWesley Cheng usleep_range(100, 150);
41051e8114fSWesley Cheng
41151e8114fSWesley Cheng ret = reset_control_deassert(hsphy->phy_reset);
41251e8114fSWesley Cheng if (ret) {
41351e8114fSWesley Cheng dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
4148a0eb8f9SAdrien Thierry goto disable_clks;
41551e8114fSWesley Cheng }
41651e8114fSWesley Cheng
41751e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
41851e8114fSWesley Cheng UTMI_PHY_CMN_CTRL_OVERRIDE_EN,
41951e8114fSWesley Cheng UTMI_PHY_CMN_CTRL_OVERRIDE_EN);
42051e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
42151e8114fSWesley Cheng POR, POR);
42251e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base,
42351e8114fSWesley Cheng USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
42451e8114fSWesley Cheng FSEL_MASK, 0);
42551e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base,
42651e8114fSWesley Cheng USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
42751e8114fSWesley Cheng PLLBTUNE, PLLBTUNE);
42851e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_REFCLK_CTRL,
42951e8114fSWesley Cheng REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK);
43051e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base,
43151e8114fSWesley Cheng USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
43251e8114fSWesley Cheng VBUSVLDEXTSEL0, VBUSVLDEXTSEL0);
43351e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,
43451e8114fSWesley Cheng VBUSVLDEXT0, VBUSVLDEXT0);
43551e8114fSWesley Cheng
436df2217ffSKrishna Kurapati for (i = 0; i < ARRAY_SIZE(hsphy->update_seq_cfg); i++) {
437df2217ffSKrishna Kurapati if (hsphy->update_seq_cfg[i].need_update)
438df2217ffSKrishna Kurapati qcom_snps_hsphy_write_mask(hsphy->base,
439df2217ffSKrishna Kurapati hsphy->update_seq_cfg[i].offset,
440df2217ffSKrishna Kurapati hsphy->update_seq_cfg[i].mask,
441df2217ffSKrishna Kurapati hsphy->update_seq_cfg[i].value);
442df2217ffSKrishna Kurapati }
443df2217ffSKrishna Kurapati
44451e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base,
44551e8114fSWesley Cheng USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,
44651e8114fSWesley Cheng VREGBYPASS, VREGBYPASS);
44751e8114fSWesley Cheng
44851e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
44951e8114fSWesley Cheng USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,
45051e8114fSWesley Cheng USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);
45151e8114fSWesley Cheng
45251e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
45351e8114fSWesley Cheng SLEEPM, SLEEPM);
45451e8114fSWesley Cheng
4557addff40SBjorn Andersson qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
4567addff40SBjorn Andersson SIDDQ, 0);
4577addff40SBjorn Andersson
45851e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
45951e8114fSWesley Cheng POR, 0);
46051e8114fSWesley Cheng
46151e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
46251e8114fSWesley Cheng USB2_SUSPEND_N_SEL, 0);
46351e8114fSWesley Cheng
46451e8114fSWesley Cheng qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
46551e8114fSWesley Cheng UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0);
46651e8114fSWesley Cheng
46751e8114fSWesley Cheng hsphy->phy_initialized = true;
46851e8114fSWesley Cheng
46951e8114fSWesley Cheng return 0;
47051e8114fSWesley Cheng
4718a0eb8f9SAdrien Thierry disable_clks:
4728a0eb8f9SAdrien Thierry clk_bulk_disable_unprepare(hsphy->num_clks, hsphy->clks);
47351e8114fSWesley Cheng poweroff_phy:
47451e8114fSWesley Cheng regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
47551e8114fSWesley Cheng
47651e8114fSWesley Cheng return ret;
47751e8114fSWesley Cheng }
47851e8114fSWesley Cheng
qcom_snps_hsphy_exit(struct phy * phy)47951e8114fSWesley Cheng static int qcom_snps_hsphy_exit(struct phy *phy)
48051e8114fSWesley Cheng {
48151e8114fSWesley Cheng struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
48251e8114fSWesley Cheng
48351e8114fSWesley Cheng reset_control_assert(hsphy->phy_reset);
4848a0eb8f9SAdrien Thierry clk_bulk_disable_unprepare(hsphy->num_clks, hsphy->clks);
48551e8114fSWesley Cheng regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
48651e8114fSWesley Cheng hsphy->phy_initialized = false;
48751e8114fSWesley Cheng
48851e8114fSWesley Cheng return 0;
48951e8114fSWesley Cheng }
49051e8114fSWesley Cheng
49151e8114fSWesley Cheng static const struct phy_ops qcom_snps_hsphy_gen_ops = {
49251e8114fSWesley Cheng .init = qcom_snps_hsphy_init,
49351e8114fSWesley Cheng .exit = qcom_snps_hsphy_exit,
494dcbec046SWesley Cheng .set_mode = qcom_snps_hsphy_set_mode,
49551e8114fSWesley Cheng .owner = THIS_MODULE,
49651e8114fSWesley Cheng };
49751e8114fSWesley Cheng
49851e8114fSWesley Cheng static const struct of_device_id qcom_snps_hsphy_of_match_table[] = {
49951e8114fSWesley Cheng { .compatible = "qcom,sm8150-usb-hs-phy", },
5007addff40SBjorn Andersson { .compatible = "qcom,usb-snps-hs-5nm-phy", },
501df2217ffSKrishna Kurapati {
502df2217ffSKrishna Kurapati .compatible = "qcom,usb-snps-hs-7nm-phy",
503df2217ffSKrishna Kurapati .data = &sc7280_snps_7nm_phy,
504df2217ffSKrishna Kurapati },
50551e8114fSWesley Cheng { .compatible = "qcom,usb-snps-femto-v2-phy", },
50651e8114fSWesley Cheng { }
50751e8114fSWesley Cheng };
50851e8114fSWesley Cheng MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table);
50951e8114fSWesley Cheng
5100d75f508SWesley Cheng static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = {
5110d75f508SWesley Cheng SET_RUNTIME_PM_OPS(qcom_snps_hsphy_runtime_suspend,
5120d75f508SWesley Cheng qcom_snps_hsphy_runtime_resume, NULL)
5130d75f508SWesley Cheng };
5140d75f508SWesley Cheng
qcom_snps_hsphy_override_param_update_val(const struct override_param_map map,s32 dt_val,struct phy_override_seq * seq_entry)515df2217ffSKrishna Kurapati static void qcom_snps_hsphy_override_param_update_val(
516df2217ffSKrishna Kurapati const struct override_param_map map,
517df2217ffSKrishna Kurapati s32 dt_val, struct phy_override_seq *seq_entry)
518df2217ffSKrishna Kurapati {
519df2217ffSKrishna Kurapati int i;
520df2217ffSKrishna Kurapati
521df2217ffSKrishna Kurapati /*
522df2217ffSKrishna Kurapati * Param table for each param is in increasing order
523df2217ffSKrishna Kurapati * of dt values. We need to iterate over the list to
524df2217ffSKrishna Kurapati * select the entry that matches the dt value and pick
525df2217ffSKrishna Kurapati * up the corresponding register value.
526df2217ffSKrishna Kurapati */
527df2217ffSKrishna Kurapati for (i = 0; i < map.table_size - 1; i++) {
528df2217ffSKrishna Kurapati if (map.param_table[i].value == dt_val)
529df2217ffSKrishna Kurapati break;
530df2217ffSKrishna Kurapati }
531df2217ffSKrishna Kurapati
532df2217ffSKrishna Kurapati seq_entry->need_update = true;
533df2217ffSKrishna Kurapati seq_entry->offset = map.reg_offset;
534df2217ffSKrishna Kurapati seq_entry->mask = map.param_mask;
535df2217ffSKrishna Kurapati seq_entry->value = map.param_table[i].reg_val << __ffs(map.param_mask);
536df2217ffSKrishna Kurapati }
537df2217ffSKrishna Kurapati
qcom_snps_hsphy_read_override_param_seq(struct device * dev)538df2217ffSKrishna Kurapati static void qcom_snps_hsphy_read_override_param_seq(struct device *dev)
539df2217ffSKrishna Kurapati {
540df2217ffSKrishna Kurapati struct device_node *node = dev->of_node;
541df2217ffSKrishna Kurapati s32 val;
542df2217ffSKrishna Kurapati int ret, i;
543df2217ffSKrishna Kurapati struct qcom_snps_hsphy *hsphy;
544df2217ffSKrishna Kurapati const struct override_param_map *cfg = of_device_get_match_data(dev);
545df2217ffSKrishna Kurapati
546df2217ffSKrishna Kurapati if (!cfg)
547df2217ffSKrishna Kurapati return;
548df2217ffSKrishna Kurapati
549df2217ffSKrishna Kurapati hsphy = dev_get_drvdata(dev);
550df2217ffSKrishna Kurapati
551df2217ffSKrishna Kurapati for (i = 0; cfg[i].prop_name != NULL; i++) {
552df2217ffSKrishna Kurapati ret = of_property_read_s32(node, cfg[i].prop_name, &val);
553df2217ffSKrishna Kurapati if (ret)
554df2217ffSKrishna Kurapati continue;
555df2217ffSKrishna Kurapati
556df2217ffSKrishna Kurapati qcom_snps_hsphy_override_param_update_val(cfg[i], val,
557df2217ffSKrishna Kurapati &hsphy->update_seq_cfg[i]);
558df2217ffSKrishna Kurapati dev_dbg(&hsphy->phy->dev, "Read param: %s dt_val: %d reg_val: 0x%x\n",
559df2217ffSKrishna Kurapati cfg[i].prop_name, val, hsphy->update_seq_cfg[i].value);
560df2217ffSKrishna Kurapati
561df2217ffSKrishna Kurapati }
562df2217ffSKrishna Kurapati }
563df2217ffSKrishna Kurapati
qcom_snps_hsphy_probe(struct platform_device * pdev)56451e8114fSWesley Cheng static int qcom_snps_hsphy_probe(struct platform_device *pdev)
56551e8114fSWesley Cheng {
56651e8114fSWesley Cheng struct device *dev = &pdev->dev;
56751e8114fSWesley Cheng struct qcom_snps_hsphy *hsphy;
56851e8114fSWesley Cheng struct phy_provider *phy_provider;
56951e8114fSWesley Cheng struct phy *generic_phy;
57051e8114fSWesley Cheng int ret, i;
57151e8114fSWesley Cheng int num;
57251e8114fSWesley Cheng
57351e8114fSWesley Cheng hsphy = devm_kzalloc(dev, sizeof(*hsphy), GFP_KERNEL);
57451e8114fSWesley Cheng if (!hsphy)
57551e8114fSWesley Cheng return -ENOMEM;
57651e8114fSWesley Cheng
5778a0eb8f9SAdrien Thierry hsphy->dev = dev;
5788a0eb8f9SAdrien Thierry
57951e8114fSWesley Cheng hsphy->base = devm_platform_ioremap_resource(pdev, 0);
58051e8114fSWesley Cheng if (IS_ERR(hsphy->base))
58151e8114fSWesley Cheng return PTR_ERR(hsphy->base);
58251e8114fSWesley Cheng
5838a0eb8f9SAdrien Thierry ret = qcom_snps_hsphy_clk_init(hsphy);
5848a0eb8f9SAdrien Thierry if (ret)
5858a0eb8f9SAdrien Thierry return dev_err_probe(dev, ret, "failed to initialize clocks\n");
58651e8114fSWesley Cheng
58751e8114fSWesley Cheng hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
58851e8114fSWesley Cheng if (IS_ERR(hsphy->phy_reset)) {
58951e8114fSWesley Cheng dev_err(dev, "failed to get phy core reset\n");
59051e8114fSWesley Cheng return PTR_ERR(hsphy->phy_reset);
59151e8114fSWesley Cheng }
59251e8114fSWesley Cheng
59351e8114fSWesley Cheng num = ARRAY_SIZE(hsphy->vregs);
59451e8114fSWesley Cheng for (i = 0; i < num; i++)
59551e8114fSWesley Cheng hsphy->vregs[i].supply = qcom_snps_hsphy_vreg_names[i];
59651e8114fSWesley Cheng
59751e8114fSWesley Cheng ret = devm_regulator_bulk_get(dev, num, hsphy->vregs);
598668dc8afSYuan Can if (ret)
599668dc8afSYuan Can return dev_err_probe(dev, ret,
600668dc8afSYuan Can "failed to get regulator supplies\n");
60151e8114fSWesley Cheng
6020d75f508SWesley Cheng pm_runtime_set_active(dev);
6030d75f508SWesley Cheng pm_runtime_enable(dev);
6040d75f508SWesley Cheng /*
6050d75f508SWesley Cheng * Prevent runtime pm from being ON by default. Users can enable
6060d75f508SWesley Cheng * it using power/control in sysfs.
6070d75f508SWesley Cheng */
6080d75f508SWesley Cheng pm_runtime_forbid(dev);
6090d75f508SWesley Cheng
61051e8114fSWesley Cheng generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops);
61151e8114fSWesley Cheng if (IS_ERR(generic_phy)) {
61251e8114fSWesley Cheng ret = PTR_ERR(generic_phy);
61351e8114fSWesley Cheng dev_err(dev, "failed to create phy, %d\n", ret);
61451e8114fSWesley Cheng return ret;
61551e8114fSWesley Cheng }
61651e8114fSWesley Cheng hsphy->phy = generic_phy;
61751e8114fSWesley Cheng
61851e8114fSWesley Cheng dev_set_drvdata(dev, hsphy);
61951e8114fSWesley Cheng phy_set_drvdata(generic_phy, hsphy);
620df2217ffSKrishna Kurapati qcom_snps_hsphy_read_override_param_seq(dev);
62151e8114fSWesley Cheng
62251e8114fSWesley Cheng phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
62351e8114fSWesley Cheng if (!IS_ERR(phy_provider))
62451e8114fSWesley Cheng dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");
6250d75f508SWesley Cheng else
6260d75f508SWesley Cheng pm_runtime_disable(dev);
62751e8114fSWesley Cheng
62851e8114fSWesley Cheng return PTR_ERR_OR_ZERO(phy_provider);
62951e8114fSWesley Cheng }
63051e8114fSWesley Cheng
63151e8114fSWesley Cheng static struct platform_driver qcom_snps_hsphy_driver = {
63251e8114fSWesley Cheng .probe = qcom_snps_hsphy_probe,
63351e8114fSWesley Cheng .driver = {
63451e8114fSWesley Cheng .name = "qcom-snps-hs-femto-v2-phy",
6350d75f508SWesley Cheng .pm = &qcom_snps_hsphy_pm_ops,
63651e8114fSWesley Cheng .of_match_table = qcom_snps_hsphy_of_match_table,
63751e8114fSWesley Cheng },
63851e8114fSWesley Cheng };
63951e8114fSWesley Cheng
64051e8114fSWesley Cheng module_platform_driver(qcom_snps_hsphy_driver);
64151e8114fSWesley Cheng
64251e8114fSWesley Cheng MODULE_DESCRIPTION("Qualcomm SNPS FEMTO USB HS PHY V2 driver");
64351e8114fSWesley Cheng MODULE_LICENSE("GPL v2");
644