summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorErik Gilling <konkers@android.com>2010-02-09 15:41:00 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:34:25 -0800
commite48bbf84539c73561bba27c4fae4f0c313807817 (patch)
treedd4cff2a16361067cf337de79ffd14f88d93c25e /drivers
parent16f891138b0e2742c14b703c7ad3834db87e61ee (diff)
usb: gadget: add preliminary Tegra support to fsl driver
Based on work by Gary King. Further abstraction of the chipidea core support needs to be done. Signed-off-by: Colin Cross <ccross@android.com> Cc: Erik Gilling <konkers@android.com> Cc: Gary King <GKing@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/gadget/Kconfig2
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/fsl_tegra_udc.c37
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c173
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.h29
5 files changed, 227 insertions, 15 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 6f5049156e17..49b945f96f37 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -141,7 +141,7 @@ config USB_ATMEL_USBA
config USB_FSL_USB2
tristate "Freescale Highspeed USB DR Peripheral Controller"
- depends on FSL_SOC || ARCH_MXC
+ depends on FSL_SOC || ARCH_MXC || ARCH_TEGRA
select USB_GADGET_DUALSPEED
select USB_FSL_MPH_DR_OF if OF
help
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9ba725af4a08..51f7c0f14c6d 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
fsl_usb2_udc-y := fsl_udc_core.o
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
+fsl_usb2_udc-$(CONFIG_ARCH_TEGRA) += fsl_tegra_udc.o
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
diff --git a/drivers/usb/gadget/fsl_tegra_udc.c b/drivers/usb/gadget/fsl_tegra_udc.c
new file mode 100644
index 000000000000..0cb9e371b750
--- /dev/null
+++ b/drivers/usb/gadget/fsl_tegra_udc.c
@@ -0,0 +1,37 @@
+/*
+ * Description:
+ * Helper functions to support the tegra USB controller
+ *
+ * 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.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fsl_devices.h>
+#include <linux/platform_device.h>
+
+int fsl_udc_clk_init(struct platform_device *pdev)
+{
+
+ return 0;
+
+}
+
+void fsl_udc_clk_finalize(struct platform_device *pdev)
+{
+}
+
+void fsl_udc_clk_release(void)
+{
+}
+
+void fsl_udc_clk_suspend(void)
+{
+}
+
+void fsl_udc_clk_resume(void)
+{
+}
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index de24a4233c25..c9b5d7ad8fe4 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -49,13 +49,21 @@
#include "fsl_usb2_udc.h"
+#ifdef CONFIG_ARCH_TEGRA
+#define DRIVER_DESC "NVidia Tegra High-Speed USB SOC Device Controller driver"
+#else
#define DRIVER_DESC "Freescale High-Speed USB SOC Device Controller driver"
+#endif
#define DRIVER_AUTHOR "Li Yang/Jiang Bo"
#define DRIVER_VERSION "Apr 20, 2007"
#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+#ifdef CONFIG_ARCH_TEGRA
+static const char driver_name[] = "fsl-tegra-udc";
+#else
static const char driver_name[] = "fsl-usb2-udc";
+#endif
static const char driver_desc[] = DRIVER_DESC;
static struct usb_dr_device *dr_regs;
@@ -245,7 +253,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
{
unsigned int tmp, portctrl, ep_num;
unsigned int max_no_of_ep;
-#ifndef CONFIG_ARCH_MXC
+#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_ARCH_TEGRA)
unsigned int ctrl;
#endif
unsigned long timeout;
@@ -301,6 +309,19 @@ static int dr_controller_setup(struct fsl_udc *udc)
tmp |= USB_MODE_ES;
fsl_writel(tmp, &dr_regs->usbmode);
+#ifdef CONFIG_ARCH_TEGRA
+ /* Wait for controller to switch to device mode */
+ timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+ while ((fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_DEVICE) !=
+ USB_MODE_CTRL_MODE_DEVICE) {
+ if (time_after(jiffies, timeout)) {
+ ERR("udc device mode setup timeout!\n");
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+#endif
+
/* Clear the setup status */
fsl_writel(0, &dr_regs->usbsts);
@@ -321,7 +342,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
fsl_writel(tmp, &dr_regs->endptctrl[ep_num]);
}
/* Config control enable i/o output, cpu endian register */
-#ifndef CONFIG_ARCH_MXC
+#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_ARCH_TEGRA)
if (udc->pdata->have_sysif_regs) {
ctrl = __raw_readl(&usb_sys_regs->control);
ctrl |= USB_CTRL_IOENB;
@@ -349,6 +370,20 @@ static int dr_controller_setup(struct fsl_udc *udc)
static void dr_controller_run(struct fsl_udc *udc)
{
u32 temp;
+#ifdef CONFIG_ARCH_TEGRA
+ unsigned long timeout;
+#define FSL_UDC_RUN_TIMEOUT 1000
+#endif
+
+#ifdef CONFIG_ARCH_TEGRA
+ /* Enable cable detection interrupt, without setting the
+ * USB_SYS_VBUS_WAKEUP_INT bit. USB_SYS_VBUS_WAKEUP_INT is
+ * clear on write */
+ temp = fsl_readl(&usb_sys_regs->vbus_wakeup);
+ temp |= (USB_SYS_VBUS_WAKEUP_INT_ENABLE | USB_SYS_VBUS_WAKEUP_ENABLE);
+ temp &= ~USB_SYS_VBUS_WAKEUP_INT_STATUS;
+ fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
+#endif
/* Enable DR irq reg */
temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
@@ -369,6 +404,21 @@ static void dr_controller_run(struct fsl_udc *udc)
temp = fsl_readl(&dr_regs->usbcmd);
temp |= USB_CMD_RUN_STOP;
fsl_writel(temp, &dr_regs->usbcmd);
+
+#ifdef CONFIG_ARCH_TEGRA
+ /* Wait for controller to start */
+ timeout = jiffies + FSL_UDC_RUN_TIMEOUT;
+ while ((fsl_readl(&dr_regs->usbcmd) & USB_CMD_RUN_STOP) !=
+ USB_CMD_RUN_STOP) {
+ if (time_after(jiffies, timeout)) {
+ ERR("udc start timeout!\n");
+ return;
+ }
+ cpu_relax();
+ }
+#endif
+
+ return;
}
static void dr_controller_stop(struct fsl_udc *udc)
@@ -641,16 +691,22 @@ static int fsl_ep_disable(struct usb_ep *_ep)
}
/* disable ep on controller */
- ep_num = ep_index(ep);
- epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
- if (ep_is_in(ep)) {
- epctrl &= ~(EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE);
- epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT;
- } else {
- epctrl &= ~(EPCTRL_RX_ENABLE | EPCTRL_TX_TYPE);
- epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT;
+#ifdef CONFIG_ARCH_TEGRA
+ /* Touch the registers if cable is connected and phy is on */
+ if (fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS)
+#endif
+ {
+ ep_num = ep_index(ep);
+ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
+ if (ep_is_in(ep)) {
+ epctrl &= ~(EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE);
+ epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT;
+ } else {
+ epctrl &= ~(EPCTRL_RX_ENABLE | EPCTRL_TX_TYPE);
+ epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT;
+ }
+ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
}
- fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
udc = (struct fsl_udc *)ep->udc;
spin_lock_irqsave(&udc->lock, flags);
@@ -1100,6 +1156,12 @@ static void fsl_ep_fifo_flush(struct usb_ep *_ep)
unsigned long timeout;
#define FSL_UDC_FLUSH_TIMEOUT 1000
+#ifdef CONFIG_ARCH_TEGRA
+ /* Touch the registers if cable is connected and phy is on */
+ if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS))
+ return;
+#endif
+
if (!_ep) {
return;
} else {
@@ -1677,7 +1739,12 @@ static void dtd_complete_irq(struct fsl_udc *udc)
if (!bit_pos)
return;
+#ifdef CONFIG_ARCH_TEGRA
+ /* XXX what's going on here */
+ for (i = 0; i < udc->max_ep; i++) {
+#else
for (i = 0; i < udc->max_ep * 2; i++) {
+#endif
ep_num = i >> 1;
direction = i % 2;
@@ -1828,6 +1895,15 @@ static void reset_irq(struct fsl_udc *udc)
/* Write 1s to the flush register */
fsl_writel(0xffffffff, &dr_regs->endptflush);
+#if defined(CONFIG_ARCH_TEGRA)
+ /* When the bus reset is seen on Tegra, the PORTSCX_PORT_RESET bit
+ * is not set */
+ VDBG("Bus reset");
+ /* Reset all the queues, include XD, dTD, EP queue
+ * head and TR Queue */
+ reset_queues(udc);
+ udc->usb_state = USB_STATE_DEFAULT;
+#else
if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
VDBG("Bus reset");
/* Bus is reseting */
@@ -1851,6 +1927,7 @@ static void reset_irq(struct fsl_udc *udc)
dr_controller_run(udc);
udc->usb_state = USB_STATE_ATTACHED;
}
+#endif
}
/*
@@ -1862,6 +1939,9 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
u32 irq_src;
irqreturn_t status = IRQ_NONE;
unsigned long flags;
+#if defined(CONFIG_ARCH_TEGRA)
+ u32 temp;
+#endif
/* Disable ISR for OTG host mode */
if (udc->stopped)
@@ -1872,6 +1952,34 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
fsl_writel(irq_src, &dr_regs->usbsts);
/* VDBG("irq_src [0x%8x]", irq_src); */
+#if defined(CONFIG_ARCH_TEGRA)
+ /* VBUS A session detection interrupts. When the interrupt is received,
+ * the mark the vbus active shadow.
+ */
+ temp = fsl_readl(&usb_sys_regs->vbus_sensors);
+ if (temp & USB_SYS_VBUS_ASESSION_CHANGED) {
+ if (temp & USB_SYS_VBUS_ASESSION) {
+ udc->vbus_active = 1;
+ } else {
+ reset_queues(udc);
+ udc->vbus_active = 0;
+ udc->usb_state = USB_STATE_DEFAULT;
+ }
+ /* write back the register to clear the interrupt */
+ fsl_writel(temp, &usb_sys_regs->vbus_wakeup);
+
+#if 0
+ /* XXX */
+ if (udc->vbus_active) {
+ fsl_udc_clk_resume();
+ } else {
+ fsl_udc_clk_suspend();
+ }
+#endif
+
+ status = IRQ_HANDLED;
+ }
+#endif
/* Need to resume? */
if (udc->usb_state == USB_STATE_SUSPENDED)
@@ -2053,7 +2161,11 @@ static int fsl_stop(struct usb_gadget_driver *driver)
#include <linux/seq_file.h>
+#ifdef CONFIG_ARCH_TEGRA
+static const char proc_filename[] = "driver/fsl_tegra_udc";
+#else
static const char proc_filename[] = "driver/fsl_usb2_udc";
+#endif
static int fsl_proc_read(char *page, char **start, off_t off, int count,
int *eof, void *_dev)
@@ -2235,7 +2347,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,
size -= t;
next += t;
-#ifndef CONFIG_ARCH_MXC
+#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_ARCH_TEGRA)
if (udc->pdata->have_sysif_regs) {
tmp_reg = usb_sys_regs->snoop1;
t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);
@@ -2352,6 +2464,13 @@ static int __init struct_udc_setup(struct fsl_udc *udc,
return -1;
}
+#ifdef CONFIG_ARCH_TEGRA
+ /* Tegra uses hardware queue heads */
+ size = udc->max_ep * sizeof(struct ep_queue_head);
+ udc->ep_qh = (struct ep_queue_head *)((u8 *)dr_regs + QH_OFFSET);
+ udc->ep_qh_dma = platform_get_resource(pdev, IORESOURCE_MEM, 0)->start +
+ QH_OFFSET;
+#else
/* initialized QHs, take care of alignment */
size = udc->max_ep * sizeof(struct ep_queue_head);
if (size < QH_ALIGNMENT)
@@ -2367,6 +2486,7 @@ static int __init struct_udc_setup(struct fsl_udc *udc,
kfree(udc->eps);
return -1;
}
+#endif
udc->ep_qh_size = size;
@@ -2431,6 +2551,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
int ret = -ENODEV;
unsigned int i;
u32 dccparams;
+#if defined(CONFIG_ARCH_TEGRA)
+ struct resource *res_sys = NULL;
+#endif
if (strcmp(pdev->name, driver_name)) {
VDBG("Wrong device");
@@ -2493,7 +2616,21 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
/* Set accessors only after pdata->init() ! */
fsl_set_accessors(pdata);
-#ifndef CONFIG_ARCH_MXC
+#if defined(CONFIG_ARCH_TEGRA)
+ /* If the PHY registers are NOT provided as a seperate aperture, then
+ * we should be using the registers inside the controller aperture. */
+ res_sys = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res_sys) {
+ usb_sys_regs = ioremap(res_sys->start, resource_size(res_sys));
+ if (!usb_sys_regs)
+ goto err_release_mem_region;
+ } else {
+ usb_sys_regs = (struct usb_sys_interface *)
+ ((u32)dr_regs + USB_DR_SYS_OFFSET);
+ }
+#endif
+
+#if !defined(CONFIG_ARCH_MXC) && !defined(CONFIG_ARCH_TEGRA)
if (pdata->have_sysif_regs)
usb_sys_regs = (struct usb_sys_interface *)
((u32)dr_regs + USB_DR_SYS_OFFSET);
@@ -2599,6 +2736,16 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
goto err_del_udc;
create_proc_file();
+
+#if 0
+/* XXX */
+#ifdef CONFIG_ARCH_TEGRA
+ /* Power down the phy if cable is not connected */
+ if (!(fsl_readl(&usb_sys_regs->vbus_wakeup) & USB_SYS_VBUS_STATUS))
+ platform_udc_clk_suspend();
+#endif
+#endif
+
return 0;
err_del_udc:
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
index 1d51be83fda8..bb10c5f6924f 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/fsl_usb2_udc.h
@@ -84,6 +84,15 @@ struct usb_dr_host {
};
/* non-EHCI USB system interface registers (Big Endian) */
+#ifdef CONFIG_ARCH_TEGRA
+struct usb_sys_interface {
+ u32 suspend_ctrl;
+ u32 vbus_sensors;
+ u32 vbus_wakeup;
+ u32 vbus_alt_status;
+ u32 legacy_ctrl;
+};
+#else
struct usb_sys_interface {
u32 snoop1;
u32 snoop2;
@@ -93,6 +102,7 @@ struct usb_sys_interface {
u8 res[236];
u32 control; /* General Purpose Control Register */
};
+#endif
/* ep0 transfer state */
#define WAIT_FOR_SETUP 0
@@ -422,10 +432,19 @@ struct ep_td_struct {
/* Alignment requirements; must be a power of two */
#define DTD_ALIGNMENT 0x20
#define QH_ALIGNMENT 2048
+#define QH_OFFSET 0x1000
/* Controller dma boundary */
#define UDC_DMA_BOUNDARY 0x1000
+#define USB_SYS_VBUS_ASESSION_INT_EN 0x10000
+#define USB_SYS_VBUS_ASESSION_CHANGED 0x20000
+#define USB_SYS_VBUS_ASESSION 0x40000
+#define USB_SYS_VBUS_WAKEUP_ENABLE 0x40000000
+#define USB_SYS_VBUS_WAKEUP_INT_ENABLE 0x100
+#define USB_SYS_VBUS_WAKEUP_INT_STATUS 0x200
+#define USB_SYS_VBUS_STATUS 0x400
+
/*-------------------------------------------------------------------------*/
/* ### driver private data
@@ -570,10 +589,12 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP))
struct platform_device;
-#ifdef CONFIG_ARCH_MXC
+#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_TEGRA)
int fsl_udc_clk_init(struct platform_device *pdev);
void fsl_udc_clk_finalize(struct platform_device *pdev);
void fsl_udc_clk_release(void);
+void fsl_udc_clk_suspend(void);
+void fsl_udc_clk_resume(void);
#else
static inline int fsl_udc_clk_init(struct platform_device *pdev)
{
@@ -585,6 +606,12 @@ static inline void fsl_udc_clk_finalize(struct platform_device *pdev)
static inline void fsl_udc_clk_release(void)
{
}
+static inline void fsl_udc_clk_suspend(void)
+{
+}
+static inline void fsl_udc_clk_resume(void)
+{
+}
#endif
#endif