blob: 6efc9b793333e117b108c80496d7ea9e6ac2cc09 [file] [log] [blame]
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +05301/*
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:
David Brownellcc835e32009-03-31 12:28:31 -070025 * This provides a "nop" transceiver for PHYs which are
26 * autonomous such as isp1504, isp1707, etc.
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053027 */
28
29#include <linux/module.h>
30#include <linux/platform_device.h>
31#include <linux/dma-mapping.h>
32#include <linux/usb/otg.h>
Felipe Balbi78c289f2012-07-19 13:32:15 +030033#include <linux/usb/nop-usb-xceiv.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090034#include <linux/slab.h>
Roger Quadros2319fb82013-03-12 13:24:21 +020035#include <linux/clk.h>
Roger Quadros58f735f2013-03-12 13:24:22 +020036#include <linux/regulator/consumer.h>
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053037
38struct nop_usb_xceiv {
Heikki Krogerus41adf102012-02-13 13:24:10 +020039 struct usb_phy phy;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053040 struct device *dev;
Roger Quadros2319fb82013-03-12 13:24:21 +020041 struct clk *clk;
Roger Quadros58f735f2013-03-12 13:24:22 +020042 struct regulator *vcc;
Roger Quadrosad63ebfc2013-03-12 13:24:23 +020043 struct regulator *reset;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053044};
45
David Brownellcc835e32009-03-31 12:28:31 -070046static struct platform_device *pd;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053047
48void usb_nop_xceiv_register(void)
49{
David Brownellcc835e32009-03-31 12:28:31 -070050 if (pd)
51 return;
52 pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0);
53 if (!pd) {
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053054 printk(KERN_ERR "Unable to register usb nop transceiver\n");
55 return;
56 }
57}
David Brownellcc835e32009-03-31 12:28:31 -070058EXPORT_SYMBOL(usb_nop_xceiv_register);
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053059
60void usb_nop_xceiv_unregister(void)
61{
David Brownellcc835e32009-03-31 12:28:31 -070062 platform_device_unregister(pd);
Ajay Kumar Guptadc7520c2009-07-03 13:18:45 +053063 pd = NULL;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053064}
David Brownellcc835e32009-03-31 12:28:31 -070065EXPORT_SYMBOL(usb_nop_xceiv_unregister);
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053066
Heikki Krogerus86753812012-02-13 13:24:02 +020067static int nop_set_suspend(struct usb_phy *x, int suspend)
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +053068{
69 return 0;
70}
71
Roger Quadros2319fb82013-03-12 13:24:21 +020072static int nop_init(struct usb_phy *phy)
73{
74 struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev);
75
Roger Quadros58f735f2013-03-12 13:24:22 +020076 if (!IS_ERR(nop->vcc)) {
77 if (regulator_enable(nop->vcc))
78 dev_err(phy->dev, "Failed to enable power\n");
79 }
80
Roger Quadros2319fb82013-03-12 13:24:21 +020081 if (!IS_ERR(nop->clk))
82 clk_enable(nop->clk);
83
Roger Quadrosad63ebfc2013-03-12 13:24:23 +020084 if (!IS_ERR(nop->reset)) {
85 /* De-assert RESET */
86 if (regulator_enable(nop->reset))
87 dev_err(phy->dev, "Failed to de-assert reset\n");
88 }
89
Roger Quadros2319fb82013-03-12 13:24:21 +020090 return 0;
91}
92
93static void nop_shutdown(struct usb_phy *phy)
94{
95 struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev);
96
Roger Quadrosad63ebfc2013-03-12 13:24:23 +020097 if (!IS_ERR(nop->reset)) {
98 /* Assert RESET */
99 if (regulator_disable(nop->reset))
100 dev_err(phy->dev, "Failed to assert reset\n");
101 }
102
Roger Quadros2319fb82013-03-12 13:24:21 +0200103 if (!IS_ERR(nop->clk))
104 clk_disable(nop->clk);
Roger Quadros58f735f2013-03-12 13:24:22 +0200105
106 if (!IS_ERR(nop->vcc)) {
107 if (regulator_disable(nop->vcc))
108 dev_err(phy->dev, "Failed to disable power\n");
109 }
Roger Quadros2319fb82013-03-12 13:24:21 +0200110}
111
Heikki Krogerus41adf102012-02-13 13:24:10 +0200112static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530113{
Heikki Krogerus41adf102012-02-13 13:24:10 +0200114 if (!otg)
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530115 return -ENODEV;
116
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530117 if (!gadget) {
Heikki Krogerus41adf102012-02-13 13:24:10 +0200118 otg->gadget = NULL;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530119 return -ENODEV;
120 }
121
Heikki Krogerus41adf102012-02-13 13:24:10 +0200122 otg->gadget = gadget;
123 otg->phy->state = OTG_STATE_B_IDLE;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530124 return 0;
125}
126
Heikki Krogerus41adf102012-02-13 13:24:10 +0200127static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530128{
Heikki Krogerus41adf102012-02-13 13:24:10 +0200129 if (!otg)
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530130 return -ENODEV;
131
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530132 if (!host) {
Heikki Krogerus41adf102012-02-13 13:24:10 +0200133 otg->host = NULL;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530134 return -ENODEV;
135 }
136
Heikki Krogerus41adf102012-02-13 13:24:10 +0200137 otg->host = host;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530138 return 0;
139}
140
Bill Pemberton41ac7b32012-11-19 13:21:48 -0500141static int nop_usb_xceiv_probe(struct platform_device *pdev)
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530142{
Felipe Balbic84d3642012-07-19 13:38:06 +0300143 struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530144 struct nop_usb_xceiv *nop;
Felipe Balbic84d3642012-07-19 13:38:06 +0300145 enum usb_phy_type type = USB_PHY_TYPE_USB2;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530146 int err;
147
Roger Quadrose4d7dc62013-03-12 13:24:20 +0200148 nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL);
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530149 if (!nop)
150 return -ENOMEM;
151
Roger Quadrose4d7dc62013-03-12 13:24:20 +0200152 nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg),
153 GFP_KERNEL);
154 if (!nop->phy.otg)
Heikki Krogerus41adf102012-02-13 13:24:10 +0200155 return -ENOMEM;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530156
Felipe Balbic84d3642012-07-19 13:38:06 +0300157 if (pdata)
158 type = pdata->type;
159
Roger Quadros2319fb82013-03-12 13:24:21 +0200160 nop->clk = devm_clk_get(&pdev->dev, "main_clk");
161 if (IS_ERR(nop->clk)) {
162 dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n",
163 PTR_ERR(nop->clk));
164 }
165
166 if (!IS_ERR(nop->clk) && pdata && pdata->clk_rate) {
167 err = clk_set_rate(nop->clk, pdata->clk_rate);
168 if (err) {
169 dev_err(&pdev->dev, "Error setting clock rate\n");
170 return err;
171 }
172 }
173
174 if (!IS_ERR(nop->clk)) {
175 err = clk_prepare(nop->clk);
176 if (err) {
177 dev_err(&pdev->dev, "Error preparing clock\n");
178 return err;
179 }
180 }
181
Roger Quadros58f735f2013-03-12 13:24:22 +0200182 nop->vcc = devm_regulator_get(&pdev->dev, "vcc");
183 if (IS_ERR(nop->vcc)) {
184 dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n",
185 PTR_ERR(nop->vcc));
186 }
187
Roger Quadrosad63ebfc2013-03-12 13:24:23 +0200188 nop->reset = devm_regulator_get(&pdev->dev, "reset");
189 if (IS_ERR(nop->reset)) {
190 dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n",
191 PTR_ERR(nop->reset));
192 }
193
Heikki Krogerus41adf102012-02-13 13:24:10 +0200194 nop->dev = &pdev->dev;
195 nop->phy.dev = nop->dev;
196 nop->phy.label = "nop-xceiv";
197 nop->phy.set_suspend = nop_set_suspend;
Roger Quadros2319fb82013-03-12 13:24:21 +0200198 nop->phy.init = nop_init;
199 nop->phy.shutdown = nop_shutdown;
Heikki Krogerus41adf102012-02-13 13:24:10 +0200200 nop->phy.state = OTG_STATE_UNDEFINED;
201
202 nop->phy.otg->phy = &nop->phy;
203 nop->phy.otg->set_host = nop_set_host;
204 nop->phy.otg->set_peripheral = nop_set_peripheral;
205
Felipe Balbic84d3642012-07-19 13:38:06 +0300206 err = usb_add_phy(&nop->phy, type);
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530207 if (err) {
208 dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
209 err);
Roger Quadros2319fb82013-03-12 13:24:21 +0200210 goto err_add;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530211 }
212
213 platform_set_drvdata(pdev, nop);
214
Heikki Krogerus41adf102012-02-13 13:24:10 +0200215 ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier);
Ming Lei20831ad2011-01-04 20:21:32 +0800216
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530217 return 0;
Roger Quadros2319fb82013-03-12 13:24:21 +0200218
219err_add:
220 if (!IS_ERR(nop->clk))
221 clk_unprepare(nop->clk);
222 return err;
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530223}
224
Bill Pembertonfb4e98a2012-11-19 13:26:20 -0500225static int nop_usb_xceiv_remove(struct platform_device *pdev)
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530226{
227 struct nop_usb_xceiv *nop = platform_get_drvdata(pdev);
228
Roger Quadros2319fb82013-03-12 13:24:21 +0200229 if (!IS_ERR(nop->clk))
230 clk_unprepare(nop->clk);
231
Kishon Vijay Abraham I662dca52012-06-22 17:02:46 +0530232 usb_remove_phy(&nop->phy);
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530233
234 platform_set_drvdata(pdev, NULL);
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530235
236 return 0;
237}
238
239static struct platform_driver nop_usb_xceiv_driver = {
240 .probe = nop_usb_xceiv_probe,
Bill Pemberton76904172012-11-19 13:21:08 -0500241 .remove = nop_usb_xceiv_remove,
Ajay Kumar Guptaf6d92a02009-02-06 17:32:35 +0530242 .driver = {
243 .name = "nop_usb_xceiv",
244 .owner = THIS_MODULE,
245 },
246};
247
248static int __init nop_usb_xceiv_init(void)
249{
250 return platform_driver_register(&nop_usb_xceiv_driver);
251}
252subsys_initcall(nop_usb_xceiv_init);
253
254static void __exit nop_usb_xceiv_exit(void)
255{
256 platform_driver_unregister(&nop_usb_xceiv_driver);
257}
258module_exit(nop_usb_xceiv_exit);
259
260MODULE_ALIAS("platform:nop_usb_xceiv");
261MODULE_AUTHOR("Texas Instruments Inc");
262MODULE_DESCRIPTION("NOP USB Transceiver driver");
263MODULE_LICENSE("GPL");