pxa3xx-ulpi.c 8.14 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
/*
 * linux/arch/arm/mach-pxa/pxa3xx-ulpi.c
 *
 * code specific to pxa3xx aka Monahans
 *
 * Copyright (C) 2010 CompuLab Ltd.
 *
 * 2010-13-07: Igor Grinberg <grinberg@compulab.co.il>
 *             initial version: pxa310 USB Host mode support
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>

#include <mach/hardware.h>
29
#include "regs-u2d.h"
30
#include <linux/platform_data/usb-pxa3xx-ulpi.h>
31 32 33 34 35

struct pxa3xx_u2d_ulpi {
	struct clk		*clk;
	void __iomem		*mmio_base;

36
	struct usb_phy		*otg;
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
	unsigned int		ulpi_mode;
};

static struct pxa3xx_u2d_ulpi *u2d;

static inline u32 u2d_readl(u32 reg)
{
	return __raw_readl(u2d->mmio_base + reg);
}

static inline void u2d_writel(u32 reg, u32 val)
{
	__raw_writel(val, u2d->mmio_base + reg);
}

#if defined(CONFIG_PXA310_ULPI)
enum u2d_ulpi_phy_mode {
	SYNCH		= 0,
	CARKIT		= (1 << 0),
	SER_3PIN	= (1 << 1),
	SER_6PIN	= (1 << 2),
	LOWPOWER	= (1 << 3),
};

static inline enum u2d_ulpi_phy_mode pxa310_ulpi_get_phymode(void)
{
	return (u2d_readl(U2DOTGUSR) >> 28) & 0xF;
}

static int pxa310_ulpi_poll(void)
{
	int timeout = 50000;

	while (timeout--) {
		if (!(u2d_readl(U2DOTGUCR) & U2DOTGUCR_RUN))
			return 0;

		cpu_relax();
	}

77
	pr_warn("%s: ULPI access timed out!\n", __func__);
78 79 80 81

	return -ETIMEDOUT;
}

82
static int pxa310_ulpi_read(struct usb_phy *otg, u32 reg)
83 84 85 86
{
	int err;

	if (pxa310_ulpi_get_phymode() != SYNCH) {
87
		pr_warn("%s: PHY is not in SYNCH mode!\n", __func__);
88 89 90 91 92 93 94 95 96 97 98 99 100
		return -EBUSY;
	}

	u2d_writel(U2DOTGUCR, U2DOTGUCR_RUN | U2DOTGUCR_RNW | (reg << 16));
	msleep(5);

	err = pxa310_ulpi_poll();
	if (err)
		return err;

	return u2d_readl(U2DOTGUCR) & U2DOTGUCR_RDATA;
}

101
static int pxa310_ulpi_write(struct usb_phy *otg, u32 val, u32 reg)
102 103
{
	if (pxa310_ulpi_get_phymode() != SYNCH) {
104
		pr_warn("%s: PHY is not in SYNCH mode!\n", __func__);
105 106 107 108 109 110 111 112 113
		return -EBUSY;
	}

	u2d_writel(U2DOTGUCR, U2DOTGUCR_RUN | (reg << 16) | (val << 8));
	msleep(5);

	return pxa310_ulpi_poll();
}

114
struct usb_phy_io_ops pxa310_ulpi_access_ops = {
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
	.read	= pxa310_ulpi_read,
	.write	= pxa310_ulpi_write,
};

static void pxa310_otg_transceiver_rtsm(void)
{
	u32 u2dotgcr;

	/* put PHY to sync mode */
	u2dotgcr = u2d_readl(U2DOTGCR);
	u2dotgcr |=  U2DOTGCR_RTSM | U2DOTGCR_UTMID;
	u2d_writel(U2DOTGCR, u2dotgcr);
	msleep(10);

	/* setup OTG sync mode */
	u2dotgcr = u2d_readl(U2DOTGCR);
	u2dotgcr |= U2DOTGCR_ULAF;
	u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF);
	u2d_writel(U2DOTGCR, u2dotgcr);
}

static int pxa310_start_otg_host_transcvr(struct usb_bus *host)
{
	int err;

	pxa310_otg_transceiver_rtsm();

142
	err = usb_phy_init(u2d->otg);
143 144 145 146 147
	if (err) {
		pr_err("OTG transceiver init failed");
		return err;
	}

148
	err = otg_set_vbus(u2d->otg->otg, 1);
149 150 151 152 153
	if (err) {
		pr_err("OTG transceiver VBUS set failed");
		return err;
	}

154
	err = otg_set_host(u2d->otg->otg, host);
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 191
	if (err)
		pr_err("OTG transceiver Host mode set failed");

	return err;
}

static int pxa310_start_otg_hc(struct usb_bus *host)
{
	u32 u2dotgcr;
	int err;

	/* disable USB device controller */
	u2d_writel(U2DCR, u2d_readl(U2DCR) & ~U2DCR_UDE);
	u2d_writel(U2DOTGCR, u2d_readl(U2DOTGCR) | U2DOTGCR_UTMID);
	u2d_writel(U2DOTGICR, u2d_readl(U2DOTGICR) & ~0x37F7F);

	err = pxa310_start_otg_host_transcvr(host);
	if (err)
		return err;

	/* set xceiver mode */
	if (u2d->ulpi_mode & ULPI_IC_6PIN_SERIAL)
		u2d_writel(U2DP3CR, u2d_readl(U2DP3CR) & ~U2DP3CR_P2SS);
	else if (u2d->ulpi_mode & ULPI_IC_3PIN_SERIAL)
		u2d_writel(U2DP3CR, u2d_readl(U2DP3CR) | U2DP3CR_P2SS);

	/* start OTG host controller */
	u2dotgcr = u2d_readl(U2DOTGCR) | U2DOTGCR_SMAF;
	u2d_writel(U2DOTGCR, u2dotgcr & ~(U2DOTGCR_ULAF | U2DOTGCR_CKAF));

	return 0;
}

static void pxa310_stop_otg_hc(void)
{
	pxa310_otg_transceiver_rtsm();

192 193
	otg_set_host(u2d->otg->otg, NULL);
	otg_set_vbus(u2d->otg->otg, 0);
194
	usb_phy_shutdown(u2d->otg);
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 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
}

static void pxa310_u2d_setup_otg_hc(void)
{
	u32 u2dotgcr;

	u2dotgcr = u2d_readl(U2DOTGCR);
	u2dotgcr |= U2DOTGCR_ULAF | U2DOTGCR_UTMID;
	u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF);
	u2d_writel(U2DOTGCR, u2dotgcr);
	msleep(5);
	u2d_writel(U2DOTGCR, u2dotgcr | U2DOTGCR_ULE);
	msleep(5);
	u2d_writel(U2DOTGICR, u2d_readl(U2DOTGICR) & ~0x37F7F);
}

static int pxa310_otg_init(struct pxa3xx_u2d_platform_data *pdata)
{
	unsigned int ulpi_mode = ULPI_OTG_DRVVBUS;

	if (pdata) {
		if (pdata->ulpi_mode & ULPI_SER_6PIN)
			ulpi_mode |= ULPI_IC_6PIN_SERIAL;
		else if (pdata->ulpi_mode & ULPI_SER_3PIN)
			ulpi_mode |= ULPI_IC_3PIN_SERIAL;
	}

	u2d->ulpi_mode = ulpi_mode;

	u2d->otg = otg_ulpi_create(&pxa310_ulpi_access_ops, ulpi_mode);
	if (!u2d->otg)
		return -ENOMEM;

	u2d->otg->io_priv = u2d->mmio_base;

	return 0;
}

static void pxa310_otg_exit(void)
{
	kfree(u2d->otg);
}
#else
static inline void pxa310_u2d_setup_otg_hc(void) {}
static inline int pxa310_start_otg_hc(struct usb_bus *host)
{
	return 0;
}
static inline void pxa310_stop_otg_hc(void) {}
static inline int pxa310_otg_init(struct pxa3xx_u2d_platform_data *pdata)
{
	return 0;
}
static inline void pxa310_otg_exit(void) {}
#endif /* CONFIG_PXA310_ULPI */

int pxa3xx_u2d_start_hc(struct usb_bus *host)
{
	int err = 0;

255 256 257 258
	/* In case the PXA3xx ULPI isn't used, do nothing. */
	if (!u2d)
		return 0;

259 260 261 262 263 264 265 266 267
	clk_enable(u2d->clk);

	if (cpu_is_pxa310()) {
		pxa310_u2d_setup_otg_hc();
		err = pxa310_start_otg_hc(host);
	}

	return err;
}
268
EXPORT_SYMBOL_GPL(pxa3xx_u2d_start_hc);
269 270 271

void pxa3xx_u2d_stop_hc(struct usb_bus *host)
{
272 273 274 275
	/* In case the PXA3xx ULPI isn't used, do nothing. */
	if (!u2d)
		return;

276 277 278 279 280
	if (cpu_is_pxa310())
		pxa310_stop_otg_hc();

	clk_disable(u2d->clk);
}
281
EXPORT_SYMBOL_GPL(pxa3xx_u2d_stop_hc);
282 283 284 285 286 287 288

static int pxa3xx_u2d_probe(struct platform_device *pdev)
{
	struct pxa3xx_u2d_platform_data *pdata = pdev->dev.platform_data;
	struct resource *r;
	int err;

289
	u2d = kzalloc(sizeof(*u2d), GFP_KERNEL);
290
	if (!u2d)
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
		return -ENOMEM;

	u2d->clk = clk_get(&pdev->dev, NULL);
	if (IS_ERR(u2d->clk)) {
		dev_err(&pdev->dev, "failed to get u2d clock\n");
		err = PTR_ERR(u2d->clk);
		goto err_free_mem;
	}

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r) {
		dev_err(&pdev->dev, "no IO memory resource defined\n");
		err = -ENODEV;
		goto err_put_clk;
	}

        r = request_mem_region(r->start, resource_size(r), pdev->name);
        if (!r) {
                dev_err(&pdev->dev, "failed to request memory resource\n");
                err = -EBUSY;
                goto err_put_clk;
        }

	u2d->mmio_base = ioremap(r->start, resource_size(r));
	if (!u2d->mmio_base) {
		dev_err(&pdev->dev, "ioremap() failed\n");
		err = -ENODEV;
		goto err_free_res;
	}

	if (pdata->init) {
		err = pdata->init(&pdev->dev);
		if (err)
			goto err_free_io;
	}

	/* Only PXA310 U2D has OTG functionality */
	if (cpu_is_pxa310()) {
		err = pxa310_otg_init(pdata);
		if (err)
			goto err_free_plat;
	}

334
	platform_set_drvdata(pdev, u2d);
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383

	return 0;

err_free_plat:
	if (pdata->exit)
		pdata->exit(&pdev->dev);
err_free_io:
	iounmap(u2d->mmio_base);
err_free_res:
	release_mem_region(r->start, resource_size(r));
err_put_clk:
	clk_put(u2d->clk);
err_free_mem:
	kfree(u2d);
	return err;
}

static int pxa3xx_u2d_remove(struct platform_device *pdev)
{
	struct pxa3xx_u2d_platform_data *pdata = pdev->dev.platform_data;
	struct resource *r;

	if (cpu_is_pxa310()) {
		pxa310_stop_otg_hc();
		pxa310_otg_exit();
	}

	if (pdata->exit)
		pdata->exit(&pdev->dev);

	platform_set_drvdata(pdev, NULL);
	iounmap(u2d->mmio_base);
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	release_mem_region(r->start, resource_size(r));

	clk_put(u2d->clk);

	kfree(u2d);

	return 0;
}

static struct platform_driver pxa3xx_u2d_ulpi_driver = {
        .driver		= {
                .name   = "pxa3xx-u2d",
        },
        .probe          = pxa3xx_u2d_probe,
        .remove         = pxa3xx_u2d_remove,
};
384
module_platform_driver(pxa3xx_u2d_ulpi_driver);
385 386 387 388

MODULE_DESCRIPTION("PXA3xx U2D ULPI driver");
MODULE_AUTHOR("Igor Grinberg");
MODULE_LICENSE("GPL v2");