2 * Copyright (C) 2016 Linaro Ltd
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 as
6 * published by the Free Software Foundation.
8 #include <linux/module.h>
9 #include <linux/ulpi/driver.h>
10 #include <linux/ulpi/regs.h>
11 #include <linux/clk.h>
12 #include <linux/regulator/consumer.h>
13 #include <linux/of_device.h>
14 #include <linux/phy/phy.h>
15 #include <linux/reset.h>
16 #include <linux/extcon.h>
17 #include <linux/notifier.h>
19 #define ULPI_PWR_CLK_MNG_REG 0x88
20 # define ULPI_PWR_OTG_COMP_DISABLE BIT(0)
22 #define ULPI_MISC_A 0x96
23 # define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1)
24 # define ULPI_MISC_A_VBUSVLDEXT BIT(0)
32 struct qcom_usb_hs_phy
{
36 struct clk
*sleep_clk
;
37 struct regulator
*v1p8
;
38 struct regulator
*v3p3
;
39 struct reset_control
*reset
;
40 struct ulpi_seq
*init_seq
;
41 struct extcon_dev
*vbus_edev
;
42 struct notifier_block vbus_notify
;
45 static int qcom_usb_hs_phy_set_mode(struct phy
*phy
, enum phy_mode mode
)
47 struct qcom_usb_hs_phy
*uphy
= phy_get_drvdata(phy
);
51 if (!uphy
->vbus_edev
) {
55 case PHY_MODE_USB_OTG
:
56 case PHY_MODE_USB_HOST
:
57 val
|= ULPI_INT_IDGRD
;
59 case PHY_MODE_USB_DEVICE
:
60 val
|= ULPI_INT_SESS_VALID
;
65 ret
= ulpi_write(uphy
->ulpi
, ULPI_USB_INT_EN_RISE
, val
);
68 ret
= ulpi_write(uphy
->ulpi
, ULPI_USB_INT_EN_FALL
, val
);
71 case PHY_MODE_USB_OTG
:
72 case PHY_MODE_USB_DEVICE
:
73 addr
= ULPI_SET(ULPI_MISC_A
);
75 case PHY_MODE_USB_HOST
:
76 addr
= ULPI_CLR(ULPI_MISC_A
);
82 ret
= ulpi_write(uphy
->ulpi
, ULPI_SET(ULPI_PWR_CLK_MNG_REG
),
83 ULPI_PWR_OTG_COMP_DISABLE
);
86 ret
= ulpi_write(uphy
->ulpi
, addr
, ULPI_MISC_A_VBUSVLDEXTSEL
);
93 qcom_usb_hs_phy_vbus_notifier(struct notifier_block
*nb
, unsigned long event
,
96 struct qcom_usb_hs_phy
*uphy
;
99 uphy
= container_of(nb
, struct qcom_usb_hs_phy
, vbus_notify
);
102 addr
= ULPI_SET(ULPI_MISC_A
);
104 addr
= ULPI_CLR(ULPI_MISC_A
);
106 return ulpi_write(uphy
->ulpi
, addr
, ULPI_MISC_A_VBUSVLDEXT
);
109 static int qcom_usb_hs_phy_power_on(struct phy
*phy
)
111 struct qcom_usb_hs_phy
*uphy
= phy_get_drvdata(phy
);
112 struct ulpi
*ulpi
= uphy
->ulpi
;
113 const struct ulpi_seq
*seq
;
116 ret
= clk_prepare_enable(uphy
->ref_clk
);
120 ret
= clk_prepare_enable(uphy
->sleep_clk
);
124 ret
= regulator_set_load(uphy
->v1p8
, 50000);
128 ret
= regulator_enable(uphy
->v1p8
);
132 ret
= regulator_set_voltage_triplet(uphy
->v3p3
, 3050000, 3300000,
137 ret
= regulator_set_load(uphy
->v3p3
, 50000);
141 ret
= regulator_enable(uphy
->v3p3
);
145 for (seq
= uphy
->init_seq
; seq
->addr
; seq
++) {
146 ret
= ulpi_write(ulpi
, ULPI_EXT_VENDOR_SPECIFIC
+ seq
->addr
,
153 ret
= reset_control_reset(uphy
->reset
);
158 if (uphy
->vbus_edev
) {
159 state
= extcon_get_state(uphy
->vbus_edev
, EXTCON_USB
);
160 /* setup initial state */
161 qcom_usb_hs_phy_vbus_notifier(&uphy
->vbus_notify
, state
,
163 ret
= extcon_register_notifier(uphy
->vbus_edev
, EXTCON_USB
,
171 regulator_disable(uphy
->v3p3
);
173 regulator_disable(uphy
->v1p8
);
175 clk_disable_unprepare(uphy
->sleep_clk
);
177 clk_disable_unprepare(uphy
->ref_clk
);
181 static int qcom_usb_hs_phy_power_off(struct phy
*phy
)
183 struct qcom_usb_hs_phy
*uphy
= phy_get_drvdata(phy
);
186 extcon_unregister_notifier(uphy
->vbus_edev
, EXTCON_USB
,
188 regulator_disable(uphy
->v3p3
);
189 regulator_disable(uphy
->v1p8
);
190 clk_disable_unprepare(uphy
->sleep_clk
);
191 clk_disable_unprepare(uphy
->ref_clk
);
196 static const struct phy_ops qcom_usb_hs_phy_ops
= {
197 .power_on
= qcom_usb_hs_phy_power_on
,
198 .power_off
= qcom_usb_hs_phy_power_off
,
199 .set_mode
= qcom_usb_hs_phy_set_mode
,
200 .owner
= THIS_MODULE
,
203 static int qcom_usb_hs_phy_probe(struct ulpi
*ulpi
)
205 struct qcom_usb_hs_phy
*uphy
;
206 struct phy_provider
*p
;
208 struct regulator
*reg
;
209 struct reset_control
*reset
;
213 uphy
= devm_kzalloc(&ulpi
->dev
, sizeof(*uphy
), GFP_KERNEL
);
216 ulpi_set_drvdata(ulpi
, uphy
);
219 size
= of_property_count_u8_elems(ulpi
->dev
.of_node
, "qcom,init-seq");
222 uphy
->init_seq
= devm_kmalloc_array(&ulpi
->dev
, (size
/ 2) + 1,
223 sizeof(*uphy
->init_seq
), GFP_KERNEL
);
226 ret
= of_property_read_u8_array(ulpi
->dev
.of_node
, "qcom,init-seq",
227 (u8
*)uphy
->init_seq
, size
);
231 uphy
->init_seq
[size
/ 2].addr
= uphy
->init_seq
[size
/ 2].val
= 0;
233 uphy
->ref_clk
= clk
= devm_clk_get(&ulpi
->dev
, "ref");
237 uphy
->sleep_clk
= clk
= devm_clk_get(&ulpi
->dev
, "sleep");
241 uphy
->v1p8
= reg
= devm_regulator_get(&ulpi
->dev
, "v1p8");
245 uphy
->v3p3
= reg
= devm_regulator_get(&ulpi
->dev
, "v3p3");
249 uphy
->reset
= reset
= devm_reset_control_get(&ulpi
->dev
, "por");
251 if (PTR_ERR(reset
) == -EPROBE_DEFER
)
252 return PTR_ERR(reset
);
256 uphy
->phy
= devm_phy_create(&ulpi
->dev
, ulpi
->dev
.of_node
,
257 &qcom_usb_hs_phy_ops
);
258 if (IS_ERR(uphy
->phy
))
259 return PTR_ERR(uphy
->phy
);
261 uphy
->vbus_edev
= extcon_get_edev_by_phandle(&ulpi
->dev
, 0);
262 if (IS_ERR(uphy
->vbus_edev
)) {
263 if (PTR_ERR(uphy
->vbus_edev
) != -ENODEV
)
264 return PTR_ERR(uphy
->vbus_edev
);
265 uphy
->vbus_edev
= NULL
;
268 uphy
->vbus_notify
.notifier_call
= qcom_usb_hs_phy_vbus_notifier
;
269 phy_set_drvdata(uphy
->phy
, uphy
);
271 p
= devm_of_phy_provider_register(&ulpi
->dev
, of_phy_simple_xlate
);
272 return PTR_ERR_OR_ZERO(p
);
275 static const struct of_device_id qcom_usb_hs_phy_match
[] = {
276 { .compatible
= "qcom,usb-hs-phy", },
279 MODULE_DEVICE_TABLE(of
, qcom_usb_hs_phy_match
);
281 static struct ulpi_driver qcom_usb_hs_phy_driver
= {
282 .probe
= qcom_usb_hs_phy_probe
,
284 .name
= "qcom_usb_hs_phy",
285 .of_match_table
= qcom_usb_hs_phy_match
,
288 module_ulpi_driver(qcom_usb_hs_phy_driver
);
290 MODULE_DESCRIPTION("Qualcomm USB HS phy");
291 MODULE_LICENSE("GPL v2");