blob: 81345790af355f6bfe5c0411afa2976bfef4937b [file] [log] [blame]
Mayank Rana3cb43a32016-08-02 12:10:20 -07001/*
Jack Phamffe2ec42017-03-22 12:03:53 -07002 * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
Mayank Rana3cb43a32016-08-02 12:10:20 -07003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/err.h>
18#include <linux/slab.h>
19#include <linux/io.h>
20#include <linux/of.h>
21#include <linux/of_device.h>
22#include <linux/platform_device.h>
23#include <linux/regulator/consumer.h>
24#include <linux/usb/phy.h>
Mayank Rana3cb43a32016-08-02 12:10:20 -070025#include <linux/clk.h>
Amit Nischal4d278212016-06-06 17:54:34 +053026#include <linux/reset.h>
Mayank Rana3cb43a32016-08-02 12:10:20 -070027
28enum core_ldo_levels {
29 CORE_LEVEL_NONE = 0,
30 CORE_LEVEL_MIN,
31 CORE_LEVEL_MAX,
32};
33
34#define INIT_MAX_TIME_USEC 1000
35
36/* default CORE votlage and load values */
37#define USB_SSPHY_1P2_VOL_MIN 1200000 /* uV */
38#define USB_SSPHY_1P2_VOL_MAX 1200000 /* uV */
39#define USB_SSPHY_HPM_LOAD 23000 /* uA */
40
41/* USB3PHY_PCIE_USB3_PCS_PCS_STATUS bit */
42#define PHYSTATUS BIT(6)
43
44/* PCIE_USB3_PHY_AUTONOMOUS_MODE_CTRL bits */
45#define ARCVR_DTCT_EN BIT(0)
46#define ALFPS_DTCT_EN BIT(1)
47#define ARCVR_DTCT_EVENT_SEL BIT(4)
48
Mayank Rana4c40cd92017-03-21 14:40:55 -070049/*
50 * register bits
51 * PCIE_USB3_PHY_PCS_MISC_TYPEC_CTRL - for QMP USB PHY
52 * USB3_DP_COM_PHY_MODE_CTRL - for QMP USB DP Combo PHY
53 */
Mayank Rana3cb43a32016-08-02 12:10:20 -070054
55/* 0 - selects Lane A. 1 - selects Lane B */
56#define SW_PORTSELECT BIT(0)
57/* port select mux: 1 - sw control. 0 - HW control*/
58#define SW_PORTSELECT_MX BIT(1)
59
Mayank Rana4c40cd92017-03-21 14:40:55 -070060/* USB3_DP_PHY_USB3_DP_COM_SWI_CTRL bits */
61
62/* LANE related register read/write with USB3 */
63#define USB3_SWI_ACT_ACCESS_EN BIT(0)
64/* LANE related register read/write with DP */
65#define DP_SWI_ACT_ACCESS_EN BIT(1)
66
67/* USB3_DP_COM_RESET_OVRD_CTRL bits */
68
69/* DP PHY soft reset */
70#define SW_DPPHY_RESET BIT(0)
71/* mux to select DP PHY reset control, 0:HW control, 1: software reset */
72#define SW_DPPHY_RESET_MUX BIT(1)
73/* USB3 PHY soft reset */
74#define SW_USB3PHY_RESET BIT(2)
75/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */
76#define SW_USB3PHY_RESET_MUX BIT(3)
77
78/* USB3_DP_COM_PHY_MODE_CTRL bits */
79#define USB3_MODE BIT(0) /* enables USB3 mode */
80#define DP_MODE BIT(1) /* enables DP mode */
81
Mayank Rana3cb43a32016-08-02 12:10:20 -070082enum qmp_phy_rev_reg {
83 USB3_PHY_PCS_STATUS,
84 USB3_PHY_AUTONOMOUS_MODE_CTRL,
85 USB3_PHY_LFPS_RXTERM_IRQ_CLEAR,
86 USB3_PHY_POWER_DOWN_CONTROL,
87 USB3_PHY_SW_RESET,
88 USB3_PHY_START,
Mayank Rana4c40cd92017-03-21 14:40:55 -070089
90 /* USB DP Combo PHY related */
91 USB3_DP_DP_PHY_PD_CTL,
92 USB3_DP_COM_POWER_DOWN_CTRL,
93 USB3_DP_COM_SW_RESET,
94 USB3_DP_COM_RESET_OVRD_CTRL,
95 USB3_DP_COM_PHY_MODE_CTRL,
96 USB3_DP_COM_TYPEC_CTRL,
97 USB3_DP_COM_SWI_CTRL,
98 USB3_PCS_MISC_CLAMP_ENABLE,
99 /* TypeC port select configuration (optional) */
Mayank Rana3cb43a32016-08-02 12:10:20 -0700100 USB3_PHY_PCS_MISC_TYPEC_CTRL,
101 USB3_PHY_REG_MAX,
102};
103
104/* reg values to write */
105struct qmp_reg_val {
106 u32 offset;
107 u32 val;
108 u32 delay;
109};
110
111struct msm_ssphy_qmp {
112 struct usb_phy phy;
113 void __iomem *base;
114 void __iomem *vls_clamp_reg;
115 void __iomem *tcsr_usb3_dp_phymode;
116
117 struct regulator *vdd;
118 int vdd_levels[3]; /* none, low, high */
119 struct regulator *core_ldo;
120 int core_voltage_levels[3];
121 struct clk *ref_clk_src;
122 struct clk *ref_clk;
123 struct clk *aux_clk;
Mayank Rana561d0442017-03-16 18:19:14 -0700124 struct clk *com_aux_clk;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700125 struct clk *cfg_ahb_clk;
126 struct clk *pipe_clk;
Amit Nischal4d278212016-06-06 17:54:34 +0530127 struct reset_control *phy_reset;
128 struct reset_control *phy_phy_reset;
Mayank Rana807b8962017-04-11 20:50:11 -0700129 struct reset_control *global_phy_reset;
Hemant Kumar16d1a052016-08-30 16:54:19 -0700130 bool power_enabled;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700131 bool clk_enabled;
132 bool cable_connected;
133 bool in_suspend;
134 bool emulation;
135 unsigned int *phy_reg; /* revision based offset */
136 unsigned int *qmp_phy_init_seq;
137 int init_seq_len;
138 unsigned int *qmp_phy_reg_offset;
139 int reg_offset_cnt;
140};
141
142static const struct of_device_id msm_usb_id_table[] = {
143 {
144 .compatible = "qcom,usb-ssphy-qmp",
145 },
146 {
147 .compatible = "qcom,usb-ssphy-qmp-v1",
148 },
149 {
150 .compatible = "qcom,usb-ssphy-qmp-v2",
151 },
Mayank Rana4c40cd92017-03-21 14:40:55 -0700152 {
153 .compatible = "qcom,usb-ssphy-qmp-dp-combo",
154 },
Hemant Kumarbce97a22017-12-07 16:17:38 -0800155 {
156 .compatible = "qcom,usb-ssphy-qmp-usb3-or-dp",
157 },
Mayank Rana3cb43a32016-08-02 12:10:20 -0700158 { },
159};
160MODULE_DEVICE_TABLE(of, msm_usb_id_table);
161
Mayank Rana807b8962017-04-11 20:50:11 -0700162static void usb_qmp_powerup_phy(struct msm_ssphy_qmp *phy);
Mayank Rana561d0442017-03-16 18:19:14 -0700163static void msm_ssphy_qmp_enable_clks(struct msm_ssphy_qmp *phy, bool on);
164
Mayank Rana3cb43a32016-08-02 12:10:20 -0700165static inline char *get_cable_status_str(struct msm_ssphy_qmp *phy)
166{
167 return phy->cable_connected ? "connected" : "disconnected";
168}
169
170static void msm_ssusb_qmp_clr_lfps_rxterm_int(struct msm_ssphy_qmp *phy)
171{
172 writeb_relaxed(1, phy->base +
173 phy->phy_reg[USB3_PHY_LFPS_RXTERM_IRQ_CLEAR]);
174 /* flush the previous write before next write */
175 wmb();
176 writeb_relaxed(0, phy->base +
177 phy->phy_reg[USB3_PHY_LFPS_RXTERM_IRQ_CLEAR]);
178}
179
Mayank Rana4c40cd92017-03-21 14:40:55 -0700180static void msm_ssusb_qmp_clamp_enable(struct msm_ssphy_qmp *phy, bool val)
181{
182 switch (phy->phy.type) {
Hemant Kumarbce97a22017-12-07 16:17:38 -0800183 case USB_PHY_TYPE_USB3_AND_DP:
Mayank Rana4c40cd92017-03-21 14:40:55 -0700184 writel_relaxed(!val, phy->base +
185 phy->phy_reg[USB3_PCS_MISC_CLAMP_ENABLE]);
186 break;
Hemant Kumarbce97a22017-12-07 16:17:38 -0800187 case USB_PHY_TYPE_USB3_OR_DP:
Mayank Rana4c40cd92017-03-21 14:40:55 -0700188 case USB_PHY_TYPE_USB3:
189 writel_relaxed(!!val, phy->vls_clamp_reg);
190 break;
191 default:
192 break;
193 }
194}
195
Mayank Rana3cb43a32016-08-02 12:10:20 -0700196static void msm_ssusb_qmp_enable_autonomous(struct msm_ssphy_qmp *phy,
197 int enable)
198{
199 u8 val;
200 unsigned int autonomous_mode_offset =
201 phy->phy_reg[USB3_PHY_AUTONOMOUS_MODE_CTRL];
202
203 dev_dbg(phy->phy.dev, "enabling QMP autonomous mode with cable %s\n",
204 get_cable_status_str(phy));
205
206 if (enable) {
207 msm_ssusb_qmp_clr_lfps_rxterm_int(phy);
Mayank Rana0d5efd72017-06-08 10:06:00 -0700208 val = readb_relaxed(phy->base + autonomous_mode_offset);
209 val |= ARCVR_DTCT_EN;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700210 if (phy->phy.flags & DEVICE_IN_SS_MODE) {
Mayank Rana3cb43a32016-08-02 12:10:20 -0700211 val |= ALFPS_DTCT_EN;
212 val &= ~ARCVR_DTCT_EVENT_SEL;
Mayank Rana0d5efd72017-06-08 10:06:00 -0700213 } else {
214 val &= ~ALFPS_DTCT_EN;
215 val |= ARCVR_DTCT_EVENT_SEL;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700216 }
Mayank Rana0d5efd72017-06-08 10:06:00 -0700217 writeb_relaxed(val, phy->base + autonomous_mode_offset);
Mayank Rana4c40cd92017-03-21 14:40:55 -0700218 msm_ssusb_qmp_clamp_enable(phy, true);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700219 } else {
Mayank Rana4c40cd92017-03-21 14:40:55 -0700220 msm_ssusb_qmp_clamp_enable(phy, false);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700221 writeb_relaxed(0, phy->base + autonomous_mode_offset);
222 msm_ssusb_qmp_clr_lfps_rxterm_int(phy);
223 }
224}
225
Mayank Rana3cb43a32016-08-02 12:10:20 -0700226static int msm_ssusb_qmp_ldo_enable(struct msm_ssphy_qmp *phy, int on)
227{
Hemant Kumar16d1a052016-08-30 16:54:19 -0700228 int min, rc = 0;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700229
230 dev_dbg(phy->phy.dev, "reg (%s)\n", on ? "HPM" : "LPM");
231
Hemant Kumar16d1a052016-08-30 16:54:19 -0700232 if (phy->power_enabled == on) {
233 dev_dbg(phy->phy.dev, "PHYs' regulators status %d\n",
234 phy->power_enabled);
235 return 0;
236 }
237
238 phy->power_enabled = on;
239
240 min = on ? 1 : 0; /* low or none? */
241
Mayank Rana3cb43a32016-08-02 12:10:20 -0700242 if (!on)
243 goto disable_regulators;
244
Hemant Kumar16d1a052016-08-30 16:54:19 -0700245 rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
246 phy->vdd_levels[2]);
247 if (rc) {
248 dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
249 return rc;
250 }
251
252 dev_dbg(phy->phy.dev, "min_vol:%d max_vol:%d\n",
253 phy->vdd_levels[min], phy->vdd_levels[2]);
254
255 rc = regulator_enable(phy->vdd);
256 if (rc) {
257 dev_err(phy->phy.dev,
258 "regulator_enable(phy->vdd) failed, ret=%d",
259 rc);
260 goto unconfig_vdd;
261 }
Mayank Rana3cb43a32016-08-02 12:10:20 -0700262
263 rc = regulator_set_load(phy->core_ldo, USB_SSPHY_HPM_LOAD);
264 if (rc < 0) {
265 dev_err(phy->phy.dev, "Unable to set HPM of core_ldo\n");
Hemant Kumar16d1a052016-08-30 16:54:19 -0700266 goto disable_vdd;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700267 }
268
269 rc = regulator_set_voltage(phy->core_ldo,
270 phy->core_voltage_levels[CORE_LEVEL_MIN],
271 phy->core_voltage_levels[CORE_LEVEL_MAX]);
272 if (rc) {
273 dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
274 goto put_core_ldo_lpm;
275 }
276
277 rc = regulator_enable(phy->core_ldo);
278 if (rc) {
279 dev_err(phy->phy.dev, "Unable to enable core_ldo\n");
280 goto unset_core_ldo;
281 }
282
283 return 0;
284
285disable_regulators:
286 rc = regulator_disable(phy->core_ldo);
287 if (rc)
288 dev_err(phy->phy.dev, "Unable to disable core_ldo\n");
289
290unset_core_ldo:
291 rc = regulator_set_voltage(phy->core_ldo,
292 phy->core_voltage_levels[CORE_LEVEL_NONE],
293 phy->core_voltage_levels[CORE_LEVEL_MAX]);
294 if (rc)
295 dev_err(phy->phy.dev, "unable to set voltage for core_ldo\n");
296
297put_core_ldo_lpm:
298 rc = regulator_set_load(phy->core_ldo, 0);
299 if (rc < 0)
300 dev_err(phy->phy.dev, "Unable to set LPM of core_ldo\n");
301
Hemant Kumar16d1a052016-08-30 16:54:19 -0700302disable_vdd:
303 rc = regulator_disable(phy->vdd);
304 if (rc)
305 dev_err(phy->phy.dev, "regulator_disable(phy->vdd) failed, ret=%d",
306 rc);
307
308unconfig_vdd:
309 rc = regulator_set_voltage(phy->vdd, phy->vdd_levels[min],
310 phy->vdd_levels[2]);
311 if (rc)
312 dev_err(phy->phy.dev, "unable to set voltage for ssusb vdd\n");
313
Mayank Rana3cb43a32016-08-02 12:10:20 -0700314 return rc < 0 ? rc : 0;
315}
316
317static int configure_phy_regs(struct usb_phy *uphy,
318 const struct qmp_reg_val *reg)
319{
320 struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp,
321 phy);
322
323 if (!reg) {
324 dev_err(uphy->dev, "NULL PHY configuration\n");
325 return -EINVAL;
326 }
327
328 while (reg->offset != -1) {
329 writel_relaxed(reg->val, phy->base + reg->offset);
330 if (reg->delay)
331 usleep_range(reg->delay, reg->delay + 10);
332 reg++;
333 }
334 return 0;
335}
336
Mayank Rana4c40cd92017-03-21 14:40:55 -0700337static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy)
338{
339 int val;
340
341 /* perform lane selection */
342 val = -EINVAL;
Mayank Rana807b8962017-04-11 20:50:11 -0700343 if (phy->phy.flags & PHY_LANE_A)
Mayank Rana4c40cd92017-03-21 14:40:55 -0700344 val = SW_PORTSELECT_MX;
Mayank Rana807b8962017-04-11 20:50:11 -0700345 else if (phy->phy.flags & PHY_LANE_B)
Mayank Rana4c40cd92017-03-21 14:40:55 -0700346 val = SW_PORTSELECT | SW_PORTSELECT_MX;
Mayank Rana807b8962017-04-11 20:50:11 -0700347
348 /* PHY must be powered up before updating portselect and phymode. */
349 usb_qmp_powerup_phy(phy);
Mayank Rana4c40cd92017-03-21 14:40:55 -0700350
351 switch (phy->phy.type) {
Hemant Kumarbce97a22017-12-07 16:17:38 -0800352 case USB_PHY_TYPE_USB3_AND_DP:
Mayank Rana4c40cd92017-03-21 14:40:55 -0700353 /* override hardware control for reset of qmp phy */
354 writel_relaxed(SW_DPPHY_RESET_MUX | SW_DPPHY_RESET |
355 SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET,
356 phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]);
357
358 /* update port select */
359 if (val > 0) {
360 dev_err(phy->phy.dev,
361 "USB DP QMP PHY: Update TYPEC CTRL(%d)\n", val);
362 writel_relaxed(val, phy->base +
363 phy->phy_reg[USB3_DP_COM_TYPEC_CTRL]);
364 }
365
366 writel_relaxed(USB3_MODE | DP_MODE,
367 phy->base + phy->phy_reg[USB3_DP_COM_PHY_MODE_CTRL]);
368
Mayank Rana4c40cd92017-03-21 14:40:55 -0700369 /* bring both QMP USB and QMP DP PHYs PCS block out of reset */
370 writel_relaxed(0x00,
371 phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]);
372 break;
Hemant Kumarbce97a22017-12-07 16:17:38 -0800373 case USB_PHY_TYPE_USB3_OR_DP:
Mayank Rana4c40cd92017-03-21 14:40:55 -0700374 if (val > 0) {
375 dev_err(phy->phy.dev,
376 "USB QMP PHY: Update TYPEC CTRL(%d)\n", val);
377 writel_relaxed(val, phy->base +
378 phy->phy_reg[USB3_PHY_PCS_MISC_TYPEC_CTRL]);
379 }
380 break;
381 default:
Hemant Kumarbce97a22017-12-07 16:17:38 -0800382 dev_dbg(phy->phy.dev, "no portselect for phy type %d\n",
383 phy->phy.type);
Mayank Rana4c40cd92017-03-21 14:40:55 -0700384 break;
385 }
386
387 /* Make sure above selection and reset sequence is gone through */
388 mb();
389}
390
391static void usb_qmp_powerup_phy(struct msm_ssphy_qmp *phy)
392{
393 switch (phy->phy.type) {
Hemant Kumarbce97a22017-12-07 16:17:38 -0800394 case USB_PHY_TYPE_USB3_AND_DP:
Mayank Rana4c40cd92017-03-21 14:40:55 -0700395 /* power up USB3 and DP common logic block */
396 writel_relaxed(0x01,
397 phy->base + phy->phy_reg[USB3_DP_COM_POWER_DOWN_CTRL]);
398
399 /*
Mayank Rana807b8962017-04-11 20:50:11 -0700400 * Don't write 0x0 to DP_COM_SW_RESET here as portselect and
401 * phymode operation needs DP_COM_SW_RESET as 0x1.
402 * msm_ssphy_qmp_init() writes 0x0 to DP_COM_SW_RESET before
403 * initializing PHY.
Mayank Rana4c40cd92017-03-21 14:40:55 -0700404 */
405
406 /* intentional fall-through */
Hemant Kumarbce97a22017-12-07 16:17:38 -0800407 case USB_PHY_TYPE_USB3_OR_DP:
Mayank Rana4c40cd92017-03-21 14:40:55 -0700408 case USB_PHY_TYPE_USB3:
409 /* power up USB3 PHY */
410 writel_relaxed(0x01,
411 phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
412 break;
413 default:
414 dev_err(phy->phy.dev, "phy_powerup: Unknown USB QMP PHY type\n");
415 break;
416 }
417
418 /* Make sure that above write completed to power up PHY */
419 mb();
420}
421
Mayank Rana3cb43a32016-08-02 12:10:20 -0700422/* SSPHY Initialization */
423static int msm_ssphy_qmp_init(struct usb_phy *uphy)
424{
425 struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp,
426 phy);
Mayank Rana4c40cd92017-03-21 14:40:55 -0700427 int ret;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700428 unsigned int init_timeout_usec = INIT_MAX_TIME_USEC;
429 const struct qmp_reg_val *reg = NULL;
430
431 dev_dbg(uphy->dev, "Initializing QMP phy\n");
432
433 if (phy->emulation)
434 return 0;
435
Hemant Kumar16d1a052016-08-30 16:54:19 -0700436 ret = msm_ssusb_qmp_ldo_enable(phy, 1);
437 if (ret) {
438 dev_err(phy->phy.dev,
439 "msm_ssusb_qmp_ldo_enable(1) failed, ret=%d\n",
440 ret);
441 return ret;
442 }
443
Mayank Rana561d0442017-03-16 18:19:14 -0700444 msm_ssphy_qmp_enable_clks(phy, true);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700445
Mayank Rana4c40cd92017-03-21 14:40:55 -0700446 /* select appropriate port select and PHY mode if applicable */
447 usb_qmp_update_portselect_phymode(phy);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700448
Mayank Rana807b8962017-04-11 20:50:11 -0700449 /* power up PHY */
450 usb_qmp_powerup_phy(phy);
451
Mayank Rana3cb43a32016-08-02 12:10:20 -0700452 reg = (struct qmp_reg_val *)phy->qmp_phy_init_seq;
453
454 /* Main configuration */
455 ret = configure_phy_regs(uphy, reg);
456 if (ret) {
457 dev_err(uphy->dev, "Failed the main PHY configuration\n");
458 return ret;
459 }
460
Mayank Rana4c40cd92017-03-21 14:40:55 -0700461 /* perform software reset of PHY common logic */
Hemant Kumarbce97a22017-12-07 16:17:38 -0800462 if (phy->phy.type == USB_PHY_TYPE_USB3_AND_DP)
Mayank Rana4c40cd92017-03-21 14:40:55 -0700463 writel_relaxed(0x00,
464 phy->base + phy->phy_reg[USB3_DP_COM_SW_RESET]);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700465
Mayank Rana4c40cd92017-03-21 14:40:55 -0700466 /* perform software reset of PCS/Serdes */
Mayank Rana3cb43a32016-08-02 12:10:20 -0700467 writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_PHY_SW_RESET]);
Mayank Rana4c40cd92017-03-21 14:40:55 -0700468 /* start PCS/Serdes to operation mode */
469 writel_relaxed(0x03, phy->base + phy->phy_reg[USB3_PHY_START]);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700470
471 /* Make sure above write completed to bring PHY out of reset */
472 mb();
473
474 /* Wait for PHY initialization to be done */
475 do {
476 if (readl_relaxed(phy->base +
477 phy->phy_reg[USB3_PHY_PCS_STATUS]) & PHYSTATUS)
478 usleep_range(1, 2);
479 else
480 break;
481 } while (--init_timeout_usec);
482
483 if (!init_timeout_usec) {
484 dev_err(uphy->dev, "QMP PHY initialization timeout\n");
485 dev_err(uphy->dev, "USB3_PHY_PCS_STATUS:%x\n",
486 readl_relaxed(phy->base +
487 phy->phy_reg[USB3_PHY_PCS_STATUS]));
488 return -EBUSY;
489 };
490
491 return 0;
492}
493
Mayank Rana4c40cd92017-03-21 14:40:55 -0700494static int msm_ssphy_qmp_dp_combo_reset(struct usb_phy *uphy)
495{
496 struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp,
497 phy);
498 int ret = 0;
499
Mayank Rana4c40cd92017-03-21 14:40:55 -0700500 dev_dbg(uphy->dev, "Global reset of QMP DP combo phy\n");
Mayank Rana807b8962017-04-11 20:50:11 -0700501 /* Assert global PHY reset */
502 ret = reset_control_assert(phy->global_phy_reset);
503 if (ret) {
504 dev_err(uphy->dev, "global_phy_reset assert failed\n");
505 goto exit;
506 }
507
508 /* Assert QMP USB PHY reset */
Mayank Rana4c40cd92017-03-21 14:40:55 -0700509 ret = reset_control_assert(phy->phy_reset);
510 if (ret) {
511 dev_err(uphy->dev, "phy_reset assert failed\n");
512 goto exit;
513 }
514
Mayank Rana807b8962017-04-11 20:50:11 -0700515 /* De-Assert QMP USB PHY reset */
Mayank Rana4c40cd92017-03-21 14:40:55 -0700516 ret = reset_control_deassert(phy->phy_reset);
517 if (ret)
518 dev_err(uphy->dev, "phy_reset deassert failed\n");
519
Mayank Rana807b8962017-04-11 20:50:11 -0700520 /* De-Assert global PHY reset */
521 ret = reset_control_deassert(phy->global_phy_reset);
522 if (ret)
523 dev_err(uphy->dev, "global_phy_reset deassert failed\n");
524
Mayank Rana4c40cd92017-03-21 14:40:55 -0700525exit:
526 return ret;
527}
528
Mayank Rana3cb43a32016-08-02 12:10:20 -0700529static int msm_ssphy_qmp_reset(struct usb_phy *uphy)
530{
531 struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp,
532 phy);
533 int ret;
534
535 dev_dbg(uphy->dev, "Resetting QMP phy\n");
536
537 /* Assert USB3 PHY reset */
Amit Nischal4d278212016-06-06 17:54:34 +0530538 ret = reset_control_assert(phy->phy_phy_reset);
539 if (ret) {
540 dev_err(uphy->dev, "phy_phy_reset assert failed\n");
541 goto exit;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700542 }
543
544 /* Assert USB3 PHY CSR reset */
Amit Nischal4d278212016-06-06 17:54:34 +0530545 ret = reset_control_assert(phy->phy_reset);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700546 if (ret) {
Amit Nischal4d278212016-06-06 17:54:34 +0530547 dev_err(uphy->dev, "phy_reset assert failed\n");
Mayank Rana3cb43a32016-08-02 12:10:20 -0700548 goto deassert_phy_phy_reset;
549 }
550
Jack Phamffe2ec42017-03-22 12:03:53 -0700551 /* select usb3 phy mode */
552 if (phy->tcsr_usb3_dp_phymode)
553 writel_relaxed(0x0, phy->tcsr_usb3_dp_phymode);
554
Mayank Rana3cb43a32016-08-02 12:10:20 -0700555 /* Deassert USB3 PHY CSR reset */
Amit Nischal4d278212016-06-06 17:54:34 +0530556 ret = reset_control_deassert(phy->phy_reset);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700557 if (ret) {
Amit Nischal4d278212016-06-06 17:54:34 +0530558 dev_err(uphy->dev, "phy_reset deassert failed\n");
Mayank Rana3cb43a32016-08-02 12:10:20 -0700559 goto deassert_phy_phy_reset;
560 }
561
562 /* Deassert USB3 PHY reset */
Amit Nischal4d278212016-06-06 17:54:34 +0530563 ret = reset_control_deassert(phy->phy_phy_reset);
564 if (ret) {
565 dev_err(uphy->dev, "phy_phy_reset deassert failed\n");
566 goto exit;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700567 }
568
569 return 0;
570
571deassert_phy_phy_reset:
Amit Nischal4d278212016-06-06 17:54:34 +0530572 ret = reset_control_deassert(phy->phy_phy_reset);
573 if (ret)
574 dev_err(uphy->dev, "phy_phy_reset deassert failed\n");
Mayank Rana3cb43a32016-08-02 12:10:20 -0700575exit:
576 phy->in_suspend = false;
577
578 return ret;
579}
580
581static int msm_ssphy_power_enable(struct msm_ssphy_qmp *phy, bool on)
582{
583 bool host = phy->phy.flags & PHY_HOST_MODE;
584 int ret = 0;
585
586 /*
587 * Turn off the phy's LDOs when cable is disconnected for device mode
588 * with external vbus_id indication.
589 */
590 if (!host && !phy->cable_connected) {
591 if (on) {
Mayank Rana3cb43a32016-08-02 12:10:20 -0700592 ret = msm_ssusb_qmp_ldo_enable(phy, 1);
593 if (ret)
594 dev_err(phy->phy.dev,
595 "msm_ssusb_qmp_ldo_enable(1) failed, ret=%d\n",
596 ret);
597 } else {
598 ret = msm_ssusb_qmp_ldo_enable(phy, 0);
599 if (ret)
600 dev_err(phy->phy.dev,
601 "msm_ssusb_qmp_ldo_enable(0) failed, ret=%d\n",
602 ret);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700603 }
604 }
605
606 return ret;
607}
608
609/**
610 * Performs QMP PHY suspend/resume functionality.
611 *
612 * @uphy - usb phy pointer.
613 * @suspend - to enable suspend or not. 1 - suspend, 0 - resume
614 *
615 */
616static int msm_ssphy_qmp_set_suspend(struct usb_phy *uphy, int suspend)
617{
618 struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp,
619 phy);
620
621 dev_dbg(uphy->dev, "QMP PHY set_suspend for %s called with cable %s\n",
622 (suspend ? "suspend" : "resume"),
623 get_cable_status_str(phy));
624
625 if (phy->in_suspend == suspend) {
626 dev_dbg(uphy->dev, "%s: USB PHY is already %s.\n",
627 __func__, (suspend ? "suspended" : "resumed"));
628 return 0;
629 }
630
631 if (suspend) {
Hemant Kumar3e7dcb52017-09-25 18:01:23 -0700632 if (phy->cable_connected)
Mayank Rana3cb43a32016-08-02 12:10:20 -0700633 msm_ssusb_qmp_enable_autonomous(phy, 1);
634
Mayank Rana813952b2016-02-04 11:11:42 -0800635 /* Make sure above write completed with PHY */
636 wmb();
637
Mayank Rana561d0442017-03-16 18:19:14 -0700638 msm_ssphy_qmp_enable_clks(phy, false);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700639 phy->in_suspend = true;
640 msm_ssphy_power_enable(phy, 0);
641 dev_dbg(uphy->dev, "QMP PHY is suspend\n");
642 } else {
643 msm_ssphy_power_enable(phy, 1);
Mayank Rana561d0442017-03-16 18:19:14 -0700644 msm_ssphy_qmp_enable_clks(phy, true);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700645 if (!phy->cable_connected) {
646 writel_relaxed(0x01,
647 phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
648 } else {
649 msm_ssusb_qmp_enable_autonomous(phy, 0);
650 }
Mayank Rana813952b2016-02-04 11:11:42 -0800651
652 /* Make sure that above write completed with PHY */
653 wmb();
654
Mayank Rana3cb43a32016-08-02 12:10:20 -0700655 phy->in_suspend = false;
656 dev_dbg(uphy->dev, "QMP PHY is resumed\n");
657 }
658
659 return 0;
660}
661
662static int msm_ssphy_qmp_notify_connect(struct usb_phy *uphy,
663 enum usb_device_speed speed)
664{
665 struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp,
666 phy);
667
668 dev_dbg(uphy->dev, "QMP phy connect notification\n");
669 phy->cable_connected = true;
670 dev_dbg(uphy->dev, "cable_connected=%d\n", phy->cable_connected);
671 return 0;
672}
673
674static int msm_ssphy_qmp_notify_disconnect(struct usb_phy *uphy,
675 enum usb_device_speed speed)
676{
677 struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp,
678 phy);
679
Hemant Kumar3e7dcb52017-09-25 18:01:23 -0700680 writel_relaxed(0x00,
681 phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
682 readl_relaxed(phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
683
Mayank Rana3cb43a32016-08-02 12:10:20 -0700684 dev_dbg(uphy->dev, "QMP phy disconnect notification\n");
685 dev_dbg(uphy->dev, " cable_connected=%d\n", phy->cable_connected);
686 phy->cable_connected = false;
687 return 0;
688}
689
Mayank Rana561d0442017-03-16 18:19:14 -0700690static int msm_ssphy_qmp_get_clks(struct msm_ssphy_qmp *phy, struct device *dev)
Mayank Rana3cb43a32016-08-02 12:10:20 -0700691{
Mayank Rana561d0442017-03-16 18:19:14 -0700692 int ret = 0;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700693
694 phy->aux_clk = devm_clk_get(dev, "aux_clk");
695 if (IS_ERR(phy->aux_clk)) {
696 ret = PTR_ERR(phy->aux_clk);
697 phy->aux_clk = NULL;
698 if (ret != -EPROBE_DEFER)
699 dev_err(dev, "failed to get aux_clk\n");
700 goto err;
701 }
Mayank Rana3cb43a32016-08-02 12:10:20 -0700702 clk_set_rate(phy->aux_clk, clk_round_rate(phy->aux_clk, ULONG_MAX));
703
Mayank Rana561d0442017-03-16 18:19:14 -0700704 if (of_property_match_string(dev->of_node,
705 "clock-names", "cfg_ahb_clk") >= 0) {
Mayank Rana3cb43a32016-08-02 12:10:20 -0700706 phy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb_clk");
707 if (IS_ERR(phy->cfg_ahb_clk)) {
708 ret = PTR_ERR(phy->cfg_ahb_clk);
709 if (ret != -EPROBE_DEFER)
710 dev_err(dev,
711 "failed to get cfg_ahb_clk ret %d\n", ret);
712 goto err;
713 }
714 }
715
716 phy->pipe_clk = devm_clk_get(dev, "pipe_clk");
717 if (IS_ERR(phy->pipe_clk)) {
718 ret = PTR_ERR(phy->pipe_clk);
719 phy->pipe_clk = NULL;
720 if (ret != -EPROBE_DEFER)
721 dev_err(dev, "failed to get pipe_clk\n");
722 goto err;
723 }
724
Mayank Rana561d0442017-03-16 18:19:14 -0700725 phy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
726 if (IS_ERR(phy->ref_clk_src))
727 phy->ref_clk_src = NULL;
728
729 phy->ref_clk = devm_clk_get(dev, "ref_clk");
730 if (IS_ERR(phy->ref_clk))
731 phy->ref_clk = NULL;
732
733 if (of_property_match_string(dev->of_node,
734 "clock-names", "com_aux_clk") >= 0) {
735 phy->com_aux_clk = devm_clk_get(dev, "com_aux_clk");
736 if (IS_ERR(phy->com_aux_clk)) {
737 ret = PTR_ERR(phy->com_aux_clk);
738 if (ret != -EPROBE_DEFER)
739 dev_err(dev,
740 "failed to get com_aux_clk ret %d\n", ret);
741 goto err;
742 }
743 }
744
745err:
746 return ret;
747}
748
749static void msm_ssphy_qmp_enable_clks(struct msm_ssphy_qmp *phy, bool on)
750{
751 dev_dbg(phy->phy.dev, "%s(): clk_enabled:%d on:%d\n", __func__,
752 phy->clk_enabled, on);
753
754 if (!phy->clk_enabled && on) {
755 if (phy->ref_clk_src)
756 clk_prepare_enable(phy->ref_clk_src);
757
758 if (phy->ref_clk)
759 clk_prepare_enable(phy->ref_clk);
760
761 if (phy->com_aux_clk)
762 clk_prepare_enable(phy->com_aux_clk);
763
764 clk_prepare_enable(phy->aux_clk);
765 if (phy->cfg_ahb_clk)
766 clk_prepare_enable(phy->cfg_ahb_clk);
767
768 clk_prepare_enable(phy->pipe_clk);
769 phy->clk_enabled = true;
770 }
771
772 if (phy->clk_enabled && !on) {
773 clk_disable_unprepare(phy->pipe_clk);
774
775 if (phy->cfg_ahb_clk)
776 clk_disable_unprepare(phy->cfg_ahb_clk);
777
778 clk_disable_unprepare(phy->aux_clk);
779 if (phy->com_aux_clk)
780 clk_disable_unprepare(phy->com_aux_clk);
781
782 if (phy->ref_clk)
783 clk_disable_unprepare(phy->ref_clk);
784
785 if (phy->ref_clk_src)
786 clk_disable_unprepare(phy->ref_clk_src);
787
788 phy->clk_enabled = false;
789 }
790}
791
792static int msm_ssphy_qmp_probe(struct platform_device *pdev)
793{
794 struct msm_ssphy_qmp *phy;
795 struct device *dev = &pdev->dev;
796 struct resource *res;
797 int ret = 0, size = 0, len;
798
799 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
800 if (!phy)
801 return -ENOMEM;
802
Mayank Rana4c40cd92017-03-21 14:40:55 -0700803 phy->phy.type = USB_PHY_TYPE_USB3;
804 if (of_device_is_compatible(dev->of_node,
805 "qcom,usb-ssphy-qmp-dp-combo"))
Hemant Kumarbce97a22017-12-07 16:17:38 -0800806 phy->phy.type = USB_PHY_TYPE_USB3_AND_DP;
807
808 if (of_device_is_compatible(dev->of_node,
809 "qcom,usb-ssphy-qmp-usb-or-dp"))
810 phy->phy.type = USB_PHY_TYPE_USB3_OR_DP;
Mayank Rana4c40cd92017-03-21 14:40:55 -0700811
Mayank Rana561d0442017-03-16 18:19:14 -0700812 ret = msm_ssphy_qmp_get_clks(phy, dev);
813 if (ret)
814 goto err;
815
Amit Nischal4d278212016-06-06 17:54:34 +0530816 phy->phy_reset = devm_reset_control_get(dev, "phy_reset");
817 if (IS_ERR(phy->phy_reset)) {
818 ret = PTR_ERR(phy->phy_reset);
819 dev_dbg(dev, "failed to get phy_reset\n");
820 goto err;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700821 }
822
Hemant Kumarbce97a22017-12-07 16:17:38 -0800823 if (phy->phy.type == USB_PHY_TYPE_USB3_AND_DP) {
Mayank Rana807b8962017-04-11 20:50:11 -0700824 phy->global_phy_reset = devm_reset_control_get(dev,
825 "global_phy_reset");
826 if (IS_ERR(phy->global_phy_reset)) {
827 ret = PTR_ERR(phy->global_phy_reset);
828 dev_dbg(dev, "failed to get global_phy_reset\n");
829 goto err;
830 }
831 } else {
Mayank Rana4c40cd92017-03-21 14:40:55 -0700832 phy->phy_phy_reset = devm_reset_control_get(dev,
833 "phy_phy_reset");
834 if (IS_ERR(phy->phy_phy_reset)) {
835 ret = PTR_ERR(phy->phy_phy_reset);
836 dev_dbg(dev, "failed to get phy_phy_reset\n");
837 goto err;
838 }
Mayank Rana3cb43a32016-08-02 12:10:20 -0700839 }
840
841 of_get_property(dev->of_node, "qcom,qmp-phy-reg-offset", &size);
842 if (size) {
843 phy->qmp_phy_reg_offset = devm_kzalloc(dev,
844 size, GFP_KERNEL);
845 if (phy->qmp_phy_reg_offset) {
846 phy->reg_offset_cnt =
847 (size / sizeof(*phy->qmp_phy_reg_offset));
848 if (phy->reg_offset_cnt > USB3_PHY_REG_MAX) {
849 dev_err(dev, "invalid reg offset count\n");
850 return -EINVAL;
851 }
852
853 of_property_read_u32_array(dev->of_node,
854 "qcom,qmp-phy-reg-offset",
855 phy->qmp_phy_reg_offset,
856 phy->reg_offset_cnt);
857 } else {
858 dev_err(dev, "err mem alloc for qmp_phy_reg_offset\n");
859 return -ENOMEM;
860 }
861 phy->phy_reg = phy->qmp_phy_reg_offset;
862 } else {
863 dev_err(dev, "err provide qcom,qmp-phy-reg-offset\n");
864 return -EINVAL;
865 }
866
867 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
868 "qmp_phy_base");
869 if (!res) {
870 dev_err(dev, "failed getting qmp_phy_base\n");
871 return -ENODEV;
872 }
Mayank Rana4c40cd92017-03-21 14:40:55 -0700873
874 /*
875 * For USB QMP DP combo PHY, common set of registers shall be accessed
876 * by DP driver as well.
877 */
878 phy->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
879 if (IS_ERR_OR_NULL(phy->base)) {
Mayank Rana3cb43a32016-08-02 12:10:20 -0700880 ret = PTR_ERR(phy->base);
881 goto err;
882 }
883
Hemant Kumarbce97a22017-12-07 16:17:38 -0800884 if (phy->phy.type != USB_PHY_TYPE_USB3_AND_DP) {
Mayank Rana4c40cd92017-03-21 14:40:55 -0700885 res = platform_get_resource_byname(pdev,
886 IORESOURCE_MEM, "vls_clamp_reg");
887 phy->vls_clamp_reg = devm_ioremap_resource(dev, res);
888 if (IS_ERR(phy->vls_clamp_reg)) {
889 dev_err(dev, "couldn't find vls_clamp_reg address.\n");
890 return PTR_ERR(phy->vls_clamp_reg);
891 }
Mayank Rana3cb43a32016-08-02 12:10:20 -0700892 }
893
894 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
895 "tcsr_usb3_dp_phymode");
896 if (res) {
897 phy->tcsr_usb3_dp_phymode = devm_ioremap_resource(dev, res);
898 if (IS_ERR(phy->tcsr_usb3_dp_phymode)) {
899 dev_err(dev, "err getting tcsr_usb3_dp_phymode addr\n");
900 return PTR_ERR(phy->tcsr_usb3_dp_phymode);
901 }
902 }
903
904 phy->emulation = of_property_read_bool(dev->of_node,
905 "qcom,emulation");
906 if (!phy->emulation) {
907 of_get_property(dev->of_node, "qcom,qmp-phy-init-seq", &size);
908 if (size) {
909 if (size % sizeof(*phy->qmp_phy_init_seq)) {
910 dev_err(dev, "invalid init_seq_len\n");
911 return -EINVAL;
912 }
913 phy->qmp_phy_init_seq = devm_kzalloc(dev,
914 size, GFP_KERNEL);
915 if (phy->qmp_phy_init_seq) {
916 phy->init_seq_len =
917 (size / sizeof(*phy->qmp_phy_init_seq));
918
919 of_property_read_u32_array(dev->of_node,
920 "qcom,qmp-phy-init-seq",
921 phy->qmp_phy_init_seq,
922 phy->init_seq_len);
923 } else {
924 dev_err(dev, "error allocating memory for phy_init_seq\n");
925 return -EINVAL;
926 }
927 } else {
928 dev_err(dev, "error need qmp-phy-init-seq\n");
929 return -EINVAL;
930 }
931 }
932
933 /* Set default core voltage values */
934 phy->core_voltage_levels[CORE_LEVEL_NONE] = 0;
935 phy->core_voltage_levels[CORE_LEVEL_MIN] = USB_SSPHY_1P2_VOL_MIN;
936 phy->core_voltage_levels[CORE_LEVEL_MAX] = USB_SSPHY_1P2_VOL_MAX;
937
938 if (of_get_property(dev->of_node, "qcom,core-voltage-level", &len) &&
939 len == sizeof(phy->core_voltage_levels)) {
940 ret = of_property_read_u32_array(dev->of_node,
941 "qcom,core-voltage-level",
942 (u32 *)phy->core_voltage_levels,
943 len / sizeof(u32));
944 if (ret) {
945 dev_err(dev, "err qcom,core-voltage-level property\n");
946 goto err;
947 }
948 }
949
950 if (of_get_property(dev->of_node, "qcom,vdd-voltage-level", &len) &&
951 len == sizeof(phy->vdd_levels)) {
952 ret = of_property_read_u32_array(dev->of_node,
953 "qcom,vdd-voltage-level",
954 (u32 *) phy->vdd_levels,
955 len / sizeof(u32));
956 if (ret) {
957 dev_err(dev, "err qcom,vdd-voltage-level property\n");
958 goto err;
959 }
960 } else {
961 ret = -EINVAL;
962 dev_err(dev, "error invalid inputs for vdd-voltage-level\n");
963 goto err;
964 }
965
966 phy->vdd = devm_regulator_get(dev, "vdd");
967 if (IS_ERR(phy->vdd)) {
968 dev_err(dev, "unable to get vdd supply\n");
969 ret = PTR_ERR(phy->vdd);
970 goto err;
971 }
972
973 phy->core_ldo = devm_regulator_get(dev, "core");
974 if (IS_ERR(phy->core_ldo)) {
975 dev_err(dev, "unable to get core ldo supply\n");
976 ret = PTR_ERR(phy->core_ldo);
977 goto err;
978 }
979
Mayank Rana3cb43a32016-08-02 12:10:20 -0700980 platform_set_drvdata(pdev, phy);
981
982 if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override"))
983 phy->phy.flags |= PHY_VBUS_VALID_OVERRIDE;
984
985 phy->phy.dev = dev;
986 phy->phy.init = msm_ssphy_qmp_init;
987 phy->phy.set_suspend = msm_ssphy_qmp_set_suspend;
988 phy->phy.notify_connect = msm_ssphy_qmp_notify_connect;
989 phy->phy.notify_disconnect = msm_ssphy_qmp_notify_disconnect;
Mayank Rana4c40cd92017-03-21 14:40:55 -0700990
Hemant Kumarbce97a22017-12-07 16:17:38 -0800991 if (phy->phy.type == USB_PHY_TYPE_USB3_AND_DP)
Mayank Rana4c40cd92017-03-21 14:40:55 -0700992 phy->phy.reset = msm_ssphy_qmp_dp_combo_reset;
993 else
994 phy->phy.reset = msm_ssphy_qmp_reset;
Mayank Rana3cb43a32016-08-02 12:10:20 -0700995
996 ret = usb_add_phy_dev(&phy->phy);
Mayank Rana3cb43a32016-08-02 12:10:20 -0700997
Mayank Rana3cb43a32016-08-02 12:10:20 -0700998err:
999 return ret;
1000}
1001
1002static int msm_ssphy_qmp_remove(struct platform_device *pdev)
1003{
1004 struct msm_ssphy_qmp *phy = platform_get_drvdata(pdev);
1005
1006 if (!phy)
1007 return 0;
1008
1009 usb_remove_phy(&phy->phy);
Mayank Rana561d0442017-03-16 18:19:14 -07001010 msm_ssphy_qmp_enable_clks(phy, false);
Mayank Rana3cb43a32016-08-02 12:10:20 -07001011 msm_ssusb_qmp_ldo_enable(phy, 0);
Mayank Rana3cb43a32016-08-02 12:10:20 -07001012 kfree(phy);
1013 return 0;
1014}
1015
1016static struct platform_driver msm_ssphy_qmp_driver = {
1017 .probe = msm_ssphy_qmp_probe,
1018 .remove = msm_ssphy_qmp_remove,
1019 .driver = {
1020 .name = "msm-usb-ssphy-qmp",
1021 .of_match_table = of_match_ptr(msm_usb_id_table),
1022 },
1023};
1024
1025module_platform_driver(msm_ssphy_qmp_driver);
1026
1027MODULE_DESCRIPTION("MSM USB SS QMP PHY driver");
1028MODULE_LICENSE("GPL v2");