1 /**
2  * Copyright (C) 2016 Linaro Ltd
3  *
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.
7  */
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>
18 
19 #define ULPI_PWR_CLK_MNG_REG		0x88
20 # define ULPI_PWR_OTG_COMP_DISABLE	BIT(0)
21 
22 #define ULPI_MISC_A			0x96
23 # define ULPI_MISC_A_VBUSVLDEXTSEL	BIT(1)
24 # define ULPI_MISC_A_VBUSVLDEXT		BIT(0)
25 
26 
27 struct ulpi_seq {
28 	u8 addr;
29 	u8 val;
30 };
31 
32 struct qcom_usb_hs_phy {
33 	struct ulpi *ulpi;
34 	struct phy *phy;
35 	struct clk *ref_clk;
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;
43 };
44 
45 static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode)
46 {
47 	struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
48 	u8 addr;
49 	int ret;
50 
51 	if (!uphy->vbus_edev) {
52 		u8 val = 0;
53 
54 		switch (mode) {
55 		case PHY_MODE_USB_OTG:
56 		case PHY_MODE_USB_HOST:
57 			val |= ULPI_INT_IDGRD;
58 			/* fall through */
59 		case PHY_MODE_USB_DEVICE:
60 			val |= ULPI_INT_SESS_VALID;
61 		default:
62 			break;
63 		}
64 
65 		ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val);
66 		if (ret)
67 			return ret;
68 		ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val);
69 	} else {
70 		switch (mode) {
71 		case PHY_MODE_USB_OTG:
72 		case PHY_MODE_USB_DEVICE:
73 			addr = ULPI_SET(ULPI_MISC_A);
74 			break;
75 		case PHY_MODE_USB_HOST:
76 			addr = ULPI_CLR(ULPI_MISC_A);
77 			break;
78 		default:
79 			return -EINVAL;
80 		}
81 
82 		ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG),
83 				 ULPI_PWR_OTG_COMP_DISABLE);
84 		if (ret)
85 			return ret;
86 		ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL);
87 	}
88 
89 	return ret;
90 }
91 
92 static int
93 qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event,
94 			      void *ptr)
95 {
96 	struct qcom_usb_hs_phy *uphy;
97 	u8 addr;
98 
99 	uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify);
100 
101 	if (event)
102 		addr = ULPI_SET(ULPI_MISC_A);
103 	else
104 		addr = ULPI_CLR(ULPI_MISC_A);
105 
106 	return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT);
107 }
108 
109 static int qcom_usb_hs_phy_power_on(struct phy *phy)
110 {
111 	struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
112 	struct ulpi *ulpi = uphy->ulpi;
113 	const struct ulpi_seq *seq;
114 	int ret, state;
115 
116 	ret = clk_prepare_enable(uphy->ref_clk);
117 	if (ret)
118 		return ret;
119 
120 	ret = clk_prepare_enable(uphy->sleep_clk);
121 	if (ret)
122 		goto err_sleep;
123 
124 	ret = regulator_set_load(uphy->v1p8, 50000);
125 	if (ret < 0)
126 		goto err_1p8;
127 
128 	ret = regulator_enable(uphy->v1p8);
129 	if (ret)
130 		goto err_1p8;
131 
132 	ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000,
133 					    3300000);
134 	if (ret)
135 		goto err_3p3;
136 
137 	ret = regulator_set_load(uphy->v3p3, 50000);
138 	if (ret < 0)
139 		goto err_3p3;
140 
141 	ret = regulator_enable(uphy->v3p3);
142 	if (ret)
143 		goto err_3p3;
144 
145 	for (seq = uphy->init_seq; seq->addr; seq++) {
146 		ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr,
147 				 seq->val);
148 		if (ret)
149 			goto err_ulpi;
150 	}
151 
152 	if (uphy->reset) {
153 		ret = reset_control_reset(uphy->reset);
154 		if (ret)
155 			goto err_ulpi;
156 	}
157 
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,
162 					      uphy->vbus_edev);
163 		ret = devm_extcon_register_notifier(&ulpi->dev, uphy->vbus_edev,
164 				EXTCON_USB, &uphy->vbus_notify);
165 		if (ret)
166 			goto err_ulpi;
167 	}
168 
169 	return 0;
170 err_ulpi:
171 	regulator_disable(uphy->v3p3);
172 err_3p3:
173 	regulator_disable(uphy->v1p8);
174 err_1p8:
175 	clk_disable_unprepare(uphy->sleep_clk);
176 err_sleep:
177 	clk_disable_unprepare(uphy->ref_clk);
178 	return ret;
179 }
180 
181 static int qcom_usb_hs_phy_power_off(struct phy *phy)
182 {
183 	struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
184 
185 	regulator_disable(uphy->v3p3);
186 	regulator_disable(uphy->v1p8);
187 	clk_disable_unprepare(uphy->sleep_clk);
188 	clk_disable_unprepare(uphy->ref_clk);
189 
190 	return 0;
191 }
192 
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,
198 };
199 
200 static int qcom_usb_hs_phy_probe(struct ulpi *ulpi)
201 {
202 	struct qcom_usb_hs_phy *uphy;
203 	struct phy_provider *p;
204 	struct clk *clk;
205 	struct regulator *reg;
206 	struct reset_control *reset;
207 	int size;
208 	int ret;
209 
210 	uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);
211 	if (!uphy)
212 		return -ENOMEM;
213 	ulpi_set_drvdata(ulpi, uphy);
214 	uphy->ulpi = ulpi;
215 
216 	size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq");
217 	if (size < 0)
218 		size = 0;
219 	uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1,
220 					   sizeof(*uphy->init_seq), GFP_KERNEL);
221 	if (!uphy->init_seq)
222 		return -ENOMEM;
223 	ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq",
224 					(u8 *)uphy->init_seq, size);
225 	if (ret && size)
226 		return ret;
227 	/* NUL terminate */
228 	uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0;
229 
230 	uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref");
231 	if (IS_ERR(clk))
232 		return PTR_ERR(clk);
233 
234 	uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep");
235 	if (IS_ERR(clk))
236 		return PTR_ERR(clk);
237 
238 	uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8");
239 	if (IS_ERR(reg))
240 		return PTR_ERR(reg);
241 
242 	uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3");
243 	if (IS_ERR(reg))
244 		return PTR_ERR(reg);
245 
246 	uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por");
247 	if (IS_ERR(reset)) {
248 		if (PTR_ERR(reset) == -EPROBE_DEFER)
249 			return PTR_ERR(reset);
250 		uphy->reset = NULL;
251 	}
252 
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);
257 
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;
263 	}
264 
265 	uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier;
266 	phy_set_drvdata(uphy->phy, uphy);
267 
268 	p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);
269 	return PTR_ERR_OR_ZERO(p);
270 }
271 
272 static const struct of_device_id qcom_usb_hs_phy_match[] = {
273 	{ .compatible = "qcom,usb-hs-phy", },
274 	{ }
275 };
276 MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match);
277 
278 static struct ulpi_driver qcom_usb_hs_phy_driver = {
279 	.probe = qcom_usb_hs_phy_probe,
280 	.driver = {
281 		.name = "qcom_usb_hs_phy",
282 		.of_match_table = qcom_usb_hs_phy_match,
283 	},
284 };
285 module_ulpi_driver(qcom_usb_hs_phy_driver);
286 
287 MODULE_DESCRIPTION("Qualcomm USB HS phy");
288 MODULE_LICENSE("GPL v2");
289