1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2016 Linaro Ltd
5 #include <linux/module.h>
6 #include <linux/ulpi/driver.h>
7 #include <linux/ulpi/regs.h>
9 #include <linux/regulator/consumer.h>
10 #include <linux/of_device.h>
11 #include <linux/phy/phy.h>
12 #include <linux/reset.h>
13 #include <linux/extcon.h>
14 #include <linux/notifier.h>
16 #define ULPI_PWR_CLK_MNG_REG 0x88
17 # define ULPI_PWR_OTG_COMP_DISABLE BIT(0)
19 #define ULPI_MISC_A 0x96
20 # define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1)
21 # define ULPI_MISC_A_VBUSVLDEXT BIT(0)
29 struct qcom_usb_hs_phy
{
33 struct clk
*sleep_clk
;
34 struct regulator
*v1p8
;
35 struct regulator
*v3p3
;
36 struct reset_control
*reset
;
37 struct ulpi_seq
*init_seq
;
38 struct extcon_dev
*vbus_edev
;
39 struct notifier_block vbus_notify
;
42 static int qcom_usb_hs_phy_set_mode(struct phy
*phy
,
43 enum phy_mode mode
, int submode
)
45 struct qcom_usb_hs_phy
*uphy
= phy_get_drvdata(phy
);
49 if (!uphy
->vbus_edev
) {
53 case PHY_MODE_USB_OTG
:
54 case PHY_MODE_USB_HOST
:
55 val
|= ULPI_INT_IDGRD
;
57 case PHY_MODE_USB_DEVICE
:
58 val
|= ULPI_INT_SESS_VALID
;
63 ret
= ulpi_write(uphy
->ulpi
, ULPI_USB_INT_EN_RISE
, val
);
66 ret
= ulpi_write(uphy
->ulpi
, ULPI_USB_INT_EN_FALL
, val
);
69 case PHY_MODE_USB_OTG
:
70 case PHY_MODE_USB_DEVICE
:
71 addr
= ULPI_SET(ULPI_MISC_A
);
73 case PHY_MODE_USB_HOST
:
74 addr
= ULPI_CLR(ULPI_MISC_A
);
80 ret
= ulpi_write(uphy
->ulpi
, ULPI_SET(ULPI_PWR_CLK_MNG_REG
),
81 ULPI_PWR_OTG_COMP_DISABLE
);
84 ret
= ulpi_write(uphy
->ulpi
, addr
, ULPI_MISC_A_VBUSVLDEXTSEL
);
91 qcom_usb_hs_phy_vbus_notifier(struct notifier_block
*nb
, unsigned long event
,
94 struct qcom_usb_hs_phy
*uphy
;
97 uphy
= container_of(nb
, struct qcom_usb_hs_phy
, vbus_notify
);
100 addr
= ULPI_SET(ULPI_MISC_A
);
102 addr
= ULPI_CLR(ULPI_MISC_A
);
104 return ulpi_write(uphy
->ulpi
, addr
, ULPI_MISC_A_VBUSVLDEXT
);
107 static int qcom_usb_hs_phy_power_on(struct phy
*phy
)
109 struct qcom_usb_hs_phy
*uphy
= phy_get_drvdata(phy
);
110 struct ulpi
*ulpi
= uphy
->ulpi
;
111 const struct ulpi_seq
*seq
;
114 ret
= clk_prepare_enable(uphy
->ref_clk
);
118 ret
= clk_prepare_enable(uphy
->sleep_clk
);
122 ret
= regulator_set_load(uphy
->v1p8
, 50000);
126 ret
= regulator_enable(uphy
->v1p8
);
130 ret
= regulator_set_voltage_triplet(uphy
->v3p3
, 3050000, 3300000,
135 ret
= regulator_set_load(uphy
->v3p3
, 50000);
139 ret
= regulator_enable(uphy
->v3p3
);
143 for (seq
= uphy
->init_seq
; seq
->addr
; seq
++) {
144 ret
= ulpi_write(ulpi
, ULPI_EXT_VENDOR_SPECIFIC
+ seq
->addr
,
151 ret
= reset_control_reset(uphy
->reset
);
156 if (uphy
->vbus_edev
) {
157 state
= extcon_get_state(uphy
->vbus_edev
, EXTCON_USB
);
158 /* setup initial state */
159 qcom_usb_hs_phy_vbus_notifier(&uphy
->vbus_notify
, state
,
161 ret
= extcon_register_notifier(uphy
->vbus_edev
, EXTCON_USB
,
169 regulator_disable(uphy
->v3p3
);
171 regulator_disable(uphy
->v1p8
);
173 clk_disable_unprepare(uphy
->sleep_clk
);
175 clk_disable_unprepare(uphy
->ref_clk
);
179 static int qcom_usb_hs_phy_power_off(struct phy
*phy
)
181 struct qcom_usb_hs_phy
*uphy
= phy_get_drvdata(phy
);
184 extcon_unregister_notifier(uphy
->vbus_edev
, EXTCON_USB
,
186 regulator_disable(uphy
->v3p3
);
187 regulator_disable(uphy
->v1p8
);
188 clk_disable_unprepare(uphy
->sleep_clk
);
189 clk_disable_unprepare(uphy
->ref_clk
);
194 static const struct phy_ops qcom_usb_hs_phy_ops
= {
195 .power_on
= qcom_usb_hs_phy_power_on
,
196 .power_off
= qcom_usb_hs_phy_power_off
,
197 .set_mode
= qcom_usb_hs_phy_set_mode
,
198 .owner
= THIS_MODULE
,
201 static int qcom_usb_hs_phy_probe(struct ulpi
*ulpi
)
203 struct qcom_usb_hs_phy
*uphy
;
204 struct phy_provider
*p
;
206 struct regulator
*reg
;
207 struct reset_control
*reset
;
211 uphy
= devm_kzalloc(&ulpi
->dev
, sizeof(*uphy
), GFP_KERNEL
);
214 ulpi_set_drvdata(ulpi
, uphy
);
217 size
= of_property_count_u8_elems(ulpi
->dev
.of_node
, "qcom,init-seq");
220 uphy
->init_seq
= devm_kmalloc_array(&ulpi
->dev
, (size
/ 2) + 1,
221 sizeof(*uphy
->init_seq
), GFP_KERNEL
);
224 ret
= of_property_read_u8_array(ulpi
->dev
.of_node
, "qcom,init-seq",
225 (u8
*)uphy
->init_seq
, size
);
229 uphy
->init_seq
[size
/ 2].addr
= uphy
->init_seq
[size
/ 2].val
= 0;
231 uphy
->ref_clk
= clk
= devm_clk_get(&ulpi
->dev
, "ref");
235 uphy
->sleep_clk
= clk
= devm_clk_get(&ulpi
->dev
, "sleep");
239 uphy
->v1p8
= reg
= devm_regulator_get(&ulpi
->dev
, "v1p8");
243 uphy
->v3p3
= reg
= devm_regulator_get(&ulpi
->dev
, "v3p3");
247 uphy
->reset
= reset
= devm_reset_control_get(&ulpi
->dev
, "por");
249 if (PTR_ERR(reset
) == -EPROBE_DEFER
)
250 return PTR_ERR(reset
);
254 uphy
->phy
= devm_phy_create(&ulpi
->dev
, ulpi
->dev
.of_node
,
255 &qcom_usb_hs_phy_ops
);
256 if (IS_ERR(uphy
->phy
))
257 return PTR_ERR(uphy
->phy
);
259 uphy
->vbus_edev
= extcon_get_edev_by_phandle(&ulpi
->dev
, 0);
260 if (IS_ERR(uphy
->vbus_edev
)) {
261 if (PTR_ERR(uphy
->vbus_edev
) != -ENODEV
)
262 return PTR_ERR(uphy
->vbus_edev
);
263 uphy
->vbus_edev
= NULL
;
266 uphy
->vbus_notify
.notifier_call
= qcom_usb_hs_phy_vbus_notifier
;
267 phy_set_drvdata(uphy
->phy
, uphy
);
269 p
= devm_of_phy_provider_register(&ulpi
->dev
, of_phy_simple_xlate
);
270 return PTR_ERR_OR_ZERO(p
);
273 static const struct of_device_id qcom_usb_hs_phy_match
[] = {
274 { .compatible
= "qcom,usb-hs-phy", },
277 MODULE_DEVICE_TABLE(of
, qcom_usb_hs_phy_match
);
279 static struct ulpi_driver qcom_usb_hs_phy_driver
= {
280 .probe
= qcom_usb_hs_phy_probe
,
282 .name
= "qcom_usb_hs_phy",
283 .of_match_table
= qcom_usb_hs_phy_match
,
286 module_ulpi_driver(qcom_usb_hs_phy_driver
);
288 MODULE_DESCRIPTION("Qualcomm USB HS phy");
289 MODULE_LICENSE("GPL v2");