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
= devm_extcon_register_notifier(&ulpi
->dev
, uphy
->vbus_edev
,
164 EXTCON_USB
, &uphy
->vbus_notify
);
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
);
185 regulator_disable(uphy
->v3p3
);
186 regulator_disable(uphy
->v1p8
);
187 clk_disable_unprepare(uphy
->sleep_clk
);
188 clk_disable_unprepare(uphy
->ref_clk
);
193 static const struct phy_ops qcom_usb_hs_phy_ops
= {
194 .power_on
= qcom_usb_hs_phy_power_on
,
195 .power_off
= qcom_usb_hs_phy_power_off
,
196 .set_mode
= qcom_usb_hs_phy_set_mode
,
197 .owner
= THIS_MODULE
,
200 static int qcom_usb_hs_phy_probe(struct ulpi
*ulpi
)
202 struct qcom_usb_hs_phy
*uphy
;
203 struct phy_provider
*p
;
205 struct regulator
*reg
;
206 struct reset_control
*reset
;
210 uphy
= devm_kzalloc(&ulpi
->dev
, sizeof(*uphy
), GFP_KERNEL
);
213 ulpi_set_drvdata(ulpi
, uphy
);
216 size
= of_property_count_u8_elems(ulpi
->dev
.of_node
, "qcom,init-seq");
219 uphy
->init_seq
= devm_kmalloc_array(&ulpi
->dev
, (size
/ 2) + 1,
220 sizeof(*uphy
->init_seq
), GFP_KERNEL
);
223 ret
= of_property_read_u8_array(ulpi
->dev
.of_node
, "qcom,init-seq",
224 (u8
*)uphy
->init_seq
, size
);
228 uphy
->init_seq
[size
/ 2].addr
= uphy
->init_seq
[size
/ 2].val
= 0;
230 uphy
->ref_clk
= clk
= devm_clk_get(&ulpi
->dev
, "ref");
234 uphy
->sleep_clk
= clk
= devm_clk_get(&ulpi
->dev
, "sleep");
238 uphy
->v1p8
= reg
= devm_regulator_get(&ulpi
->dev
, "v1p8");
242 uphy
->v3p3
= reg
= devm_regulator_get(&ulpi
->dev
, "v3p3");
246 uphy
->reset
= reset
= devm_reset_control_get(&ulpi
->dev
, "por");
248 if (PTR_ERR(reset
) == -EPROBE_DEFER
)
249 return PTR_ERR(reset
);
253 uphy
->phy
= devm_phy_create(&ulpi
->dev
, ulpi
->dev
.of_node
,
254 &qcom_usb_hs_phy_ops
);
255 if (IS_ERR(uphy
->phy
))
256 return PTR_ERR(uphy
->phy
);
258 uphy
->vbus_edev
= extcon_get_edev_by_phandle(&ulpi
->dev
, 0);
259 if (IS_ERR(uphy
->vbus_edev
)) {
260 if (PTR_ERR(uphy
->vbus_edev
) != -ENODEV
)
261 return PTR_ERR(uphy
->vbus_edev
);
262 uphy
->vbus_edev
= NULL
;
265 uphy
->vbus_notify
.notifier_call
= qcom_usb_hs_phy_vbus_notifier
;
266 phy_set_drvdata(uphy
->phy
, uphy
);
268 p
= devm_of_phy_provider_register(&ulpi
->dev
, of_phy_simple_xlate
);
269 return PTR_ERR_OR_ZERO(p
);
272 static const struct of_device_id qcom_usb_hs_phy_match
[] = {
273 { .compatible
= "qcom,usb-hs-phy", },
276 MODULE_DEVICE_TABLE(of
, qcom_usb_hs_phy_match
);
278 static struct ulpi_driver qcom_usb_hs_phy_driver
= {
279 .probe
= qcom_usb_hs_phy_probe
,
281 .name
= "qcom_usb_hs_phy",
282 .of_match_table
= qcom_usb_hs_phy_match
,
285 module_ulpi_driver(qcom_usb_hs_phy_driver
);
287 MODULE_DESCRIPTION("Qualcomm USB HS phy");
288 MODULE_LICENSE("GPL v2");