phy-generic.c 8.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
/*
 * 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:
23 24
 *	This provides a "nop" transceiver for PHYs which are
 *	autonomous such as isp1504, isp1707, etc.
25 26 27 28 29
 */

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
30
#include <linux/usb/gadget.h>
31
#include <linux/usb/otg.h>
32
#include <linux/usb/usb_phy_generic.h>
33
#include <linux/slab.h>
R
Roger Quadros 已提交
34
#include <linux/clk.h>
35
#include <linux/regulator/consumer.h>
36
#include <linux/of.h>
37 38 39
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/delay.h>
40

41
#include "phy-generic.h"
42

43
#define VBUS_IRQ_FLAGS \
44 45
	(IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \
		IRQF_ONESHOT)
46

47
struct platform_device *usb_phy_generic_register(void)
48
{
49 50
	return platform_device_register_simple("usb_phy_generic",
			PLATFORM_DEVID_AUTO, NULL, 0);
51
}
52
EXPORT_SYMBOL_GPL(usb_phy_generic_register);
53

54
void usb_phy_generic_unregister(struct platform_device *pdev)
55
{
56
	platform_device_unregister(pdev);
57
}
58
EXPORT_SYMBOL_GPL(usb_phy_generic_unregister);
59

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

65
static void nop_reset(struct usb_phy_generic *nop)
66
{
67 68
	if (!nop->gpiod_reset)
		return;
69

70
	gpiod_set_value(nop->gpiod_reset, 1);
71
	usleep_range(10000, 20000);
72
	gpiod_set_value(nop->gpiod_reset, 0);
73 74
}

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
/* interface to regulator framework */
static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
{
	struct regulator *vbus_draw = nop->vbus_draw;
	int enabled;
	int ret;

	if (!vbus_draw)
		return;

	enabled = nop->vbus_draw_enabled;
	if (mA) {
		regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
		if (!enabled) {
			ret = regulator_enable(vbus_draw);
			if (ret < 0)
				return;
			nop->vbus_draw_enabled = 1;
		}
	} else {
		if (enabled) {
			ret = regulator_disable(vbus_draw);
			if (ret < 0)
				return;
			nop->vbus_draw_enabled = 0;
		}
	}
	nop->mA = mA;
}


static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
{
	struct usb_phy_generic *nop = data;
	struct usb_otg *otg = nop->phy.otg;
	int vbus, status;

	vbus = gpiod_get_value(nop->gpiod_vbus);
	if ((vbus ^ nop->vbus) == 0)
		return IRQ_HANDLED;
	nop->vbus = vbus;

	if (vbus) {
		status = USB_EVENT_VBUS;
		otg->state = OTG_STATE_B_PERIPHERAL;
		nop->phy.last_event = status;
		usb_gadget_vbus_connect(otg->gadget);

		/* drawing a "unit load" is *always* OK, except for OTG */
		nop_set_vbus_draw(nop, 100);

		atomic_notifier_call_chain(&nop->phy.notifier, status,
					   otg->gadget);
	} else {
		nop_set_vbus_draw(nop, 0);

		usb_gadget_vbus_disconnect(otg->gadget);
		status = USB_EVENT_NONE;
		otg->state = OTG_STATE_B_IDLE;
		nop->phy.last_event = status;

		atomic_notifier_call_chain(&nop->phy.notifier, status,
					   otg->gadget);
	}
	return IRQ_HANDLED;
}

142
int usb_gen_phy_init(struct usb_phy *phy)
R
Roger Quadros 已提交
143
{
144
	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
R
Roger Quadros 已提交
145

146 147 148 149 150
	if (!IS_ERR(nop->vcc)) {
		if (regulator_enable(nop->vcc))
			dev_err(phy->dev, "Failed to enable power\n");
	}

R
Roger Quadros 已提交
151
	if (!IS_ERR(nop->clk))
152
		clk_prepare_enable(nop->clk);
R
Roger Quadros 已提交
153

154
	nop_reset(nop);
155

R
Roger Quadros 已提交
156 157
	return 0;
}
158
EXPORT_SYMBOL_GPL(usb_gen_phy_init);
R
Roger Quadros 已提交
159

160
void usb_gen_phy_shutdown(struct usb_phy *phy)
R
Roger Quadros 已提交
161
{
162
	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
R
Roger Quadros 已提交
163

164
	gpiod_set_value(nop->gpiod_reset, 1);
165

R
Roger Quadros 已提交
166
	if (!IS_ERR(nop->clk))
167
		clk_disable_unprepare(nop->clk);
168 169 170 171 172

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

176
static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
177
{
178
	if (!otg)
179 180 181
		return -ENODEV;

	if (!gadget) {
182
		otg->gadget = NULL;
183 184 185
		return -ENODEV;
	}

186
	otg->gadget = gadget;
187
	otg->state = OTG_STATE_B_IDLE;
188 189 190
	return 0;
}

191
static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
192
{
193
	if (!otg)
194 195 196
		return -ENODEV;

	if (!host) {
197
		otg->host = NULL;
198 199 200
		return -ENODEV;
	}

201
	otg->host = host;
202 203 204
	return 0;
}

205 206
int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
		struct usb_phy_generic_platform_data *pdata)
207
{
208
	enum usb_phy_type type = USB_PHY_TYPE_USB2;
209
	int err = 0;
210

211 212 213 214 215 216 217 218 219 220
	u32 clk_rate = 0;
	bool needs_vcc = 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");
221 222
		nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset");
		err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
223
		if (!err) {
224
			nop->gpiod_vbus = devm_gpiod_get_optional(dev,
225 226
							 "vbus-detect");
			err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
227
		}
228 229 230 231
	} else if (pdata) {
		type = pdata->type;
		clk_rate = pdata->clk_rate;
		needs_vcc = pdata->needs_vcc;
232
		if (gpio_is_valid(pdata->gpio_reset)) {
233 234 235 236 237 238
			err = devm_gpio_request_one(dev, pdata->gpio_reset, 0,
						    dev_name(dev));
			if (!err)
				nop->gpiod_reset =
					gpio_to_desc(pdata->gpio_reset);
		}
239
		nop->gpiod_vbus = pdata->gpiod_vbus;
240 241 242 243 244
	}

	if (err == -EPROBE_DEFER)
		return -EPROBE_DEFER;
	if (err) {
245
		dev_err(dev, "Error requesting RESET or VBUS GPIO\n");
246
		return err;
247
	}
248 249
	if (nop->gpiod_reset)
		gpiod_direction_output(nop->gpiod_reset, 1);
250

251 252
	nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
			GFP_KERNEL);
253
	if (!nop->phy.otg)
254 255
		return -ENOMEM;

256
	nop->clk = devm_clk_get(dev, "main_clk");
R
Roger Quadros 已提交
257
	if (IS_ERR(nop->clk)) {
258
		dev_dbg(dev, "Can't get phy clock: %ld\n",
R
Roger Quadros 已提交
259 260 261
					PTR_ERR(nop->clk));
	}

262 263
	if (!IS_ERR(nop->clk) && clk_rate) {
		err = clk_set_rate(nop->clk, clk_rate);
R
Roger Quadros 已提交
264
		if (err) {
265
			dev_err(dev, "Error setting clock rate\n");
R
Roger Quadros 已提交
266 267 268 269
			return err;
		}
	}

270
	nop->vcc = devm_regulator_get(dev, "vcc");
271
	if (IS_ERR(nop->vcc)) {
272
		dev_dbg(dev, "Error getting vcc regulator: %ld\n",
273
					PTR_ERR(nop->vcc));
274 275
		if (needs_vcc)
			return -EPROBE_DEFER;
276 277
	}

278
	nop->dev		= dev;
279 280 281
	nop->phy.dev		= nop->dev;
	nop->phy.label		= "nop-xceiv";
	nop->phy.set_suspend	= nop_set_suspend;
282
	nop->phy.type		= type;
283

284
	nop->phy.otg->state		= OTG_STATE_UNDEFINED;
A
Antoine Tenart 已提交
285
	nop->phy.otg->usb_phy		= &nop->phy;
286 287 288
	nop->phy.otg->set_host		= nop_set_host;
	nop->phy.otg->set_peripheral	= nop_set_peripheral;

289 290 291 292
	return 0;
}
EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);

293
static int usb_phy_generic_probe(struct platform_device *pdev)
294 295
{
	struct device *dev = &pdev->dev;
296
	struct usb_phy_generic	*nop;
297
	int err;
298 299 300 301 302

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

303
	err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
304 305
	if (err)
		return err;
306 307 308 309 310 311 312 313 314 315 316 317
	if (nop->gpiod_vbus) {
		err = devm_request_threaded_irq(&pdev->dev,
						gpiod_to_irq(nop->gpiod_vbus),
						NULL, nop_gpio_vbus_thread,
						VBUS_IRQ_FLAGS, "vbus_detect",
						nop);
		if (err) {
			dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
				gpiod_to_irq(nop->gpiod_vbus), err);
			return err;
		}
	}
318 319 320 321

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

322
	err = usb_add_phy_dev(&nop->phy);
323 324 325
	if (err) {
		dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
			err);
326
		return err;
327 328 329 330 331 332 333
	}

	platform_set_drvdata(pdev, nop);

	return 0;
}

334
static int usb_phy_generic_remove(struct platform_device *pdev)
335
{
336
	struct usb_phy_generic *nop = platform_get_drvdata(pdev);
337

338
	usb_remove_phy(&nop->phy);
339 340 341 342

	return 0;
}

343 344 345 346 347 348 349
static const struct of_device_id nop_xceiv_dt_ids[] = {
	{ .compatible = "usb-nop-xceiv" },
	{ }
};

MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);

350 351 352
static struct platform_driver usb_phy_generic_driver = {
	.probe		= usb_phy_generic_probe,
	.remove		= usb_phy_generic_remove,
353
	.driver		= {
354
		.name	= "usb_phy_generic",
355
		.of_match_table = nop_xceiv_dt_ids,
356 357 358
	},
};

359
static int __init usb_phy_generic_init(void)
360
{
361
	return platform_driver_register(&usb_phy_generic_driver);
362
}
363
subsys_initcall(usb_phy_generic_init);
364

365
static void __exit usb_phy_generic_exit(void)
366
{
367
	platform_driver_unregister(&usb_phy_generic_driver);
368
}
369
module_exit(usb_phy_generic_exit);
370

371
MODULE_ALIAS("platform:usb_phy_generic");
372 373 374
MODULE_AUTHOR("Texas Instruments Inc");
MODULE_DESCRIPTION("NOP USB Transceiver driver");
MODULE_LICENSE("GPL");