phy-generic.c 7.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * drivers/usb/otg/nop-usb-xceiv.c
 *
 * NOP USB transceiver for all USB transceiver which are either built-in
 * into USB IP or which are mostly autonomous.
 *
 * Copyright (C) 2009 Texas Instruments Inc
 * Author: Ajay Kumar Gupta <ajay.gupta@ti.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Current status:
25 26
 *	This provides a "nop" transceiver for PHYs which are
 *	autonomous such as isp1504, isp1707, etc.
27 28 29 30 31 32
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/otg.h>
33
#include <linux/usb/usb_phy_gen_xceiv.h>
34
#include <linux/slab.h>
R
Roger Quadros 已提交
35
#include <linux/clk.h>
36
#include <linux/regulator/consumer.h>
37
#include <linux/of.h>
38

39
#include "phy-generic.h"
40

41
static struct platform_device *pd;
42 43 44

void usb_nop_xceiv_register(void)
{
45 46
	if (pd)
		return;
47
	pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0);
48
	if (!pd) {
49
		pr_err("Unable to register generic usb transceiver\n");
50 51 52
		return;
	}
}
53
EXPORT_SYMBOL(usb_nop_xceiv_register);
54 55 56

void usb_nop_xceiv_unregister(void)
{
57
	platform_device_unregister(pd);
58
	pd = NULL;
59
}
60
EXPORT_SYMBOL(usb_nop_xceiv_unregister);
61

62
static int nop_set_suspend(struct usb_phy *x, int suspend)
63 64 65 66
{
	return 0;
}

67
int usb_gen_phy_init(struct usb_phy *phy)
R
Roger Quadros 已提交
68
{
69
	struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev);
R
Roger Quadros 已提交
70

71 72 73 74 75
	if (!IS_ERR(nop->vcc)) {
		if (regulator_enable(nop->vcc))
			dev_err(phy->dev, "Failed to enable power\n");
	}

R
Roger Quadros 已提交
76 77 78
	if (!IS_ERR(nop->clk))
		clk_enable(nop->clk);

79 80 81 82 83 84
	if (!IS_ERR(nop->reset)) {
		/* De-assert RESET */
		if (regulator_enable(nop->reset))
			dev_err(phy->dev, "Failed to de-assert reset\n");
	}

R
Roger Quadros 已提交
85 86
	return 0;
}
87
EXPORT_SYMBOL_GPL(usb_gen_phy_init);
R
Roger Quadros 已提交
88

89
void usb_gen_phy_shutdown(struct usb_phy *phy)
R
Roger Quadros 已提交
90
{
91
	struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev);
R
Roger Quadros 已提交
92

93 94 95 96 97 98
	if (!IS_ERR(nop->reset)) {
		/* Assert RESET */
		if (regulator_disable(nop->reset))
			dev_err(phy->dev, "Failed to assert reset\n");
	}

R
Roger Quadros 已提交
99 100
	if (!IS_ERR(nop->clk))
		clk_disable(nop->clk);
101 102 103 104 105

	if (!IS_ERR(nop->vcc)) {
		if (regulator_disable(nop->vcc))
			dev_err(phy->dev, "Failed to disable power\n");
	}
R
Roger Quadros 已提交
106
}
107
EXPORT_SYMBOL_GPL(usb_gen_phy_shutdown);
R
Roger Quadros 已提交
108

109
static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
110
{
111
	if (!otg)
112 113 114
		return -ENODEV;

	if (!gadget) {
115
		otg->gadget = NULL;
116 117 118
		return -ENODEV;
	}

119 120
	otg->gadget = gadget;
	otg->phy->state = OTG_STATE_B_IDLE;
121 122 123
	return 0;
}

124
static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
125
{
126
	if (!otg)
127 128 129
		return -ENODEV;

	if (!host) {
130
		otg->host = NULL;
131 132 133
		return -ENODEV;
	}

134
	otg->host = host;
135 136 137
	return 0;
}

138 139 140
int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
		enum usb_phy_type type, u32 clk_rate, bool needs_vcc,
		bool needs_reset)
141 142 143
{
	int err;

144 145
	nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
			GFP_KERNEL);
146
	if (!nop->phy.otg)
147 148
		return -ENOMEM;

149
	nop->clk = devm_clk_get(dev, "main_clk");
R
Roger Quadros 已提交
150
	if (IS_ERR(nop->clk)) {
151
		dev_dbg(dev, "Can't get phy clock: %ld\n",
R
Roger Quadros 已提交
152 153 154
					PTR_ERR(nop->clk));
	}

155 156
	if (!IS_ERR(nop->clk) && clk_rate) {
		err = clk_set_rate(nop->clk, clk_rate);
R
Roger Quadros 已提交
157
		if (err) {
158
			dev_err(dev, "Error setting clock rate\n");
R
Roger Quadros 已提交
159 160 161 162 163 164 165
			return err;
		}
	}

	if (!IS_ERR(nop->clk)) {
		err = clk_prepare(nop->clk);
		if (err) {
166
			dev_err(dev, "Error preparing clock\n");
R
Roger Quadros 已提交
167 168 169 170
			return err;
		}
	}

171
	nop->vcc = devm_regulator_get(dev, "vcc");
172
	if (IS_ERR(nop->vcc)) {
173
		dev_dbg(dev, "Error getting vcc regulator: %ld\n",
174
					PTR_ERR(nop->vcc));
175 176
		if (needs_vcc)
			return -EPROBE_DEFER;
177 178
	}

179
	nop->reset = devm_regulator_get(dev, "reset");
180
	if (IS_ERR(nop->reset)) {
181
		dev_dbg(dev, "Error getting reset regulator: %ld\n",
182
					PTR_ERR(nop->reset));
183 184
		if (needs_reset)
			return -EPROBE_DEFER;
185 186
	}

187
	nop->dev		= dev;
188 189 190 191
	nop->phy.dev		= nop->dev;
	nop->phy.label		= "nop-xceiv";
	nop->phy.set_suspend	= nop_set_suspend;
	nop->phy.state		= OTG_STATE_UNDEFINED;
192
	nop->phy.type		= type;
193 194 195 196 197

	nop->phy.otg->phy		= &nop->phy;
	nop->phy.otg->set_host		= nop_set_host;
	nop->phy.otg->set_peripheral	= nop_set_peripheral;

198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
	ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier);
	return 0;
}
EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);

void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop)
{
	if (!IS_ERR(nop->clk))
		clk_unprepare(nop->clk);
}
EXPORT_SYMBOL_GPL(usb_phy_gen_cleanup_phy);

static int usb_phy_gen_xceiv_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct usb_phy_gen_xceiv_platform_data *pdata =
			dev_get_platdata(&pdev->dev);
	struct usb_phy_gen_xceiv	*nop;
	enum usb_phy_type	type = USB_PHY_TYPE_USB2;
	int err;
	u32 clk_rate = 0;
	bool needs_vcc = false;
	bool needs_reset = false;

	if (dev->of_node) {
		struct device_node *node = dev->of_node;

		if (of_property_read_u32(node, "clock-frequency", &clk_rate))
			clk_rate = 0;

		needs_vcc = of_property_read_bool(node, "vcc-supply");
		needs_reset = of_property_read_bool(node, "reset-supply");

	} else if (pdata) {
		type = pdata->type;
		clk_rate = pdata->clk_rate;
		needs_vcc = pdata->needs_vcc;
		needs_reset = pdata->needs_reset;
	}

	nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL);
	if (!nop)
		return -ENOMEM;


	err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc,
			needs_reset);
	if (err)
		return err;

	nop->phy.init		= usb_gen_phy_init;
	nop->phy.shutdown	= usb_gen_phy_shutdown;

251
	err = usb_add_phy_dev(&nop->phy);
252 253 254
	if (err) {
		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
			err);
R
Roger Quadros 已提交
255
		goto err_add;
256 257 258 259 260
	}

	platform_set_drvdata(pdev, nop);

	return 0;
R
Roger Quadros 已提交
261 262

err_add:
263
	usb_phy_gen_cleanup_phy(nop);
R
Roger Quadros 已提交
264
	return err;
265 266
}

267
static int usb_phy_gen_xceiv_remove(struct platform_device *pdev)
268
{
269
	struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev);
270

271
	usb_phy_gen_cleanup_phy(nop);
272
	usb_remove_phy(&nop->phy);
273 274 275 276

	return 0;
}

277 278 279 280 281 282 283
static const struct of_device_id nop_xceiv_dt_ids[] = {
	{ .compatible = "usb-nop-xceiv" },
	{ }
};

MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);

284 285 286
static struct platform_driver usb_phy_gen_xceiv_driver = {
	.probe		= usb_phy_gen_xceiv_probe,
	.remove		= usb_phy_gen_xceiv_remove,
287
	.driver		= {
288
		.name	= "usb_phy_gen_xceiv",
289
		.owner	= THIS_MODULE,
290
		.of_match_table = nop_xceiv_dt_ids,
291 292 293
	},
};

294
static int __init usb_phy_gen_xceiv_init(void)
295
{
296
	return platform_driver_register(&usb_phy_gen_xceiv_driver);
297
}
298
subsys_initcall(usb_phy_gen_xceiv_init);
299

300
static void __exit usb_phy_gen_xceiv_exit(void)
301
{
302
	platform_driver_unregister(&usb_phy_gen_xceiv_driver);
303
}
304
module_exit(usb_phy_gen_xceiv_exit);
305

306
MODULE_ALIAS("platform:usb_phy_gen_xceiv");
307 308 309
MODULE_AUTHOR("Texas Instruments Inc");
MODULE_DESCRIPTION("NOP USB Transceiver driver");
MODULE_LICENSE("GPL");