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",
							   GPIOD_ASIS);
223
		err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
224
		if (!err) {
225
			nop->gpiod_vbus = devm_gpiod_get_optional(dev,
226 227
							 "vbus-detect",
							 GPIOD_ASIS);
228
			err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
229
		}
230 231 232 233
	} else if (pdata) {
		type = pdata->type;
		clk_rate = pdata->clk_rate;
		needs_vcc = pdata->needs_vcc;
234
		if (gpio_is_valid(pdata->gpio_reset)) {
235 236 237 238 239 240
			err = devm_gpio_request_one(dev, pdata->gpio_reset, 0,
						    dev_name(dev));
			if (!err)
				nop->gpiod_reset =
					gpio_to_desc(pdata->gpio_reset);
		}
241
		nop->gpiod_vbus = pdata->gpiod_vbus;
242 243 244 245 246
	}

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

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

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

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

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

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

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

291 292 293 294
	return 0;
}
EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);

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

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

305
	err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
306 307
	if (err)
		return err;
308 309 310 311 312 313 314 315 316 317 318 319
	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;
		}
	}
320 321 322 323

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

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

	platform_set_drvdata(pdev, nop);

	return 0;
}

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

340
	usb_remove_phy(&nop->phy);
341 342 343 344

	return 0;
}

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

MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids);

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

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

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

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