diff options
author | Kevin Hilman <khilman@linaro.org> | 2013-08-19 13:18:28 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@linaro.org> | 2013-08-19 13:19:18 -0400 |
commit | 47aad66c2659b8985512b66f87bb5fe7bdc88dff (patch) | |
tree | 8e788d9ff4bacd907ba3df176eb67e87ff5c3dde /drivers/usb/phy/phy-generic.c | |
parent | fea67d3dfd13bcca4daa62cbafe6f8eb99523217 (diff) | |
parent | 3fa4d7344be0afebd80382ffeea6b1787cccf971 (diff) |
Merge tag 'omap-for-v3.12/usb-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/cleanup
From Tony Lindgren:
USB nop phy rename via Felipe Balbi <balbi@ti.com>:
Here's a pull request of one patch to avoid conflicts during the merge
window.
* tag 'omap-for-v3.12/usb-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
usb: phy: rename nop_usb_xceiv => usb_phy_gen_xceiv
Signed-off-by: Kevin Hilman <khilman@linaro.org>
Diffstat (limited to 'drivers/usb/phy/phy-generic.c')
-rw-r--r-- | drivers/usb/phy/phy-generic.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c new file mode 100644 index 000000000000..f379b7ded037 --- /dev/null +++ b/drivers/usb/phy/phy-generic.c | |||
@@ -0,0 +1,292 @@ | |||
1 | /* | ||
2 | * drivers/usb/otg/nop-usb-xceiv.c | ||
3 | * | ||
4 | * NOP USB transceiver for all USB transceiver which are either built-in | ||
5 | * into USB IP or which are mostly autonomous. | ||
6 | * | ||
7 | * Copyright (C) 2009 Texas Instruments Inc | ||
8 | * Author: Ajay Kumar Gupta <ajay.gupta@ti.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | * | ||
24 | * Current status: | ||
25 | * This provides a "nop" transceiver for PHYs which are | ||
26 | * autonomous such as isp1504, isp1707, etc. | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/dma-mapping.h> | ||
32 | #include <linux/usb/otg.h> | ||
33 | #include <linux/usb/usb_phy_gen_xceiv.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/clk.h> | ||
36 | #include <linux/regulator/consumer.h> | ||
37 | #include <linux/of.h> | ||
38 | |||
39 | struct usb_phy_gen_xceiv { | ||
40 | struct usb_phy phy; | ||
41 | struct device *dev; | ||
42 | struct clk *clk; | ||
43 | struct regulator *vcc; | ||
44 | struct regulator *reset; | ||
45 | }; | ||
46 | |||
47 | static struct platform_device *pd; | ||
48 | |||
49 | void usb_nop_xceiv_register(void) | ||
50 | { | ||
51 | if (pd) | ||
52 | return; | ||
53 | pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0); | ||
54 | if (!pd) { | ||
55 | pr_err("Unable to register generic usb transceiver\n"); | ||
56 | return; | ||
57 | } | ||
58 | } | ||
59 | EXPORT_SYMBOL(usb_nop_xceiv_register); | ||
60 | |||
61 | void usb_nop_xceiv_unregister(void) | ||
62 | { | ||
63 | platform_device_unregister(pd); | ||
64 | pd = NULL; | ||
65 | } | ||
66 | EXPORT_SYMBOL(usb_nop_xceiv_unregister); | ||
67 | |||
68 | static int nop_set_suspend(struct usb_phy *x, int suspend) | ||
69 | { | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int nop_init(struct usb_phy *phy) | ||
74 | { | ||
75 | struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); | ||
76 | |||
77 | if (!IS_ERR(nop->vcc)) { | ||
78 | if (regulator_enable(nop->vcc)) | ||
79 | dev_err(phy->dev, "Failed to enable power\n"); | ||
80 | } | ||
81 | |||
82 | if (!IS_ERR(nop->clk)) | ||
83 | clk_enable(nop->clk); | ||
84 | |||
85 | if (!IS_ERR(nop->reset)) { | ||
86 | /* De-assert RESET */ | ||
87 | if (regulator_enable(nop->reset)) | ||
88 | dev_err(phy->dev, "Failed to de-assert reset\n"); | ||
89 | } | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static void nop_shutdown(struct usb_phy *phy) | ||
95 | { | ||
96 | struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); | ||
97 | |||
98 | if (!IS_ERR(nop->reset)) { | ||
99 | /* Assert RESET */ | ||
100 | if (regulator_disable(nop->reset)) | ||
101 | dev_err(phy->dev, "Failed to assert reset\n"); | ||
102 | } | ||
103 | |||
104 | if (!IS_ERR(nop->clk)) | ||
105 | clk_disable(nop->clk); | ||
106 | |||
107 | if (!IS_ERR(nop->vcc)) { | ||
108 | if (regulator_disable(nop->vcc)) | ||
109 | dev_err(phy->dev, "Failed to disable power\n"); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) | ||
114 | { | ||
115 | if (!otg) | ||
116 | return -ENODEV; | ||
117 | |||
118 | if (!gadget) { | ||
119 | otg->gadget = NULL; | ||
120 | return -ENODEV; | ||
121 | } | ||
122 | |||
123 | otg->gadget = gadget; | ||
124 | otg->phy->state = OTG_STATE_B_IDLE; | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) | ||
129 | { | ||
130 | if (!otg) | ||
131 | return -ENODEV; | ||
132 | |||
133 | if (!host) { | ||
134 | otg->host = NULL; | ||
135 | return -ENODEV; | ||
136 | } | ||
137 | |||
138 | otg->host = host; | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) | ||
143 | { | ||
144 | struct device *dev = &pdev->dev; | ||
145 | struct usb_phy_gen_xceiv_platform_data *pdata = pdev->dev.platform_data; | ||
146 | struct usb_phy_gen_xceiv *nop; | ||
147 | enum usb_phy_type type = USB_PHY_TYPE_USB2; | ||
148 | int err; | ||
149 | u32 clk_rate = 0; | ||
150 | bool needs_vcc = false; | ||
151 | bool needs_reset = false; | ||
152 | |||
153 | nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); | ||
154 | if (!nop) | ||
155 | return -ENOMEM; | ||
156 | |||
157 | nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), | ||
158 | GFP_KERNEL); | ||
159 | if (!nop->phy.otg) | ||
160 | return -ENOMEM; | ||
161 | |||
162 | if (dev->of_node) { | ||
163 | struct device_node *node = dev->of_node; | ||
164 | |||
165 | if (of_property_read_u32(node, "clock-frequency", &clk_rate)) | ||
166 | clk_rate = 0; | ||
167 | |||
168 | needs_vcc = of_property_read_bool(node, "vcc-supply"); | ||
169 | needs_reset = of_property_read_bool(node, "reset-supply"); | ||
170 | |||
171 | } else if (pdata) { | ||
172 | type = pdata->type; | ||
173 | clk_rate = pdata->clk_rate; | ||
174 | needs_vcc = pdata->needs_vcc; | ||
175 | needs_reset = pdata->needs_reset; | ||
176 | } | ||
177 | |||
178 | nop->clk = devm_clk_get(&pdev->dev, "main_clk"); | ||
179 | if (IS_ERR(nop->clk)) { | ||
180 | dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", | ||
181 | PTR_ERR(nop->clk)); | ||
182 | } | ||
183 | |||
184 | if (!IS_ERR(nop->clk) && clk_rate) { | ||
185 | err = clk_set_rate(nop->clk, clk_rate); | ||
186 | if (err) { | ||
187 | dev_err(&pdev->dev, "Error setting clock rate\n"); | ||
188 | return err; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | if (!IS_ERR(nop->clk)) { | ||
193 | err = clk_prepare(nop->clk); | ||
194 | if (err) { | ||
195 | dev_err(&pdev->dev, "Error preparing clock\n"); | ||
196 | return err; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); | ||
201 | if (IS_ERR(nop->vcc)) { | ||
202 | dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", | ||
203 | PTR_ERR(nop->vcc)); | ||
204 | if (needs_vcc) | ||
205 | return -EPROBE_DEFER; | ||
206 | } | ||
207 | |||
208 | nop->reset = devm_regulator_get(&pdev->dev, "reset"); | ||
209 | if (IS_ERR(nop->reset)) { | ||
210 | dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", | ||
211 | PTR_ERR(nop->reset)); | ||
212 | if (needs_reset) | ||
213 | return -EPROBE_DEFER; | ||
214 | } | ||
215 | |||
216 | nop->dev = &pdev->dev; | ||
217 | nop->phy.dev = nop->dev; | ||
218 | nop->phy.label = "nop-xceiv"; | ||
219 | nop->phy.set_suspend = nop_set_suspend; | ||
220 | nop->phy.init = nop_init; | ||
221 | nop->phy.shutdown = nop_shutdown; | ||
222 | nop->phy.state = OTG_STATE_UNDEFINED; | ||
223 | nop->phy.type = type; | ||
224 | |||
225 | nop->phy.otg->phy = &nop->phy; | ||
226 | nop->phy.otg->set_host = nop_set_host; | ||
227 | nop->phy.otg->set_peripheral = nop_set_peripheral; | ||
228 | |||
229 | err = usb_add_phy_dev(&nop->phy); | ||
230 | if (err) { | ||
231 | dev_err(&pdev->dev, "can't register transceiver, err: %d\n", | ||
232 | err); | ||
233 | goto err_add; | ||
234 | } | ||
235 | |||
236 | platform_set_drvdata(pdev, nop); | ||
237 | |||
238 | ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); | ||
239 | |||
240 | return 0; | ||
241 | |||
242 | err_add: | ||
243 | if (!IS_ERR(nop->clk)) | ||
244 | clk_unprepare(nop->clk); | ||
245 | return err; | ||
246 | } | ||
247 | |||
248 | static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) | ||
249 | { | ||
250 | struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); | ||
251 | |||
252 | if (!IS_ERR(nop->clk)) | ||
253 | clk_unprepare(nop->clk); | ||
254 | |||
255 | usb_remove_phy(&nop->phy); | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static const struct of_device_id nop_xceiv_dt_ids[] = { | ||
261 | { .compatible = "usb-nop-xceiv" }, | ||
262 | { } | ||
263 | }; | ||
264 | |||
265 | MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); | ||
266 | |||
267 | static struct platform_driver usb_phy_gen_xceiv_driver = { | ||
268 | .probe = usb_phy_gen_xceiv_probe, | ||
269 | .remove = usb_phy_gen_xceiv_remove, | ||
270 | .driver = { | ||
271 | .name = "usb_phy_gen_xceiv", | ||
272 | .owner = THIS_MODULE, | ||
273 | .of_match_table = nop_xceiv_dt_ids, | ||
274 | }, | ||
275 | }; | ||
276 | |||
277 | static int __init usb_phy_gen_xceiv_init(void) | ||
278 | { | ||
279 | return platform_driver_register(&usb_phy_gen_xceiv_driver); | ||
280 | } | ||
281 | subsys_initcall(usb_phy_gen_xceiv_init); | ||
282 | |||
283 | static void __exit usb_phy_gen_xceiv_exit(void) | ||
284 | { | ||
285 | platform_driver_unregister(&usb_phy_gen_xceiv_driver); | ||
286 | } | ||
287 | module_exit(usb_phy_gen_xceiv_exit); | ||
288 | |||
289 | MODULE_ALIAS("platform:usb_phy_gen_xceiv"); | ||
290 | MODULE_AUTHOR("Texas Instruments Inc"); | ||
291 | MODULE_DESCRIPTION("NOP USB Transceiver driver"); | ||
292 | MODULE_LICENSE("GPL"); | ||