2011-03-10 03:28:56 +03:00
/*
* EHCI - compliant USB host controller driver for NVIDIA Tegra SoCs
*
* Copyright ( C ) 2010 Google , Inc .
2013-01-18 00:15:37 +04:00
* Copyright ( C ) 2009 - 2013 NVIDIA Corporation
2011-03-10 03:28:56 +03:00
*
* 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 .
*
*/
# include <linux/clk.h>
2013-06-13 21:24:12 +04:00
# include <linux/clk/tegra.h>
# include <linux/dma-mapping.h>
2012-06-26 16:10:32 +04:00
# include <linux/err.h>
2011-11-04 13:12:40 +04:00
# include <linux/gpio.h>
2013-06-13 21:24:12 +04:00
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/module.h>
2011-11-04 13:12:40 +04:00
# include <linux/of.h>
# include <linux/of_gpio.h>
2013-06-13 21:24:12 +04:00
# include <linux/platform_device.h>
# include <linux/platform_data/tegra_usb.h>
2012-05-01 19:28:49 +04:00
# include <linux/pm_runtime.h>
2013-06-13 21:24:12 +04:00
# include <linux/slab.h>
2013-01-18 00:15:37 +04:00
# include <linux/usb/ehci_def.h>
2012-09-05 17:20:23 +04:00
# include <linux/usb/tegra_usb_phy.h>
2013-06-13 21:24:12 +04:00
# include <linux/usb.h>
# include <linux/usb/hcd.h>
# include <linux/usb/otg.h>
# include "ehci.h"
2012-10-03 02:49:25 +04:00
# define TEGRA_USB_BASE 0xC5000000
# define TEGRA_USB2_BASE 0xC5004000
# define TEGRA_USB3_BASE 0xC5008000
2011-03-10 03:28:56 +03:00
2013-06-13 21:24:12 +04:00
# define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
2011-03-10 03:28:57 +03:00
# define TEGRA_USB_DMA_ALIGN 32
2013-06-13 21:24:12 +04:00
# define DRIVER_DESC "Tegra EHCI driver"
# define DRV_NAME "tegra-ehci"
static struct hc_driver __read_mostly tegra_ehci_hc_driver ;
static int ( * orig_hub_control ) ( struct usb_hcd * hcd ,
u16 typeReq , u16 wValue , u16 wIndex ,
char * buf , u16 wLength ) ;
2011-03-10 03:28:56 +03:00
struct tegra_ehci_hcd {
struct tegra_usb_phy * phy ;
struct clk * clk ;
2012-02-13 15:24:02 +04:00
struct usb_phy * transceiver ;
2011-03-10 03:28:56 +03:00
int port_resuming ;
2012-12-14 00:59:08 +04:00
bool needs_double_reset ;
2011-03-10 03:28:56 +03:00
enum tegra_usb_phy_port_speed port_speed ;
} ;
2011-04-17 12:58:25 +04:00
static int tegra_ehci_internal_port_reset (
struct ehci_hcd * ehci ,
u32 __iomem * portsc_reg
)
{
u32 temp ;
unsigned long flags ;
int retval = 0 ;
int i , tries ;
u32 saved_usbintr ;
spin_lock_irqsave ( & ehci - > lock , flags ) ;
saved_usbintr = ehci_readl ( ehci , & ehci - > regs - > intr_enable ) ;
/* disable USB interrupt */
ehci_writel ( ehci , 0 , & ehci - > regs - > intr_enable ) ;
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
/*
* Here we have to do Port Reset at most twice for
* Port Enable bit to be set .
*/
for ( i = 0 ; i < 2 ; i + + ) {
temp = ehci_readl ( ehci , portsc_reg ) ;
temp | = PORT_RESET ;
ehci_writel ( ehci , temp , portsc_reg ) ;
mdelay ( 10 ) ;
temp & = ~ PORT_RESET ;
ehci_writel ( ehci , temp , portsc_reg ) ;
mdelay ( 1 ) ;
tries = 100 ;
do {
mdelay ( 1 ) ;
/*
* Up to this point , Port Enable bit is
* expected to be set after 2 ms waiting .
* USB1 usually takes extra 45 ms , for safety ,
* we take 100 ms as timeout .
*/
temp = ehci_readl ( ehci , portsc_reg ) ;
} while ( ! ( temp & PORT_PE ) & & tries - - ) ;
if ( temp & PORT_PE )
break ;
}
if ( i = = 2 )
retval = - ETIMEDOUT ;
/*
* Clear Connect Status Change bit if it ' s set .
* We can ' t clear PORT_PEC . It will also cause PORT_PE to be cleared .
*/
if ( temp & PORT_CSC )
ehci_writel ( ehci , PORT_CSC , portsc_reg ) ;
/*
* Write to clear any interrupt status bits that might be set
* during port reset .
*/
temp = ehci_readl ( ehci , & ehci - > regs - > status ) ;
ehci_writel ( ehci , temp , & ehci - > regs - > status ) ;
/* restore original interrupt enable bits */
ehci_writel ( ehci , saved_usbintr , & ehci - > regs - > intr_enable ) ;
return retval ;
}
2011-03-10 03:28:56 +03:00
static int tegra_ehci_hub_control (
struct usb_hcd * hcd ,
u16 typeReq ,
u16 wValue ,
u16 wIndex ,
char * buf ,
u16 wLength
)
{
2013-06-13 21:24:13 +04:00
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
struct tegra_ehci_hcd * tegra = ( struct tegra_ehci_hcd * ) ehci - > priv ;
2011-03-10 03:28:56 +03:00
u32 __iomem * status_reg ;
u32 temp ;
unsigned long flags ;
int retval = 0 ;
status_reg = & ehci - > regs - > port_status [ ( wIndex & 0xff ) - 1 ] ;
spin_lock_irqsave ( & ehci - > lock , flags ) ;
2012-04-19 01:32:46 +04:00
if ( typeReq = = GetPortStatus ) {
2011-03-10 03:28:56 +03:00
temp = ehci_readl ( ehci , status_reg ) ;
if ( tegra - > port_resuming & & ! ( temp & PORT_SUSPEND ) ) {
/* Resume completed, re-enable disconnect detection */
tegra - > port_resuming = 0 ;
2013-01-24 14:27:03 +04:00
tegra_usb_phy_postresume ( hcd - > phy ) ;
2011-03-10 03:28:56 +03:00
}
}
else if ( typeReq = = SetPortFeature & & wValue = = USB_PORT_FEAT_SUSPEND ) {
temp = ehci_readl ( ehci , status_reg ) ;
if ( ( temp & PORT_PE ) = = 0 | | ( temp & PORT_RESET ) ! = 0 ) {
retval = - EPIPE ;
goto done ;
}
2012-04-25 22:31:10 +04:00
temp & = ~ ( PORT_RWC_BITS | PORT_WKCONN_E ) ;
2011-03-10 03:28:56 +03:00
temp | = PORT_WKDISC_E | PORT_WKOC_E ;
ehci_writel ( ehci , temp | PORT_SUSPEND , status_reg ) ;
/*
* If a transaction is in progress , there may be a delay in
* suspending the port . Poll until the port is suspended .
*/
2013-06-13 21:24:09 +04:00
if ( ehci_handshake ( ehci , status_reg , PORT_SUSPEND ,
2011-03-10 03:28:56 +03:00
PORT_SUSPEND , 5000 ) )
pr_err ( " %s: timeout waiting for SUSPEND \n " , __func__ ) ;
set_bit ( ( wIndex & 0xff ) - 1 , & ehci - > suspended_ports ) ;
goto done ;
}
2011-04-17 12:58:25 +04:00
/* For USB1 port we need to issue Port Reset twice internally */
2012-12-14 00:59:08 +04:00
if ( tegra - > needs_double_reset & &
2011-04-17 12:58:25 +04:00
( typeReq = = SetPortFeature & & wValue = = USB_PORT_FEAT_RESET ) ) {
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
return tegra_ehci_internal_port_reset ( ehci , status_reg ) ;
}
2011-03-10 03:28:56 +03:00
/*
* Tegra host controller will time the resume operation to clear the bit
* when the port control state switches to HS or FS Idle . This behavior
* is different from EHCI where the host controller driver is required
* to set this bit to a zero after the resume duration is timed in the
* driver .
*/
else if ( typeReq = = ClearPortFeature & &
wValue = = USB_PORT_FEAT_SUSPEND ) {
temp = ehci_readl ( ehci , status_reg ) ;
if ( ( temp & PORT_RESET ) | | ! ( temp & PORT_PE ) ) {
retval = - EPIPE ;
goto done ;
}
if ( ! ( temp & PORT_SUSPEND ) )
goto done ;
/* Disable disconnect detection during port resume */
2013-01-24 14:27:03 +04:00
tegra_usb_phy_preresume ( hcd - > phy ) ;
2011-03-10 03:28:56 +03:00
ehci - > reset_done [ wIndex - 1 ] = jiffies + msecs_to_jiffies ( 25 ) ;
temp & = ~ ( PORT_RWC_BITS | PORT_WAKE_BITS ) ;
/* start resume signalling */
ehci_writel ( ehci , temp | PORT_RESUME , status_reg ) ;
2012-04-03 23:24:30 +04:00
set_bit ( wIndex - 1 , & ehci - > resuming_ports ) ;
2011-03-10 03:28:56 +03:00
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
msleep ( 20 ) ;
spin_lock_irqsave ( & ehci - > lock , flags ) ;
/* Poll until the controller clears RESUME and SUSPEND */
2013-06-13 21:24:09 +04:00
if ( ehci_handshake ( ehci , status_reg , PORT_RESUME , 0 , 2000 ) )
2011-03-10 03:28:56 +03:00
pr_err ( " %s: timeout waiting for RESUME \n " , __func__ ) ;
2013-06-13 21:24:09 +04:00
if ( ehci_handshake ( ehci , status_reg , PORT_SUSPEND , 0 , 2000 ) )
2011-03-10 03:28:56 +03:00
pr_err ( " %s: timeout waiting for SUSPEND \n " , __func__ ) ;
ehci - > reset_done [ wIndex - 1 ] = 0 ;
2012-04-03 23:24:30 +04:00
clear_bit ( wIndex - 1 , & ehci - > resuming_ports ) ;
2011-03-10 03:28:56 +03:00
tegra - > port_resuming = 1 ;
goto done ;
}
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
/* Handle the hub control events here */
2013-06-13 21:24:12 +04:00
return orig_hub_control ( hcd , typeReq , wValue , wIndex , buf , wLength ) ;
2011-03-10 03:28:56 +03:00
done :
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
return retval ;
}
2012-04-05 09:55:30 +04:00
struct dma_aligned_buffer {
2011-03-10 03:28:57 +03:00
void * kmalloc_ptr ;
void * old_xfer_buffer ;
u8 data [ 0 ] ;
} ;
2012-04-05 09:55:30 +04:00
static void free_dma_aligned_buffer ( struct urb * urb )
2011-03-10 03:28:57 +03:00
{
2012-04-05 09:55:30 +04:00
struct dma_aligned_buffer * temp ;
2011-03-10 03:28:57 +03:00
if ( ! ( urb - > transfer_flags & URB_ALIGNED_TEMP_BUFFER ) )
return ;
2012-04-05 09:55:30 +04:00
temp = container_of ( urb - > transfer_buffer ,
struct dma_aligned_buffer , data ) ;
2011-03-10 03:28:57 +03:00
2012-04-05 09:55:30 +04:00
if ( usb_urb_dir_in ( urb ) )
2011-03-10 03:28:57 +03:00
memcpy ( temp - > old_xfer_buffer , temp - > data ,
urb - > transfer_buffer_length ) ;
urb - > transfer_buffer = temp - > old_xfer_buffer ;
kfree ( temp - > kmalloc_ptr ) ;
urb - > transfer_flags & = ~ URB_ALIGNED_TEMP_BUFFER ;
}
2012-04-05 09:55:30 +04:00
static int alloc_dma_aligned_buffer ( struct urb * urb , gfp_t mem_flags )
2011-03-10 03:28:57 +03:00
{
2012-04-05 09:55:30 +04:00
struct dma_aligned_buffer * temp , * kmalloc_ptr ;
2011-03-10 03:28:57 +03:00
size_t kmalloc_size ;
if ( urb - > num_sgs | | urb - > sg | |
urb - > transfer_buffer_length = = 0 | |
! ( ( uintptr_t ) urb - > transfer_buffer & ( TEGRA_USB_DMA_ALIGN - 1 ) ) )
return 0 ;
/* Allocate a buffer with enough padding for alignment */
kmalloc_size = urb - > transfer_buffer_length +
2012-04-05 09:55:30 +04:00
sizeof ( struct dma_aligned_buffer ) + TEGRA_USB_DMA_ALIGN - 1 ;
2011-03-10 03:28:57 +03:00
kmalloc_ptr = kmalloc ( kmalloc_size , mem_flags ) ;
if ( ! kmalloc_ptr )
return - ENOMEM ;
2012-04-05 09:55:30 +04:00
/* Position our struct dma_aligned_buffer such that data is aligned */
2011-03-10 03:28:57 +03:00
temp = PTR_ALIGN ( kmalloc_ptr + 1 , TEGRA_USB_DMA_ALIGN ) - 1 ;
temp - > kmalloc_ptr = kmalloc_ptr ;
temp - > old_xfer_buffer = urb - > transfer_buffer ;
2012-04-05 09:55:30 +04:00
if ( usb_urb_dir_out ( urb ) )
2011-03-10 03:28:57 +03:00
memcpy ( temp - > data , urb - > transfer_buffer ,
urb - > transfer_buffer_length ) ;
urb - > transfer_buffer = temp - > data ;
urb - > transfer_flags | = URB_ALIGNED_TEMP_BUFFER ;
return 0 ;
}
static int tegra_ehci_map_urb_for_dma ( struct usb_hcd * hcd , struct urb * urb ,
gfp_t mem_flags )
{
int ret ;
2012-04-05 09:55:30 +04:00
ret = alloc_dma_aligned_buffer ( urb , mem_flags ) ;
2011-03-10 03:28:57 +03:00
if ( ret )
return ret ;
ret = usb_hcd_map_urb_for_dma ( hcd , urb , mem_flags ) ;
if ( ret )
2012-04-05 09:55:30 +04:00
free_dma_aligned_buffer ( urb ) ;
2011-03-10 03:28:57 +03:00
return ret ;
}
static void tegra_ehci_unmap_urb_for_dma ( struct usb_hcd * hcd , struct urb * urb )
{
usb_hcd_unmap_urb_for_dma ( hcd , urb ) ;
2012-04-05 09:55:30 +04:00
free_dma_aligned_buffer ( urb ) ;
2011-03-10 03:28:57 +03:00
}
2012-03-17 02:06:07 +04:00
static int setup_vbus_gpio ( struct platform_device * pdev ,
struct tegra_ehci_platform_data * pdata )
2011-11-04 13:12:40 +04:00
{
int err = 0 ;
int gpio ;
2012-03-17 02:06:07 +04:00
gpio = pdata - > vbus_gpio ;
if ( ! gpio_is_valid ( gpio ) )
gpio = of_get_named_gpio ( pdev - > dev . of_node ,
" nvidia,vbus-gpio " , 0 ) ;
2011-11-04 13:12:40 +04:00
if ( ! gpio_is_valid ( gpio ) )
return 0 ;
err = gpio_request ( gpio , " vbus_gpio " ) ;
if ( err ) {
dev_err ( & pdev - > dev , " can't request vbus gpio %d " , gpio ) ;
return err ;
}
err = gpio_direction_output ( gpio , 1 ) ;
if ( err ) {
dev_err ( & pdev - > dev , " can't enable vbus \n " ) ;
return err ;
}
return err ;
}
2011-03-10 03:28:56 +03:00
static int tegra_ehci_probe ( struct platform_device * pdev )
{
struct resource * res ;
struct usb_hcd * hcd ;
2013-06-13 21:24:13 +04:00
struct ehci_hcd * ehci ;
2011-03-10 03:28:56 +03:00
struct tegra_ehci_hcd * tegra ;
struct tegra_ehci_platform_data * pdata ;
int err = 0 ;
int irq ;
2013-05-16 18:13:02 +04:00
struct device_node * np_phy ;
2013-01-18 00:15:37 +04:00
struct usb_phy * u_phy ;
2011-03-10 03:28:56 +03:00
pdata = pdev - > dev . platform_data ;
if ( ! pdata ) {
dev_err ( & pdev - > dev , " Platform data missing \n " ) ;
return - EINVAL ;
}
2011-11-04 13:12:40 +04:00
/* Right now device-tree probed devices don't get dma_mask set.
* Since shared usb code relies on it , set it here for now .
* Once we have dma capability bindings this can go away .
*/
if ( ! pdev - > dev . dma_mask )
2013-05-08 02:53:52 +04:00
pdev - > dev . dma_mask = & pdev - > dev . coherent_dma_mask ;
if ( ! pdev - > dev . coherent_dma_mask )
pdev - > dev . coherent_dma_mask = DMA_BIT_MASK ( 32 ) ;
2011-11-04 13:12:40 +04:00
2012-03-17 02:06:07 +04:00
setup_vbus_gpio ( pdev , pdata ) ;
2011-11-04 13:12:40 +04:00
2013-06-13 21:24:13 +04:00
hcd = usb_create_hcd ( & tegra_ehci_hc_driver , & pdev - > dev ,
dev_name ( & pdev - > dev ) ) ;
if ( ! hcd ) {
dev_err ( & pdev - > dev , " Unable to create HCD \n " ) ;
err = - ENOMEM ;
goto cleanup_vbus_gpio ;
}
platform_set_drvdata ( pdev , hcd ) ;
ehci = hcd_to_ehci ( hcd ) ;
tegra = ( struct tegra_ehci_hcd * ) ehci - > priv ;
hcd - > has_tt = 1 ;
2011-03-10 03:28:56 +03:00
2012-07-30 18:43:41 +04:00
tegra - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2011-03-10 03:28:56 +03:00
if ( IS_ERR ( tegra - > clk ) ) {
dev_err ( & pdev - > dev , " Can't get ehci clock \n " ) ;
2013-06-13 21:24:13 +04:00
err = PTR_ERR ( tegra - > clk ) ;
goto cleanup_hcd_create ;
2011-03-10 03:28:56 +03:00
}
2012-06-05 08:29:38 +04:00
err = clk_prepare_enable ( tegra - > clk ) ;
2011-03-10 03:28:56 +03:00
if ( err )
2013-06-13 21:24:13 +04:00
goto cleanup_clk_get ;
2011-03-10 03:28:56 +03:00
2013-04-03 14:41:12 +04:00
tegra_periph_reset_assert ( tegra - > clk ) ;
udelay ( 1 ) ;
tegra_periph_reset_deassert ( tegra - > clk ) ;
2013-05-16 18:13:02 +04:00
np_phy = of_parse_phandle ( pdev - > dev . of_node , " nvidia,phy " , 0 ) ;
if ( ! np_phy ) {
err = - ENODEV ;
2013-06-13 21:24:13 +04:00
goto cleanup_clk_en ;
2013-05-16 18:13:02 +04:00
}
u_phy = tegra_usb_get_phy ( np_phy ) ;
if ( IS_ERR ( u_phy ) ) {
err = PTR_ERR ( u_phy ) ;
2013-06-13 21:24:13 +04:00
goto cleanup_clk_en ;
2013-05-16 18:13:02 +04:00
}
2013-06-13 21:24:13 +04:00
hcd - > phy = u_phy ;
2013-05-16 18:13:02 +04:00
2012-12-14 00:59:08 +04:00
tegra - > needs_double_reset = of_property_read_bool ( pdev - > dev . of_node ,
" nvidia,needs-double-reset " ) ;
2011-03-10 03:28:56 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " Failed to get I/O memory \n " ) ;
err = - ENXIO ;
2013-06-13 21:24:13 +04:00
goto cleanup_clk_en ;
2011-03-10 03:28:56 +03:00
}
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
2012-07-30 18:43:41 +04:00
hcd - > regs = devm_ioremap ( & pdev - > dev , res - > start , resource_size ( res ) ) ;
2011-03-10 03:28:56 +03:00
if ( ! hcd - > regs ) {
dev_err ( & pdev - > dev , " Failed to remap I/O memory \n " ) ;
err = - ENOMEM ;
2013-06-13 21:24:13 +04:00
goto cleanup_clk_en ;
2011-11-04 13:12:40 +04:00
}
2013-06-13 21:24:13 +04:00
ehci - > caps = hcd - > regs + 0x100 ;
2011-11-04 13:12:40 +04:00
2013-05-16 18:13:02 +04:00
err = usb_phy_init ( hcd - > phy ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Failed to initialize phy \n " ) ;
2013-06-13 21:24:13 +04:00
goto cleanup_clk_en ;
2011-03-10 03:28:56 +03:00
}
2013-01-18 00:15:37 +04:00
u_phy - > otg = devm_kzalloc ( & pdev - > dev , sizeof ( struct usb_otg ) ,
GFP_KERNEL ) ;
if ( ! u_phy - > otg ) {
dev_err ( & pdev - > dev , " Failed to alloc memory for otg \n " ) ;
err = - ENOMEM ;
2013-05-16 18:13:02 +04:00
goto cleanup_phy ;
2013-01-18 00:15:37 +04:00
}
u_phy - > otg - > host = hcd_to_bus ( hcd ) ;
2013-01-24 14:27:03 +04:00
err = usb_phy_set_suspend ( hcd - > phy , 0 ) ;
2011-03-10 03:28:56 +03:00
if ( err ) {
dev_err ( & pdev - > dev , " Failed to power on the phy \n " ) ;
2013-05-16 18:13:02 +04:00
goto cleanup_phy ;
2011-03-10 03:28:56 +03:00
}
irq = platform_get_irq ( pdev , 0 ) ;
if ( ! irq ) {
dev_err ( & pdev - > dev , " Failed to get IRQ \n " ) ;
err = - ENODEV ;
2013-05-16 18:13:02 +04:00
goto cleanup_phy ;
2011-03-10 03:28:56 +03:00
}
if ( pdata - > operating_mode = = TEGRA_USB_OTG ) {
2012-07-30 18:43:41 +04:00
tegra - > transceiver =
devm_usb_get_phy ( & pdev - > dev , USB_PHY_TYPE_USB2 ) ;
2013-03-15 13:04:39 +04:00
if ( ! IS_ERR ( tegra - > transceiver ) )
2012-02-13 15:24:20 +04:00
otg_set_host ( tegra - > transceiver - > otg , & hcd - > self ) ;
2013-04-03 23:57:57 +04:00
} else {
tegra - > transceiver = ERR_PTR ( - ENODEV ) ;
2011-03-10 03:28:56 +03:00
}
2011-09-07 12:10:52 +04:00
err = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
2011-03-10 03:28:56 +03:00
if ( err ) {
dev_err ( & pdev - > dev , " Failed to add USB HCD \n " ) ;
2013-05-16 18:13:02 +04:00
goto cleanup_phy ;
2011-03-10 03:28:56 +03:00
}
return err ;
2013-05-16 18:13:02 +04:00
cleanup_phy :
2013-03-15 13:04:39 +04:00
if ( ! IS_ERR ( tegra - > transceiver ) )
2012-02-13 15:24:20 +04:00
otg_set_host ( tegra - > transceiver - > otg , NULL ) ;
2013-05-16 18:13:02 +04:00
2013-01-24 14:27:03 +04:00
usb_phy_shutdown ( hcd - > phy ) ;
2013-06-13 21:24:13 +04:00
cleanup_clk_en :
clk_disable_unprepare ( tegra - > clk ) ;
cleanup_clk_get :
clk_put ( tegra - > clk ) ;
2013-05-16 18:13:02 +04:00
cleanup_hcd_create :
2011-03-10 03:28:56 +03:00
usb_put_hcd ( hcd ) ;
2013-06-13 21:24:13 +04:00
cleanup_vbus_gpio :
/* FIXME: Undo setup_vbus_gpio() here */
2011-03-10 03:28:56 +03:00
return err ;
}
static int tegra_ehci_remove ( struct platform_device * pdev )
{
2013-06-13 21:24:13 +04:00
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
struct tegra_ehci_hcd * tegra =
( struct tegra_ehci_hcd * ) hcd_to_ehci ( hcd ) - > priv ;
2011-03-10 03:28:56 +03:00
2013-03-15 13:04:39 +04:00
if ( ! IS_ERR ( tegra - > transceiver ) )
2012-02-13 15:24:20 +04:00
otg_set_host ( tegra - > transceiver - > otg , NULL ) ;
2011-03-10 03:28:56 +03:00
2013-01-24 14:27:03 +04:00
usb_phy_shutdown ( hcd - > phy ) ;
2011-03-10 03:28:56 +03:00
usb_remove_hcd ( hcd ) ;
2012-08-10 10:12:43 +04:00
usb_put_hcd ( hcd ) ;
2012-06-05 08:29:38 +04:00
clk_disable_unprepare ( tegra - > clk ) ;
2011-03-10 03:28:56 +03:00
return 0 ;
}
static void tegra_ehci_hcd_shutdown ( struct platform_device * pdev )
{
2013-06-13 21:24:13 +04:00
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
2011-03-10 03:28:56 +03:00
if ( hcd - > driver - > shutdown )
hcd - > driver - > shutdown ( hcd ) ;
}
2012-11-19 22:24:34 +04:00
static struct of_device_id tegra_ehci_of_match [ ] = {
2011-11-04 13:12:40 +04:00
{ . compatible = " nvidia,tegra20-ehci " , } ,
{ } ,
} ;
2011-03-10 03:28:56 +03:00
static struct platform_driver tegra_ehci_driver = {
. probe = tegra_ehci_probe ,
. remove = tegra_ehci_remove ,
. shutdown = tegra_ehci_hcd_shutdown ,
. driver = {
2013-06-13 21:24:12 +04:00
. name = DRV_NAME ,
2011-11-04 13:12:40 +04:00
. of_match_table = tegra_ehci_of_match ,
2011-03-10 03:28:56 +03:00
}
} ;
2013-06-13 21:24:12 +04:00
static const struct ehci_driver_overrides tegra_overrides __initconst = {
. extra_priv_size = sizeof ( struct tegra_ehci_hcd ) ,
} ;
static int __init ehci_tegra_init ( void )
{
if ( usb_disabled ( ) )
return - ENODEV ;
pr_info ( DRV_NAME " : " DRIVER_DESC " \n " ) ;
ehci_init_driver ( & tegra_ehci_hc_driver , & tegra_overrides ) ;
/*
* The Tegra HW has some unusual quirks , which require Tegra - specific
* workarounds . We override certain hc_driver functions here to
* achieve that . We explicitly do not enhance ehci_driver_overrides to
* allow this more easily , since this is an unusual case , and we don ' t
* want to encourage others to override these functions by making it
* too easy .
*/
orig_hub_control = tegra_ehci_hc_driver . hub_control ;
tegra_ehci_hc_driver . map_urb_for_dma = tegra_ehci_map_urb_for_dma ;
tegra_ehci_hc_driver . unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma ;
tegra_ehci_hc_driver . hub_control = tegra_ehci_hub_control ;
return platform_driver_register ( & tegra_ehci_driver ) ;
}
module_init ( ehci_tegra_init ) ;
static void __exit ehci_tegra_cleanup ( void )
{
platform_driver_unregister ( & tegra_ehci_driver ) ;
}
module_exit ( ehci_tegra_cleanup ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;
MODULE_DEVICE_TABLE ( of , tegra_ehci_of_match ) ;