phy-generic.c 6.7 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
	if (!IS_ERR(nop->clk))
77
		clk_prepare_enable(nop->clk);
R
Roger Quadros 已提交
78

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
	if (!IS_ERR(nop->clk))
100
		clk_disable_unprepare(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
			return err;
		}
	}

163
	nop->vcc = devm_regulator_get(dev, "vcc");
164
	if (IS_ERR(nop->vcc)) {
165
		dev_dbg(dev, "Error getting vcc regulator: %ld\n",
166
					PTR_ERR(nop->vcc));
167 168
		if (needs_vcc)
			return -EPROBE_DEFER;
169 170
	}

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

179
	nop->dev		= dev;
180 181 182 183
	nop->phy.dev		= nop->dev;
	nop->phy.label		= "nop-xceiv";
	nop->phy.set_suspend	= nop_set_suspend;
	nop->phy.state		= OTG_STATE_UNDEFINED;
184
	nop->phy.type		= type;
185 186 187 188 189

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

190 191 192 193 194 195 196 197 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
	ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier);
	return 0;
}
EXPORT_SYMBOL_GPL(usb_phy_gen_create_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;

236
	err = usb_add_phy_dev(&nop->phy);
237 238 239
	if (err) {
		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
			err);
240
		return err;
241 242 243 244 245
	}

	platform_set_drvdata(pdev, nop);

	return 0;
R
Roger Quadros 已提交
246 247

	return err;
248 249
}

250
static int usb_phy_gen_xceiv_remove(struct platform_device *pdev)
251
{
252
	struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev);
253

254
	usb_remove_phy(&nop->phy);
255 256 257 258

	return 0;
}

259 260 261 262 263 264 265
static const struct of_device_id nop_xceiv_dt_ids[] = {
	{ .compatible = "usb-nop-xceiv" },
	{ }
};

MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);

266 267 268
static struct platform_driver usb_phy_gen_xceiv_driver = {
	.probe		= usb_phy_gen_xceiv_probe,
	.remove		= usb_phy_gen_xceiv_remove,
269
	.driver		= {
270
		.name	= "usb_phy_gen_xceiv",
271
		.owner	= THIS_MODULE,
272
		.of_match_table = nop_xceiv_dt_ids,
273 274 275
	},
};

276
static int __init usb_phy_gen_xceiv_init(void)
277
{
278
	return platform_driver_register(&usb_phy_gen_xceiv_driver);
279
}
280
subsys_initcall(usb_phy_gen_xceiv_init);
281

282
static void __exit usb_phy_gen_xceiv_exit(void)
283
{
284
	platform_driver_unregister(&usb_phy_gen_xceiv_driver);
285
}
286
module_exit(usb_phy_gen_xceiv_exit);
287

288
MODULE_ALIAS("platform:usb_phy_gen_xceiv");
289 290 291
MODULE_AUTHOR("Texas Instruments Inc");
MODULE_DESCRIPTION("NOP USB Transceiver driver");
MODULE_LICENSE("GPL");