dwc3-omap.c 12.5 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 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/**
 * dwc3-omap.c - OMAP Specific Glue layer
 *
 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
 *
 * Authors: Felipe Balbi <balbi@ti.com>,
 *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The names of the above-listed copyright holders may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * ALTERNATIVELY, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2, as published by the Free
 * Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

39
#include <linux/module.h>
40 41 42 43 44
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
45
#include <linux/platform_data/dwc3-omap.h>
46
#include <linux/usb/dwc3-omap.h>
47
#include <linux/pm_runtime.h>
48 49 50
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
#include <linux/io.h>
51
#include <linux/of.h>
52
#include <linux/of_platform.h>
53

54 55
#include <linux/usb/otg.h>

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
/*
 * All these registers belong to OMAP's Wrapper around the
 * DesignWare USB3 Core.
 */

#define USBOTGSS_REVISION			0x0000
#define USBOTGSS_SYSCONFIG			0x0010
#define USBOTGSS_IRQ_EOI			0x0020
#define USBOTGSS_IRQSTATUS_RAW_0		0x0024
#define USBOTGSS_IRQSTATUS_0			0x0028
#define USBOTGSS_IRQENABLE_SET_0		0x002c
#define USBOTGSS_IRQENABLE_CLR_0		0x0030
#define USBOTGSS_IRQSTATUS_RAW_1		0x0034
#define USBOTGSS_IRQSTATUS_1			0x0038
#define USBOTGSS_IRQENABLE_SET_1		0x003c
#define USBOTGSS_IRQENABLE_CLR_1		0x0040
#define USBOTGSS_UTMI_OTG_CTRL			0x0080
#define USBOTGSS_UTMI_OTG_STATUS		0x0084
#define USBOTGSS_MMRAM_OFFSET			0x0100
#define USBOTGSS_FLADJ				0x0104
#define USBOTGSS_DEBUG_CFG			0x0108
#define USBOTGSS_DEBUG_DATA			0x010c

/* SYSCONFIG REGISTER */
#define USBOTGSS_SYSCONFIG_DMADISABLE		(1 << 16)
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
/* IRQ_EOI REGISTER */
#define USBOTGSS_IRQ_EOI_LINE_NUMBER		(1 << 0)

/* IRQS0 BITS */
#define USBOTGSS_IRQO_COREIRQ_ST		(1 << 0)

/* IRQ1 BITS */
#define USBOTGSS_IRQ1_DMADISABLECLR		(1 << 17)
#define USBOTGSS_IRQ1_OEVT			(1 << 16)
#define USBOTGSS_IRQ1_DRVVBUS_RISE		(1 << 13)
#define USBOTGSS_IRQ1_CHRGVBUS_RISE		(1 << 12)
#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE		(1 << 11)
#define USBOTGSS_IRQ1_IDPULLUP_RISE		(1 << 8)
#define USBOTGSS_IRQ1_DRVVBUS_FALL		(1 << 5)
#define USBOTGSS_IRQ1_CHRGVBUS_FALL		(1 << 4)
#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL		(1 << 3)
#define USBOTGSS_IRQ1_IDPULLUP_FALL		(1 << 0)

/* UTMI_OTG_CTRL REGISTER */
#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS		(1 << 5)
#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS		(1 << 4)
#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS	(1 << 3)
#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP		(1 << 0)

/* UTMI_OTG_STATUS REGISTER */
#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE	(1 << 31)
#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT	(1 << 9)
#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8)
#define USBOTGSS_UTMI_OTG_STATUS_IDDIG		(1 << 4)
#define USBOTGSS_UTMI_OTG_STATUS_SESSEND	(1 << 3)
#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID	(1 << 2)
#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID	(1 << 1)

struct dwc3_omap {
	/* device lock */
	spinlock_t		lock;

	struct device		*dev;

	int			irq;
	void __iomem		*base;

124 125
	u32			utmi_otg_status;

126 127 128
	u32			dma_status:1;
};

129
static struct dwc3_omap		*_omap;
130

131 132 133 134 135 136 137 138 139 140
static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
{
	return readl(base + offset);
}

static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
{
	writel(value, base + offset);
}

141
int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
142 143 144 145
{
	u32			val;
	struct dwc3_omap	*omap = _omap;

146 147 148
	if (!omap)
		return -EPROBE_DEFER;

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
	switch (status) {
	case OMAP_DWC3_ID_GROUND:
		dev_dbg(omap->dev, "ID GND\n");

		val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
		val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
				| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
				| USBOTGSS_UTMI_OTG_STATUS_SESSEND);
		val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID
				| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
		dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
		break;

	case OMAP_DWC3_VBUS_VALID:
		dev_dbg(omap->dev, "VBUS Connect\n");

		val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
		val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND;
		val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG
				| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
				| USBOTGSS_UTMI_OTG_STATUS_SESSVALID
				| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
		dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
		break;

	case OMAP_DWC3_ID_FLOAT:
	case OMAP_DWC3_VBUS_OFF:
		dev_dbg(omap->dev, "VBUS Disconnect\n");

		val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
		val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID
				| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
				| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT);
		val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND
				| USBOTGSS_UTMI_OTG_STATUS_IDDIG;
		dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
		break;

	default:
		dev_dbg(omap->dev, "ID float\n");
	}

191
	return 0;
192 193 194
}
EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);

195 196 197 198 199 200 201
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
	struct dwc3_omap	*omap = _omap;
	u32			reg;

	spin_lock(&omap->lock);

202
	reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_1);
203 204

	if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
205
		dev_dbg(omap->dev, "DMA Disable was Cleared\n");
206 207 208 209
		omap->dma_status = false;
	}

	if (reg & USBOTGSS_IRQ1_OEVT)
210
		dev_dbg(omap->dev, "OTG Event\n");
211

F
Felipe Balbi 已提交
212
	if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE)
213
		dev_dbg(omap->dev, "DRVVBUS Rise\n");
214

F
Felipe Balbi 已提交
215
	if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE)
216
		dev_dbg(omap->dev, "CHRGVBUS Rise\n");
217

F
Felipe Balbi 已提交
218
	if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE)
219
		dev_dbg(omap->dev, "DISCHRGVBUS Rise\n");
220

F
Felipe Balbi 已提交
221
	if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE)
222
		dev_dbg(omap->dev, "IDPULLUP Rise\n");
223

F
Felipe Balbi 已提交
224
	if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL)
225
		dev_dbg(omap->dev, "DRVVBUS Fall\n");
226

F
Felipe Balbi 已提交
227
	if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL)
228
		dev_dbg(omap->dev, "CHRGVBUS Fall\n");
229

F
Felipe Balbi 已提交
230
	if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL)
231
		dev_dbg(omap->dev, "DISCHRGVBUS Fall\n");
232

F
Felipe Balbi 已提交
233
	if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL)
234
		dev_dbg(omap->dev, "IDPULLUP Fall\n");
235

236
	dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
F
Felipe Balbi 已提交
237

238 239
	reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0);
	dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
240 241 242 243 244 245

	spin_unlock(&omap->lock);

	return IRQ_HANDLED;
}

246 247 248 249 250 251 252 253 254
static int dwc3_omap_remove_core(struct device *dev, void *c)
{
	struct platform_device *pdev = to_platform_device(dev);

	platform_device_unregister(pdev);

	return 0;
}

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
{
	u32			reg;

	/* enable all IRQs */
	reg = USBOTGSS_IRQO_COREIRQ_ST;
	dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);

	reg = (USBOTGSS_IRQ1_OEVT |
			USBOTGSS_IRQ1_DRVVBUS_RISE |
			USBOTGSS_IRQ1_CHRGVBUS_RISE |
			USBOTGSS_IRQ1_DISCHRGVBUS_RISE |
			USBOTGSS_IRQ1_IDPULLUP_RISE |
			USBOTGSS_IRQ1_DRVVBUS_FALL |
			USBOTGSS_IRQ1_CHRGVBUS_FALL |
			USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
			USBOTGSS_IRQ1_IDPULLUP_FALL);

	dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
}

static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
{
	/* disable all IRQs */
	dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, 0x00);
	dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x00);
}

283 284
static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);

B
Bill Pemberton 已提交
285
static int dwc3_omap_probe(struct platform_device *pdev)
286
{
287 288
	struct device_node	*node = pdev->dev.of_node;

289 290
	struct dwc3_omap	*omap;
	struct resource		*res;
C
Chanho Park 已提交
291
	struct device		*dev = &pdev->dev;
292 293 294 295

	int			ret = -ENOMEM;
	int			irq;

296 297
	int			utmi_mode = 0;

298 299 300 301
	u32			reg;

	void __iomem		*base;

302 303 304 305 306
	if (!node) {
		dev_err(dev, "device node not found\n");
		return -EINVAL;
	}

C
Chanho Park 已提交
307
	omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
308
	if (!omap) {
C
Chanho Park 已提交
309 310
		dev_err(dev, "not enough memory\n");
		return -ENOMEM;
311 312 313 314
	}

	platform_set_drvdata(pdev, omap);

315
	irq = platform_get_irq(pdev, 0);
316
	if (irq < 0) {
C
Chanho Park 已提交
317 318
		dev_err(dev, "missing IRQ resource\n");
		return -EINVAL;
319 320
	}

321
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
322
	if (!res) {
C
Chanho Park 已提交
323 324
		dev_err(dev, "missing memory base resource\n");
		return -EINVAL;
325 326
	}

C
Chanho Park 已提交
327
	base = devm_ioremap_nocache(dev, res->start, resource_size(res));
328
	if (!base) {
C
Chanho Park 已提交
329 330
		dev_err(dev, "ioremap failed\n");
		return -ENOMEM;
331 332 333 334
	}

	spin_lock_init(&omap->lock);

C
Chanho Park 已提交
335
	omap->dev	= dev;
336 337
	omap->irq	= irq;
	omap->base	= base;
338
	dev->dma_mask	= &dwc3_omap_dma_mask;
339

340 341 342 343 344 345
	/*
	 * REVISIT if we ever have two instances of the wrapper, we will be
	 * in big trouble
	 */
	_omap	= omap;

346 347 348 349 350 351 352
	pm_runtime_enable(dev);
	ret = pm_runtime_get_sync(dev);
	if (ret < 0) {
		dev_err(dev, "get_sync failed with err %d\n", ret);
		return ret;
	}

353
	reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
354

355
	of_property_read_u32(node, "utmi-mode", &utmi_mode);
356 357 358 359 360 361 362 363 364 365

	switch (utmi_mode) {
	case DWC3_OMAP_UTMI_MODE_SW:
		reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
		break;
	case DWC3_OMAP_UTMI_MODE_HW:
		reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
		break;
	default:
		dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
366 367
	}

368
	dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
369

370
	/* check the DMA Status */
371
	reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
372 373
	omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);

C
Chanho Park 已提交
374
	ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
375
			"dwc3-omap", omap);
376
	if (ret) {
C
Chanho Park 已提交
377
		dev_err(dev, "failed to request IRQ #%d --> %d\n",
378
				omap->irq, ret);
379
		return ret;
380 381
	}

382
	dwc3_omap_enable_irqs(omap);
383

384 385 386 387
	ret = of_platform_populate(node, NULL, NULL, dev);
	if (ret) {
		dev_err(&pdev->dev, "failed to create dwc3 core\n");
		return ret;
388 389 390 391 392
	}

	return 0;
}

B
Bill Pemberton 已提交
393
static int dwc3_omap_remove(struct platform_device *pdev)
394
{
395 396 397
	struct dwc3_omap	*omap = platform_get_drvdata(pdev);

	dwc3_omap_disable_irqs(omap);
398 399
	pm_runtime_put_sync(&pdev->dev);
	pm_runtime_disable(&pdev->dev);
400 401
	device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);

402 403 404
	return 0;
}

405
static const struct of_device_id of_dwc3_match[] = {
406
	{
407
		.compatible =	"ti,dwc3"
408 409 410
	},
	{ },
};
411
MODULE_DEVICE_TABLE(of, of_dwc3_match);
412

413
#ifdef CONFIG_PM_SLEEP
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
static int dwc3_omap_prepare(struct device *dev)
{
	struct dwc3_omap	*omap = dev_get_drvdata(dev);

	dwc3_omap_disable_irqs(omap);

	return 0;
}

static void dwc3_omap_complete(struct device *dev)
{
	struct dwc3_omap	*omap = dev_get_drvdata(dev);

	dwc3_omap_enable_irqs(omap);
}

static int dwc3_omap_suspend(struct device *dev)
{
	struct dwc3_omap	*omap = dev_get_drvdata(dev);

	omap->utmi_otg_status = dwc3_omap_readl(omap->base,
			USBOTGSS_UTMI_OTG_STATUS);

	return 0;
}

static int dwc3_omap_resume(struct device *dev)
{
	struct dwc3_omap	*omap = dev_get_drvdata(dev);

	dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS,
			omap->utmi_otg_status);

	pm_runtime_disable(dev);
	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);

	return 0;
}

static const struct dev_pm_ops dwc3_omap_dev_pm_ops = {
	.prepare	= dwc3_omap_prepare,
	.complete	= dwc3_omap_complete,

	SET_SYSTEM_SLEEP_PM_OPS(dwc3_omap_suspend, dwc3_omap_resume)
};

#define DEV_PM_OPS	(&dwc3_omap_dev_pm_ops)
#else
#define DEV_PM_OPS	NULL
464
#endif /* CONFIG_PM_SLEEP */
465

466 467
static struct platform_driver dwc3_omap_driver = {
	.probe		= dwc3_omap_probe,
B
Bill Pemberton 已提交
468
	.remove		= dwc3_omap_remove,
469 470
	.driver		= {
		.name	= "omap-dwc3",
471
		.of_match_table	= of_dwc3_match,
472
		.pm	= DEV_PM_OPS,
473 474 475
	},
};

476 477
module_platform_driver(dwc3_omap_driver);

478
MODULE_ALIAS("platform:omap-dwc3");
479 480 481
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer");