1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * NOP USB transceiver for all USB transceiver which are either built-in |
4 | * into USB IP or which are mostly autonomous. |
5 | * |
6 | * Copyright (C) 2009 Texas Instruments Inc |
7 | * Author: Ajay Kumar Gupta <ajay.gupta@ti.com> |
8 | * |
9 | * Current status: |
10 | * This provides a "nop" transceiver for PHYs which are |
11 | * autonomous such as isp1504, isp1707, etc. |
12 | */ |
13 | |
14 | #include <linux/module.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/dma-mapping.h> |
17 | #include <linux/usb/gadget.h> |
18 | #include <linux/usb/otg.h> |
19 | #include <linux/usb/usb_phy_generic.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/clk.h> |
22 | #include <linux/regulator/consumer.h> |
23 | #include <linux/of.h> |
24 | #include <linux/gpio/consumer.h> |
25 | #include <linux/delay.h> |
26 | |
27 | #include "phy-generic.h" |
28 | |
29 | #define VBUS_IRQ_FLAGS \ |
30 | (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \ |
31 | IRQF_ONESHOT) |
32 | |
33 | struct platform_device *usb_phy_generic_register(void) |
34 | { |
35 | return platform_device_register_simple(name: "usb_phy_generic" , |
36 | PLATFORM_DEVID_AUTO, NULL, num: 0); |
37 | } |
38 | EXPORT_SYMBOL_GPL(usb_phy_generic_register); |
39 | |
40 | void usb_phy_generic_unregister(struct platform_device *pdev) |
41 | { |
42 | platform_device_unregister(pdev); |
43 | } |
44 | EXPORT_SYMBOL_GPL(usb_phy_generic_unregister); |
45 | |
46 | static int nop_set_suspend(struct usb_phy *x, int suspend) |
47 | { |
48 | struct usb_phy_generic *nop = dev_get_drvdata(dev: x->dev); |
49 | |
50 | if (!IS_ERR(ptr: nop->clk)) { |
51 | if (suspend) |
52 | clk_disable_unprepare(clk: nop->clk); |
53 | else |
54 | clk_prepare_enable(clk: nop->clk); |
55 | } |
56 | |
57 | return 0; |
58 | } |
59 | |
60 | static void nop_reset(struct usb_phy_generic *nop) |
61 | { |
62 | if (!nop->gpiod_reset) |
63 | return; |
64 | |
65 | gpiod_set_value_cansleep(desc: nop->gpiod_reset, value: 1); |
66 | usleep_range(min: 10000, max: 20000); |
67 | gpiod_set_value_cansleep(desc: nop->gpiod_reset, value: 0); |
68 | } |
69 | |
70 | /* interface to regulator framework */ |
71 | static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA) |
72 | { |
73 | struct regulator *vbus_draw = nop->vbus_draw; |
74 | int enabled; |
75 | int ret; |
76 | |
77 | if (!vbus_draw) |
78 | return; |
79 | |
80 | enabled = nop->vbus_draw_enabled; |
81 | if (mA) { |
82 | regulator_set_current_limit(regulator: vbus_draw, min_uA: 0, max_uA: 1000 * mA); |
83 | if (!enabled) { |
84 | ret = regulator_enable(regulator: vbus_draw); |
85 | if (ret < 0) |
86 | return; |
87 | nop->vbus_draw_enabled = 1; |
88 | } |
89 | } else { |
90 | if (enabled) { |
91 | ret = regulator_disable(regulator: vbus_draw); |
92 | if (ret < 0) |
93 | return; |
94 | nop->vbus_draw_enabled = 0; |
95 | } |
96 | } |
97 | nop->mA = mA; |
98 | } |
99 | |
100 | |
101 | static irqreturn_t nop_gpio_vbus_thread(int irq, void *data) |
102 | { |
103 | struct usb_phy_generic *nop = data; |
104 | struct usb_otg *otg = nop->phy.otg; |
105 | int vbus, status; |
106 | |
107 | vbus = gpiod_get_value(desc: nop->gpiod_vbus); |
108 | if ((vbus ^ nop->vbus) == 0) |
109 | return IRQ_HANDLED; |
110 | nop->vbus = vbus; |
111 | |
112 | if (vbus) { |
113 | status = USB_EVENT_VBUS; |
114 | otg->state = OTG_STATE_B_PERIPHERAL; |
115 | nop->phy.last_event = status; |
116 | |
117 | /* drawing a "unit load" is *always* OK, except for OTG */ |
118 | nop_set_vbus_draw(nop, mA: 100); |
119 | |
120 | atomic_notifier_call_chain(nh: &nop->phy.notifier, val: status, |
121 | v: otg->gadget); |
122 | } else { |
123 | nop_set_vbus_draw(nop, mA: 0); |
124 | |
125 | status = USB_EVENT_NONE; |
126 | otg->state = OTG_STATE_B_IDLE; |
127 | nop->phy.last_event = status; |
128 | |
129 | atomic_notifier_call_chain(nh: &nop->phy.notifier, val: status, |
130 | v: otg->gadget); |
131 | } |
132 | return IRQ_HANDLED; |
133 | } |
134 | |
135 | int usb_gen_phy_init(struct usb_phy *phy) |
136 | { |
137 | struct usb_phy_generic *nop = dev_get_drvdata(dev: phy->dev); |
138 | int ret; |
139 | |
140 | if (!IS_ERR(ptr: nop->vcc)) { |
141 | if (regulator_enable(regulator: nop->vcc)) |
142 | dev_err(phy->dev, "Failed to enable power\n" ); |
143 | } |
144 | |
145 | if (!IS_ERR(ptr: nop->clk)) { |
146 | ret = clk_prepare_enable(clk: nop->clk); |
147 | if (ret) |
148 | return ret; |
149 | } |
150 | |
151 | nop_reset(nop); |
152 | |
153 | return 0; |
154 | } |
155 | EXPORT_SYMBOL_GPL(usb_gen_phy_init); |
156 | |
157 | void usb_gen_phy_shutdown(struct usb_phy *phy) |
158 | { |
159 | struct usb_phy_generic *nop = dev_get_drvdata(dev: phy->dev); |
160 | |
161 | gpiod_set_value_cansleep(desc: nop->gpiod_reset, value: 1); |
162 | |
163 | if (!IS_ERR(ptr: nop->clk)) |
164 | clk_disable_unprepare(clk: nop->clk); |
165 | |
166 | if (!IS_ERR(ptr: nop->vcc)) { |
167 | if (regulator_disable(regulator: nop->vcc)) |
168 | dev_err(phy->dev, "Failed to disable power\n" ); |
169 | } |
170 | } |
171 | EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown); |
172 | |
173 | static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) |
174 | { |
175 | if (!otg) |
176 | return -ENODEV; |
177 | |
178 | if (!gadget) { |
179 | otg->gadget = NULL; |
180 | return -ENODEV; |
181 | } |
182 | |
183 | otg->gadget = gadget; |
184 | if (otg->state == OTG_STATE_B_PERIPHERAL) |
185 | atomic_notifier_call_chain(nh: &otg->usb_phy->notifier, |
186 | val: USB_EVENT_VBUS, v: otg->gadget); |
187 | else |
188 | otg->state = OTG_STATE_B_IDLE; |
189 | return 0; |
190 | } |
191 | |
192 | static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) |
193 | { |
194 | if (!otg) |
195 | return -ENODEV; |
196 | |
197 | if (!host) { |
198 | otg->host = NULL; |
199 | return -ENODEV; |
200 | } |
201 | |
202 | otg->host = host; |
203 | return 0; |
204 | } |
205 | |
206 | int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop) |
207 | { |
208 | enum usb_phy_type type = USB_PHY_TYPE_USB2; |
209 | int err = 0; |
210 | |
211 | u32 clk_rate = 0; |
212 | bool needs_clk = false; |
213 | |
214 | if (dev->of_node) { |
215 | struct device_node *node = dev->of_node; |
216 | |
217 | if (of_property_read_u32(np: node, propname: "clock-frequency" , out_value: &clk_rate)) |
218 | clk_rate = 0; |
219 | |
220 | needs_clk = of_property_read_bool(np: node, propname: "clocks" ); |
221 | } |
222 | nop->gpiod_reset = devm_gpiod_get_optional(dev, con_id: "reset" , |
223 | flags: GPIOD_ASIS); |
224 | err = PTR_ERR_OR_ZERO(ptr: nop->gpiod_reset); |
225 | if (!err) { |
226 | nop->gpiod_vbus = devm_gpiod_get_optional(dev, |
227 | con_id: "vbus-detect" , |
228 | flags: GPIOD_ASIS); |
229 | err = PTR_ERR_OR_ZERO(ptr: nop->gpiod_vbus); |
230 | } |
231 | |
232 | if (err) |
233 | return dev_err_probe(dev, err, |
234 | fmt: "Error requesting RESET or VBUS GPIO\n" ); |
235 | if (nop->gpiod_reset) |
236 | gpiod_direction_output(desc: nop->gpiod_reset, value: 1); |
237 | |
238 | nop->phy.otg = devm_kzalloc(dev, size: sizeof(*nop->phy.otg), |
239 | GFP_KERNEL); |
240 | if (!nop->phy.otg) |
241 | return -ENOMEM; |
242 | |
243 | nop->clk = devm_clk_get(dev, id: "main_clk" ); |
244 | if (IS_ERR(ptr: nop->clk)) { |
245 | dev_dbg(dev, "Can't get phy clock: %ld\n" , |
246 | PTR_ERR(nop->clk)); |
247 | if (needs_clk) |
248 | return PTR_ERR(ptr: nop->clk); |
249 | } |
250 | |
251 | if (!IS_ERR(ptr: nop->clk) && clk_rate) { |
252 | err = clk_set_rate(clk: nop->clk, rate: clk_rate); |
253 | if (err) { |
254 | dev_err(dev, "Error setting clock rate\n" ); |
255 | return err; |
256 | } |
257 | } |
258 | |
259 | nop->vcc = devm_regulator_get_optional(dev, id: "vcc" ); |
260 | if (IS_ERR(ptr: nop->vcc) && PTR_ERR(ptr: nop->vcc) != -ENODEV) |
261 | return dev_err_probe(dev, err: PTR_ERR(ptr: nop->vcc), |
262 | fmt: "could not get vcc regulator\n" ); |
263 | |
264 | nop->vbus_draw = devm_regulator_get_exclusive(dev, id: "vbus" ); |
265 | if (PTR_ERR(ptr: nop->vbus_draw) == -ENODEV) |
266 | nop->vbus_draw = NULL; |
267 | if (IS_ERR(ptr: nop->vbus_draw)) |
268 | return dev_err_probe(dev, err: PTR_ERR(ptr: nop->vbus_draw), |
269 | fmt: "could not get vbus regulator\n" ); |
270 | |
271 | nop->dev = dev; |
272 | nop->phy.dev = nop->dev; |
273 | nop->phy.label = "nop-xceiv" ; |
274 | nop->phy.set_suspend = nop_set_suspend; |
275 | nop->phy.type = type; |
276 | |
277 | nop->phy.otg->state = OTG_STATE_UNDEFINED; |
278 | nop->phy.otg->usb_phy = &nop->phy; |
279 | nop->phy.otg->set_host = nop_set_host; |
280 | nop->phy.otg->set_peripheral = nop_set_peripheral; |
281 | |
282 | return 0; |
283 | } |
284 | EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); |
285 | |
286 | static int usb_phy_generic_probe(struct platform_device *pdev) |
287 | { |
288 | struct device *dev = &pdev->dev; |
289 | struct device_node *dn = dev->of_node; |
290 | struct usb_phy_generic *nop; |
291 | int err; |
292 | |
293 | nop = devm_kzalloc(dev, size: sizeof(*nop), GFP_KERNEL); |
294 | if (!nop) |
295 | return -ENOMEM; |
296 | |
297 | err = usb_phy_gen_create_phy(dev, nop); |
298 | if (err) |
299 | return err; |
300 | if (nop->gpiod_vbus) { |
301 | err = devm_request_threaded_irq(dev: &pdev->dev, |
302 | irq: gpiod_to_irq(desc: nop->gpiod_vbus), |
303 | NULL, thread_fn: nop_gpio_vbus_thread, |
304 | VBUS_IRQ_FLAGS, devname: "vbus_detect" , |
305 | dev_id: nop); |
306 | if (err) { |
307 | dev_err(&pdev->dev, "can't request irq %i, err: %d\n" , |
308 | gpiod_to_irq(nop->gpiod_vbus), err); |
309 | return err; |
310 | } |
311 | nop->phy.otg->state = gpiod_get_value(desc: nop->gpiod_vbus) ? |
312 | OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE; |
313 | } |
314 | |
315 | nop->phy.init = usb_gen_phy_init; |
316 | nop->phy.shutdown = usb_gen_phy_shutdown; |
317 | |
318 | err = usb_add_phy_dev(&nop->phy); |
319 | if (err) { |
320 | dev_err(&pdev->dev, "can't register transceiver, err: %d\n" , |
321 | err); |
322 | return err; |
323 | } |
324 | |
325 | platform_set_drvdata(pdev, data: nop); |
326 | |
327 | device_set_wakeup_capable(dev: &pdev->dev, |
328 | capable: of_property_read_bool(np: dn, propname: "wakeup-source" )); |
329 | |
330 | return 0; |
331 | } |
332 | |
333 | static void usb_phy_generic_remove(struct platform_device *pdev) |
334 | { |
335 | struct usb_phy_generic *nop = platform_get_drvdata(pdev); |
336 | |
337 | usb_remove_phy(&nop->phy); |
338 | } |
339 | |
340 | static const struct of_device_id nop_xceiv_dt_ids[] = { |
341 | { .compatible = "usb-nop-xceiv" }, |
342 | { } |
343 | }; |
344 | |
345 | MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); |
346 | |
347 | static struct platform_driver usb_phy_generic_driver = { |
348 | .probe = usb_phy_generic_probe, |
349 | .remove_new = usb_phy_generic_remove, |
350 | .driver = { |
351 | .name = "usb_phy_generic" , |
352 | .of_match_table = nop_xceiv_dt_ids, |
353 | }, |
354 | }; |
355 | |
356 | static int __init usb_phy_generic_init(void) |
357 | { |
358 | return platform_driver_register(&usb_phy_generic_driver); |
359 | } |
360 | subsys_initcall(usb_phy_generic_init); |
361 | |
362 | static void __exit usb_phy_generic_exit(void) |
363 | { |
364 | platform_driver_unregister(&usb_phy_generic_driver); |
365 | } |
366 | module_exit(usb_phy_generic_exit); |
367 | |
368 | MODULE_ALIAS("platform:usb_phy_generic" ); |
369 | MODULE_AUTHOR("Texas Instruments Inc" ); |
370 | MODULE_DESCRIPTION("NOP USB Transceiver driver" ); |
371 | MODULE_LICENSE("GPL" ); |
372 | |