/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/pm_runtime.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
#include <linux/usb/gadget.h>
#include <linux/usb/hcd.h>
#include <linux/usb/msm_hsusb.h>
#include <linux/usb/msm_hsusb_hw.h>
#include <mach/clk.h>
#define MSM_USB_BASE (motg->regs)
#define DRIVER_NAME "msm_otg"
#define ULPI_IO_TIMEOUT_USEC (10 * 1000)
static int ulpi_read(struct otg_transceiver *otg, u32 reg)
{
struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
int cnt = 0;
/* initiate read operation */
writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
USB_ULPI_VIEWPORT);
/* wait for completion */
while (cnt < ULPI_IO_TIMEOUT_USEC) {
if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN))
break;
udelay(1);
cnt++;
}
if (cnt >= ULPI_IO_TIMEOUT_USEC) {
dev_err(otg->dev, "ulpi_read: timeout %08x\n",
readl(USB_ULPI_VIEWPORT));
return -ETIMEDOUT;
}
return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
}
static int ulpi_write(struct otg_transceiver *otg, u32 val, u32 reg)
{
struct msm_otg *motg = container_of(otg, struct msm_otg, otg);
int cnt = 0;
/* initiate write operation */
writel(ULPI_RUN | ULPI_WRITE |
ULPI_ADDR(reg) | ULPI_DATA(val),
USB_ULPI_VIEWPORT);
/* wait for completion */
while (cnt < ULPI_IO_TIMEOUT_USEC) {
if (!(readl(USB_ULPI_VIEWPORT) & ULPI_RUN))
break;
udelay(1);
cnt++;
}
if (cnt >= ULPI_IO_TIMEOUT_USEC) {
dev_err(otg->dev, "ulpi_write: timeout\n");
return -ETIMEDOUT;
}
return 0;
}
static struct otg_io_access_ops msm_otg_io_ops = {
.read = ulpi_read,
.write = ulpi_write,
};
static void ulpi_init(struct msm_otg *motg)
{
struct msm_otg_platform_data *pdata = motg->pdata;
int *seq = pdata->phy_init_seq;
if (!seq)
return;
while (seq[0] >= 0) {
dev_vdbg(motg->otg.dev, "ulpi: write 0x%02x to 0x%02x\n",
seq[0], seq[1]);
ulpi_write(&motg->otg, seq[0], seq[1]);
seq += 2;
}
}
static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert)
{
int ret;
if (assert) {
ret = clk_reset(motg->clk, CLK_RESET_ASSERT);
if (ret)
dev_err(motg->otg.dev, "usb hs_clk assert failed\n");
} else {
ret = clk_reset(motg->clk, CLK_RESET_DEASSERT);
if (ret)
dev_err(motg->otg.d