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
;
58 case PHY_MODE_USB_DEVICE
:
59 val
|= ULPI_INT_SESS_VALID
;
64 ret
= ulpi_write(uphy
->ulpi
, ULPI_USB_INT_EN_RISE
, val
);
67 ret
= ulpi_write(uphy
->ulpi
, ULPI_USB_INT_EN_FALL
, val
);
70 case PHY_MODE_USB_OTG
:
71 case PHY_MODE_USB_DEVICE
:
72 addr
= ULPI_SET(ULPI_MISC_A
);
74 case PHY_MODE_USB_HOST
:
75 addr
= ULPI_CLR(ULPI_MISC_A
);
81 ret
= ulpi_write(uphy
->ulpi
, ULPI_SET(ULPI_PWR_CLK_MNG_REG
),
82 ULPI_PWR_OTG_COMP_DISABLE
);
85 ret
= ulpi_write(uphy
->ulpi
, addr
, ULPI_MISC_A_VBUSVLDEXTSEL
);
92 qcom_usb_hs_phy_vbus_notifier(struct notifier_block
*nb
, unsigned long event
,
95 struct qcom_usb_hs_phy
*uphy
;
98 uphy
= container_of(nb
, struct qcom_usb_hs_phy
, vbus_notify
);
101 addr
= ULPI_SET(ULPI_MISC_A
);
103 addr
= ULPI_CLR(ULPI_MISC_A
);
105 return ulpi_write(uphy
->ulpi
, addr
, ULPI_MISC_A_VBUSVLDEXT
);
108 static int qcom_usb_hs_phy_power_on(struct phy
*phy
)
110 struct qcom_usb_hs_phy
*uphy
= phy_get_drvdata(phy
);
111 struct ulpi
*ulpi
= uphy
->ulpi
;
112 const struct ulpi_seq
*seq
;
115 ret
= clk_prepare_enable(uphy
->ref_clk
);
119 ret
= clk_prepare_enable(uphy
->sleep_clk
);
123 ret
= regulator_set_load(uphy
->v1p8
, 50000);
127 ret
= regulator_enable(uphy
->v1p8
);
131 ret
= regulator_set_voltage_triplet(uphy
->v3p3
, 3050000, 3300000,
136 ret
= regulator_set_load(uphy
->v3p3
, 50000);
140 ret
= regulator_enable(uphy
->v3p3
);
144 for (seq
= uphy
->init_seq
; seq
->addr
; seq
++) {
145 ret
= ulpi_write(ulpi
, ULPI_EXT_VENDOR_SPECIFIC
+ seq
->addr
,
152 ret
= reset_control_reset(uphy
->reset
);
157 if (uphy
->vbus_edev
) {
158 state
= extcon_get_state(uphy
->vbus_edev
, EXTCON_USB
);
159 /* setup initial state */
160 qcom_usb_hs_phy_vbus_notifier(&uphy
->vbus_notify
, state
,
162 ret
= devm_extcon_register_notifier(&ulpi
->dev
, uphy
->vbus_edev
,
163 EXTCON_USB
, &uphy
->vbus_notify
);
170 regulator_disable(uphy
->v3p3
);
172 regulator_disable(uphy
->v1p8
);
174 clk_disable_unprepare(uphy
->sleep_clk
);
176 clk_disable_unprepare(uphy
->ref_clk
);
180 static int qcom_usb_hs_phy_power_off(struct phy
*phy
)
182 struct qcom_usb_hs_phy
*uphy
= phy_get_drvdata(phy
);
184 regulator_disable(uphy
->v3p3
);
185 regulator_disable(uphy
->v1p8
);
186 clk_disable_unprepare(uphy
->sleep_clk
);
187 clk_disable_unprepare(uphy
->ref_clk
);
192 static const struct phy_ops qcom_usb_hs_phy_ops
= {
193 .power_on
= qcom_usb_hs_phy_power_on
,
194 .power_off
= qcom_usb_hs_phy_power_off
,
195 .set_mode
= qcom_usb_hs_phy_set_mode
,
196 .owner
= THIS_MODULE
,
199 static int qcom_usb_hs_phy_probe(struct ulpi
*ulpi
)
201 struct qcom_usb_hs_phy
*uphy
;
202 struct phy_provider
*p
;
204 struct regulator
*reg
;
205 struct reset_control
*reset
;
209 uphy
= devm_kzalloc(&ulpi
->dev
, sizeof(*uphy
), GFP_KERNEL
);
212 ulpi_set_drvdata(ulpi
, uphy
);
215 size
= of_property_count_u8_elems(ulpi
->dev
.of_node
, "qcom,init-seq");
218 uphy
->init_seq
= devm_kmalloc_array(&ulpi
->dev
, (size
/ 2) + 1,
219 sizeof(*uphy
->init_seq
), GFP_KERNEL
);
222 ret
= of_property_read_u8_array(ulpi
->dev
.of_node
, "qcom,init-seq",
223 (u8
*)uphy
->init_seq
, size
);
227 uphy
->init_seq
[size
/ 2].addr
= uphy
->init_seq
[size
/ 2].val
= 0;
229 uphy
->ref_clk
= clk
= devm_clk_get(&ulpi
->dev
, "ref");
233 uphy
->sleep_clk
= clk
= devm_clk_get(&ulpi
->dev
, "sleep");
237 uphy
->v1p8
= reg
= devm_regulator_get(&ulpi
->dev
, "v1p8");
241 uphy
->v3p3
= reg
= devm_regulator_get(&ulpi
->dev
, "v3p3");
245 uphy
->reset
= reset
= devm_reset_control_get(&ulpi
->dev
, "por");
247 if (PTR_ERR(reset
) == -EPROBE_DEFER
)
248 return PTR_ERR(reset
);
252 uphy
->phy
= devm_phy_create(&ulpi
->dev
, ulpi
->dev
.of_node
,
253 &qcom_usb_hs_phy_ops
);
254 if (IS_ERR(uphy
->phy
))
255 return PTR_ERR(uphy
->phy
);
257 uphy
->vbus_edev
= extcon_get_edev_by_phandle(&ulpi
->dev
, 0);
258 if (IS_ERR(uphy
->vbus_edev
)) {
259 if (PTR_ERR(uphy
->vbus_edev
) != -ENODEV
)
260 return PTR_ERR(uphy
->vbus_edev
);
261 uphy
->vbus_edev
= NULL
;
264 uphy
->vbus_notify
.notifier_call
= qcom_usb_hs_phy_vbus_notifier
;
265 phy_set_drvdata(uphy
->phy
, uphy
);
267 p
= devm_of_phy_provider_register(&ulpi
->dev
, of_phy_simple_xlate
);
268 return PTR_ERR_OR_ZERO(p
);
271 static const struct of_device_id qcom_usb_hs_phy_match
[] = {
272 { .compatible
= "qcom,usb-hs-phy", },
275 MODULE_DEVICE_TABLE(of
, qcom_usb_hs_phy_match
);
277 static struct ulpi_driver qcom_usb_hs_phy_driver
= {
278 .probe
= qcom_usb_hs_phy_probe
,
280 .name
= "qcom_usb_hs_phy",
281 .of_match_table
= qcom_usb_hs_phy_match
,
284 module_ulpi_driver(qcom_usb_hs_phy_driver
);
286 MODULE_DESCRIPTION("Qualcomm USB HS phy");
287 MODULE_LICENSE("GPL v2");