blob: 55b8975ce27720bbf26e7f2149b9d1b93ed7ce54 [file] [log] [blame]
/*
* arch/arm/plat-ambarella/generic/udc.c
*
* Author: Anthony Ginger <hfjiang@ambarella.com>
*
* Copyright (C) 2004-2010, Ambarella, Inc.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <mach/hardware.h>
#include <plat/udc.h>
/* amb_udc_status will be modified in ambarella_udc.c */
enum ambarella_udc_status amb_udc_status = AMBARELLA_UDC_STATUS_UNKNOWN;
EXPORT_SYMBOL(amb_udc_status);
/* ==========================================================================*/
struct resource ambarella_udc_resources[] = {
[0] = {
.start = USBDC_BASE,
.end = USBDC_BASE + 0x1FFF,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = USBC_IRQ,
.end = USBC_IRQ,
.flags = IORESOURCE_IRQ,
},
};
static void init_usb(void)
{
_init_usb_pll();
}
static void reset_usb(void)
{
#if (CHIP_REV == A5S)||(CHIP_REV == A7)||(CHIP_REV == I1)
rct_usb_reset();
#endif
}
static int flush_rxfifo(void)
{
int retry_count = 1000;
int rval = 0;
#if (CHIP_REV == A2) || (CHIP_REV == A3)
retry_count = retry_count * 10;
if (!(amba_readl(USB_DEV_STS_REG) & USB_DEV_RXFIFO_EMPTY_STS)){
/* Switch to slave mode */
amba_clrbitsl(USB_DEV_CTRL_REG, USB_DEV_DMA_MD);
while (!(amba_readl(USB_DEV_STS_REG) & USB_DEV_RXFIFO_EMPTY_STS)) {
if (retry_count-- < 0) {
printk (KERN_ERR "%s: failed", __func__);
rval = -1;
break;
}
amba_readl (USB_RXFIFO_BASE);
udelay(5);
}
/* Switch to DMA mode */
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_DMA_MD);
}
#else
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_NAK);
amba_setbitsl(USB_DEV_CTRL_REG, USB_DEV_FLUSH_RXFIFO);
while(!(amba_readl(USB_DEV_STS_REG) & USB_DEV_RXFIFO_EMPTY_STS)) {
if (retry_count-- < 0) {
printk (KERN_ERR "%s: failed", __func__);
rval = -1;
break;
}
udelay(5);
}
amba_clrbitsl(USB_DEV_CTRL_REG, USB_DEV_NAK);
#endif
return rval;
}
static struct ambarella_udc_controller ambarella_platform_udc_controller0 = {
.vbus_polled = 1, /* need to poll vbus(usb cable) status */
.init_pll = init_usb,
.reset_usb = reset_usb,
.flush_rxfifo = flush_rxfifo,
};
struct platform_device ambarella_udc0 = {
.name = "ambarella-udc",
.id = -1,
.resource = ambarella_udc_resources,
.num_resources = ARRAY_SIZE(ambarella_udc_resources),
.dev = {
.platform_data = &ambarella_platform_udc_controller0,
.dma_mask = &ambarella_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
}
};