From e48bbf84539c73561bba27c4fae4f0c313807817 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Tue, 9 Feb 2010 15:41:00 -0800 Subject: 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 Cc: Erik Gilling Cc: Gary King --- drivers/usb/gadget/Kconfig | 2 +- drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/fsl_tegra_udc.c | 37 ++++++++ drivers/usb/gadget/fsl_udc_core.c | 173 ++++++++++++++++++++++++++++++++++--- drivers/usb/gadget/fsl_usb2_udc.h | 29 ++++++- 5 files changed, 227 insertions(+), 15 deletions(-) create mode 100644 drivers/usb/gadget/fsl_tegra_udc.c (limited to 'drivers') 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 +#include +#include +#include +#include + +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 +#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 -- cgit v1.2.3