2011-03-10 03:28:56 +03:00
/*
* EHCI - compliant USB host controller driver for NVIDIA Tegra SoCs
*
* Copyright ( C ) 2010 Google , Inc .
* Copyright ( C ) 2009 NVIDIA Corporation
*
* 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>
# include <linux/platform_device.h>
# include <linux/platform_data/tegra_usb.h>
# include <linux/irq.h>
# include <linux/usb/otg.h>
2011-11-04 13:12:40 +04:00
# include <linux/gpio.h>
# include <linux/of.h>
# include <linux/of_gpio.h>
2011-03-10 03:28:56 +03:00
# include <mach/usb_phy.h>
2011-11-04 13:12:40 +04:00
# include <mach/iomap.h>
2011-03-10 03:28:56 +03:00
2011-03-10 03:28:57 +03:00
# define TEGRA_USB_DMA_ALIGN 32
2011-03-10 03:28:56 +03:00
struct tegra_ehci_hcd {
struct ehci_hcd * ehci ;
struct tegra_usb_phy * phy ;
struct clk * clk ;
struct clk * emc_clk ;
2012-02-13 15:24:02 +04:00
struct usb_phy * transceiver ;
2011-03-10 03:28:56 +03:00
int host_resumed ;
int bus_suspended ;
int port_resuming ;
int power_down_on_bus_suspend ;
enum tegra_usb_phy_port_speed port_speed ;
} ;
static void tegra_ehci_power_up ( struct usb_hcd * hcd )
{
struct tegra_ehci_hcd * tegra = dev_get_drvdata ( hcd - > self . controller ) ;
clk_enable ( tegra - > emc_clk ) ;
clk_enable ( tegra - > clk ) ;
tegra_usb_phy_power_on ( tegra - > phy ) ;
tegra - > host_resumed = 1 ;
}
static void tegra_ehci_power_down ( struct usb_hcd * hcd )
{
struct tegra_ehci_hcd * tegra = dev_get_drvdata ( hcd - > self . controller ) ;
tegra - > host_resumed = 0 ;
tegra_usb_phy_power_off ( tegra - > phy ) ;
clk_disable ( tegra - > clk ) ;
clk_disable ( tegra - > emc_clk ) ;
}
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
)
{
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
struct tegra_ehci_hcd * tegra = dev_get_drvdata ( hcd - > self . controller ) ;
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 ;
tegra_usb_phy_postresume ( tegra - > phy ) ;
}
}
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 ;
}
temp & = ~ PORT_WKCONN_E ;
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 .
*/
if ( handshake ( ehci , status_reg , PORT_SUSPEND ,
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 */
if ( tegra - > phy - > instance = = 0 & &
( 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 */
tegra_usb_phy_preresume ( tegra - > phy ) ;
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 */
if ( handshake ( ehci , status_reg , PORT_RESUME , 0 , 2000 ) )
pr_err ( " %s: timeout waiting for RESUME \n " , __func__ ) ;
if ( handshake ( ehci , status_reg , PORT_SUSPEND , 0 , 2000 ) )
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 */
return ehci_hub_control ( hcd , typeReq , wValue , wIndex , buf , wLength ) ;
done :
spin_unlock_irqrestore ( & ehci - > lock , flags ) ;
return retval ;
}
static void tegra_ehci_restart ( struct usb_hcd * hcd )
{
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
ehci_reset ( ehci ) ;
/* setup the frame list and Async q heads */
ehci_writel ( ehci , ehci - > periodic_dma , & ehci - > regs - > frame_list ) ;
ehci_writel ( ehci , ( u32 ) ehci - > async - > qh_dma , & ehci - > regs - > async_next ) ;
/* setup the command register and set the controller in RUN mode */
ehci - > command & = ~ ( CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET ) ;
ehci - > command | = CMD_RUN ;
ehci_writel ( ehci , ehci - > command , & ehci - > regs - > command ) ;
down_write ( & ehci_cf_port_reset_rwsem ) ;
ehci_writel ( ehci , FLAG_CF , & ehci - > regs - > configured_flag ) ;
/* flush posted writes */
ehci_readl ( ehci , & ehci - > regs - > command ) ;
up_write ( & ehci_cf_port_reset_rwsem ) ;
}
static int tegra_usb_suspend ( struct usb_hcd * hcd )
{
struct tegra_ehci_hcd * tegra = dev_get_drvdata ( hcd - > self . controller ) ;
struct ehci_regs __iomem * hw = tegra - > ehci - > regs ;
unsigned long flags ;
spin_lock_irqsave ( & tegra - > ehci - > lock , flags ) ;
tegra - > port_speed = ( readl ( & hw - > port_status [ 0 ] ) > > 26 ) & 0x3 ;
ehci_halt ( tegra - > ehci ) ;
clear_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ;
spin_unlock_irqrestore ( & tegra - > ehci - > lock , flags ) ;
tegra_ehci_power_down ( hcd ) ;
return 0 ;
}
static int tegra_usb_resume ( struct usb_hcd * hcd )
{
struct tegra_ehci_hcd * tegra = dev_get_drvdata ( hcd - > self . controller ) ;
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
struct ehci_regs __iomem * hw = ehci - > regs ;
unsigned long val ;
set_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ;
tegra_ehci_power_up ( hcd ) ;
if ( tegra - > port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH ) {
/* Wait for the phy to detect new devices
* before we restart the controller */
msleep ( 10 ) ;
goto restart ;
}
/* Force the phy to keep data lines in suspend state */
tegra_ehci_phy_restore_start ( tegra - > phy , tegra - > port_speed ) ;
/* Enable host mode */
tdi_reset ( ehci ) ;
/* Enable Port Power */
val = readl ( & hw - > port_status [ 0 ] ) ;
val | = PORT_POWER ;
writel ( val , & hw - > port_status [ 0 ] ) ;
udelay ( 10 ) ;
/* Check if the phy resume from LP0. When the phy resume from LP0
* USB register will be reset . */
if ( ! readl ( & hw - > async_next ) ) {
/* Program the field PTC based on the saved speed mode */
val = readl ( & hw - > port_status [ 0 ] ) ;
val & = ~ PORT_TEST ( ~ 0 ) ;
if ( tegra - > port_speed = = TEGRA_USB_PHY_PORT_SPEED_HIGH )
val | = PORT_TEST_FORCE ;
else if ( tegra - > port_speed = = TEGRA_USB_PHY_PORT_SPEED_FULL )
val | = PORT_TEST ( 6 ) ;
else if ( tegra - > port_speed = = TEGRA_USB_PHY_PORT_SPEED_LOW )
val | = PORT_TEST ( 7 ) ;
writel ( val , & hw - > port_status [ 0 ] ) ;
udelay ( 10 ) ;
/* Disable test mode by setting PTC field to NORMAL_OP */
val = readl ( & hw - > port_status [ 0 ] ) ;
val & = ~ PORT_TEST ( ~ 0 ) ;
writel ( val , & hw - > port_status [ 0 ] ) ;
udelay ( 10 ) ;
}
/* Poll until CCS is enabled */
if ( handshake ( ehci , & hw - > port_status [ 0 ] , PORT_CONNECT ,
PORT_CONNECT , 2000 ) ) {
pr_err ( " %s: timeout waiting for PORT_CONNECT \n " , __func__ ) ;
goto restart ;
}
/* Poll until PE is enabled */
if ( handshake ( ehci , & hw - > port_status [ 0 ] , PORT_PE ,
PORT_PE , 2000 ) ) {
pr_err ( " %s: timeout waiting for USB_PORTSC1_PE \n " , __func__ ) ;
goto restart ;
}
/* Clear the PCI status, to avoid an interrupt taken upon resume */
val = readl ( & hw - > status ) ;
val | = STS_PCD ;
writel ( val , & hw - > status ) ;
/* Put controller in suspend mode by writing 1 to SUSP bit of PORTSC */
val = readl ( & hw - > port_status [ 0 ] ) ;
if ( ( val & PORT_POWER ) & & ( val & PORT_PE ) ) {
val | = PORT_SUSPEND ;
writel ( val , & hw - > port_status [ 0 ] ) ;
/* Wait until port suspend completes */
if ( handshake ( ehci , & hw - > port_status [ 0 ] , PORT_SUSPEND ,
PORT_SUSPEND , 1000 ) ) {
pr_err ( " %s: timeout waiting for PORT_SUSPEND \n " ,
__func__ ) ;
goto restart ;
}
}
tegra_ehci_phy_restore_end ( tegra - > phy ) ;
return 0 ;
restart :
if ( tegra - > port_speed < = TEGRA_USB_PHY_PORT_SPEED_HIGH )
tegra_ehci_phy_restore_end ( tegra - > phy ) ;
tegra_ehci_restart ( hcd ) ;
return 0 ;
}
static void tegra_ehci_shutdown ( struct usb_hcd * hcd )
{
struct tegra_ehci_hcd * tegra = dev_get_drvdata ( hcd - > self . controller ) ;
/* ehci_shutdown touches the USB controller registers, make sure
* controller has clocks to it */
if ( ! tegra - > host_resumed )
tegra_ehci_power_up ( hcd ) ;
ehci_shutdown ( hcd ) ;
}
static int tegra_ehci_setup ( struct usb_hcd * hcd )
{
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
int retval ;
/* EHCI registers start at offset 0x100 */
ehci - > caps = hcd - > regs + 0x100 ;
ehci - > regs = hcd - > regs + 0x100 +
2011-05-03 22:11:57 +04:00
HC_LENGTH ( ehci , readl ( & ehci - > caps - > hc_capbase ) ) ;
2011-03-10 03:28:56 +03:00
dbg_hcs_params ( ehci , " reset " ) ;
dbg_hcc_params ( ehci , " reset " ) ;
/* cache this readonly data; minimize chip reads */
ehci - > hcs_params = readl ( & ehci - > caps - > hcs_params ) ;
/* switch to host mode */
hcd - > has_tt = 1 ;
ehci_reset ( ehci ) ;
retval = ehci_halt ( ehci ) ;
if ( retval )
return retval ;
/* data structure init */
retval = ehci_init ( hcd ) ;
if ( retval )
return retval ;
ehci - > sbrn = 0x20 ;
ehci_port_power ( ehci , 1 ) ;
return retval ;
}
# ifdef CONFIG_PM
static int tegra_ehci_bus_suspend ( struct usb_hcd * hcd )
{
struct tegra_ehci_hcd * tegra = dev_get_drvdata ( hcd - > self . controller ) ;
int error_status = 0 ;
error_status = ehci_bus_suspend ( hcd ) ;
if ( ! error_status & & tegra - > power_down_on_bus_suspend ) {
tegra_usb_suspend ( hcd ) ;
tegra - > bus_suspended = 1 ;
}
return error_status ;
}
static int tegra_ehci_bus_resume ( struct usb_hcd * hcd )
{
struct tegra_ehci_hcd * tegra = dev_get_drvdata ( hcd - > self . controller ) ;
if ( tegra - > bus_suspended & & tegra - > power_down_on_bus_suspend ) {
tegra_usb_resume ( hcd ) ;
tegra - > bus_suspended = 0 ;
}
tegra_usb_phy_preresume ( tegra - > phy ) ;
tegra - > port_resuming = 1 ;
return ehci_bus_resume ( hcd ) ;
}
# endif
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
}
2011-03-10 03:28:56 +03:00
static const struct hc_driver tegra_ehci_hc_driver = {
. description = hcd_name ,
. product_desc = " Tegra EHCI Host Controller " ,
. hcd_priv_size = sizeof ( struct ehci_hcd ) ,
. flags = HCD_USB2 | HCD_MEMORY ,
2012-04-06 08:10:18 +04:00
/* standard ehci functions */
2011-03-10 03:28:56 +03:00
. irq = ehci_irq ,
. start = ehci_run ,
. stop = ehci_stop ,
. urb_enqueue = ehci_urb_enqueue ,
. urb_dequeue = ehci_urb_dequeue ,
. endpoint_disable = ehci_endpoint_disable ,
. endpoint_reset = ehci_endpoint_reset ,
. get_frame_number = ehci_get_frame ,
. hub_status_data = ehci_hub_status_data ,
. clear_tt_buffer_complete = ehci_clear_tt_buffer_complete ,
2012-04-06 08:10:18 +04:00
. relinquish_port = ehci_relinquish_port ,
. port_handed_over = ehci_port_handed_over ,
/* modified ehci functions for tegra */
. reset = tegra_ehci_setup ,
. shutdown = tegra_ehci_shutdown ,
. map_urb_for_dma = tegra_ehci_map_urb_for_dma ,
. unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma ,
. hub_control = tegra_ehci_hub_control ,
2011-03-10 03:28:56 +03:00
# ifdef CONFIG_PM
. bus_suspend = tegra_ehci_bus_suspend ,
. bus_resume = tegra_ehci_bus_resume ,
# endif
} ;
2011-11-04 13:12:40 +04:00
static int setup_vbus_gpio ( struct platform_device * pdev )
{
int err = 0 ;
int gpio ;
if ( ! pdev - > dev . of_node )
return 0 ;
gpio = of_get_named_gpio ( pdev - > dev . of_node , " nvidia,vbus-gpio " , 0 ) ;
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 ;
}
gpio_set_value ( gpio , 1 ) ;
return err ;
}
static u64 tegra_ehci_dma_mask = DMA_BIT_MASK ( 32 ) ;
2011-03-10 03:28:56 +03:00
static int tegra_ehci_probe ( struct platform_device * pdev )
{
struct resource * res ;
struct usb_hcd * hcd ;
struct tegra_ehci_hcd * tegra ;
struct tegra_ehci_platform_data * pdata ;
int err = 0 ;
int irq ;
int instance = pdev - > id ;
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 )
pdev - > dev . dma_mask = & tegra_ehci_dma_mask ;
setup_vbus_gpio ( pdev ) ;
2011-03-10 03:28:56 +03:00
tegra = kzalloc ( sizeof ( struct tegra_ehci_hcd ) , GFP_KERNEL ) ;
if ( ! tegra )
return - ENOMEM ;
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 fail_hcd ;
}
platform_set_drvdata ( pdev , tegra ) ;
tegra - > clk = clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( tegra - > clk ) ) {
dev_err ( & pdev - > dev , " Can't get ehci clock \n " ) ;
err = PTR_ERR ( tegra - > clk ) ;
goto fail_clk ;
}
err = clk_enable ( tegra - > clk ) ;
if ( err )
goto fail_clken ;
tegra - > emc_clk = clk_get ( & pdev - > dev , " emc " ) ;
if ( IS_ERR ( tegra - > emc_clk ) ) {
dev_err ( & pdev - > dev , " Can't get emc clock \n " ) ;
err = PTR_ERR ( tegra - > emc_clk ) ;
goto fail_emc_clk ;
}
clk_enable ( tegra - > emc_clk ) ;
clk_set_rate ( tegra - > emc_clk , 400000000 ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " Failed to get I/O memory \n " ) ;
err = - ENXIO ;
goto fail_io ;
}
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
hcd - > regs = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! hcd - > regs ) {
dev_err ( & pdev - > dev , " Failed to remap I/O memory \n " ) ;
err = - ENOMEM ;
goto fail_io ;
}
2011-11-04 13:12:40 +04:00
/* This is pretty ugly and needs to be fixed when we do only
* device - tree probing . Old code relies on the platform_device
* numbering that we lack for device - tree - instantiated devices .
*/
if ( instance < 0 ) {
switch ( res - > start ) {
case TEGRA_USB_BASE :
instance = 0 ;
break ;
case TEGRA_USB2_BASE :
instance = 1 ;
break ;
case TEGRA_USB3_BASE :
instance = 2 ;
break ;
default :
err = - ENODEV ;
dev_err ( & pdev - > dev , " unknown usb instance \n " ) ;
goto fail_phy ;
}
}
2011-03-10 03:28:56 +03:00
tegra - > phy = tegra_usb_phy_open ( instance , hcd - > regs , pdata - > phy_config ,
TEGRA_USB_PHY_MODE_HOST ) ;
if ( IS_ERR ( tegra - > phy ) ) {
dev_err ( & pdev - > dev , " Failed to open USB phy \n " ) ;
err = - ENXIO ;
goto fail_phy ;
}
err = tegra_usb_phy_power_on ( tegra - > phy ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Failed to power on the phy \n " ) ;
goto fail ;
}
tegra - > host_resumed = 1 ;
tegra - > power_down_on_bus_suspend = pdata - > power_down_on_bus_suspend ;
tegra - > ehci = hcd_to_ehci ( hcd ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( ! irq ) {
dev_err ( & pdev - > dev , " Failed to get IRQ \n " ) ;
err = - ENODEV ;
goto fail ;
}
# ifdef CONFIG_USB_OTG_UTILS
if ( pdata - > operating_mode = = TEGRA_USB_OTG ) {
2012-02-13 15:24:18 +04:00
tegra - > transceiver = usb_get_transceiver ( ) ;
2011-03-10 03:28:56 +03:00
if ( tegra - > transceiver )
2012-02-13 15:24:20 +04:00
otg_set_host ( tegra - > transceiver - > otg , & hcd - > self ) ;
2011-03-10 03:28:56 +03:00
}
# endif
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 " ) ;
goto fail ;
}
return err ;
fail :
# ifdef CONFIG_USB_OTG_UTILS
if ( tegra - > transceiver ) {
2012-02-13 15:24:20 +04:00
otg_set_host ( tegra - > transceiver - > otg , NULL ) ;
2012-02-13 15:24:18 +04:00
usb_put_transceiver ( tegra - > transceiver ) ;
2011-03-10 03:28:56 +03:00
}
# endif
tegra_usb_phy_close ( tegra - > phy ) ;
fail_phy :
iounmap ( hcd - > regs ) ;
fail_io :
clk_disable ( tegra - > emc_clk ) ;
clk_put ( tegra - > emc_clk ) ;
fail_emc_clk :
clk_disable ( tegra - > clk ) ;
fail_clken :
clk_put ( tegra - > clk ) ;
fail_clk :
usb_put_hcd ( hcd ) ;
fail_hcd :
kfree ( tegra ) ;
return err ;
}
# ifdef CONFIG_PM
static int tegra_ehci_resume ( struct platform_device * pdev )
{
struct tegra_ehci_hcd * tegra = platform_get_drvdata ( pdev ) ;
struct usb_hcd * hcd = ehci_to_hcd ( tegra - > ehci ) ;
if ( tegra - > bus_suspended )
return 0 ;
return tegra_usb_resume ( hcd ) ;
}
static int tegra_ehci_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct tegra_ehci_hcd * tegra = platform_get_drvdata ( pdev ) ;
struct usb_hcd * hcd = ehci_to_hcd ( tegra - > ehci ) ;
if ( tegra - > bus_suspended )
return 0 ;
if ( time_before ( jiffies , tegra - > ehci - > next_statechange ) )
msleep ( 10 ) ;
return tegra_usb_suspend ( hcd ) ;
}
# endif
static int tegra_ehci_remove ( struct platform_device * pdev )
{
struct tegra_ehci_hcd * tegra = platform_get_drvdata ( pdev ) ;
struct usb_hcd * hcd = ehci_to_hcd ( tegra - > ehci ) ;
if ( tegra = = NULL | | hcd = = NULL )
return - EINVAL ;
# ifdef CONFIG_USB_OTG_UTILS
if ( tegra - > transceiver ) {
2012-02-13 15:24:20 +04:00
otg_set_host ( tegra - > transceiver - > otg , NULL ) ;
2012-02-13 15:24:18 +04:00
usb_put_transceiver ( tegra - > transceiver ) ;
2011-03-10 03:28:56 +03:00
}
# endif
usb_remove_hcd ( hcd ) ;
usb_put_hcd ( hcd ) ;
tegra_usb_phy_close ( tegra - > phy ) ;
iounmap ( hcd - > regs ) ;
clk_disable ( tegra - > clk ) ;
clk_put ( tegra - > clk ) ;
clk_disable ( tegra - > emc_clk ) ;
clk_put ( tegra - > emc_clk ) ;
kfree ( tegra ) ;
return 0 ;
}
static void tegra_ehci_hcd_shutdown ( struct platform_device * pdev )
{
struct tegra_ehci_hcd * tegra = platform_get_drvdata ( pdev ) ;
struct usb_hcd * hcd = ehci_to_hcd ( tegra - > ehci ) ;
if ( hcd - > driver - > shutdown )
hcd - > driver - > shutdown ( hcd ) ;
}
2011-11-04 13:12:40 +04:00
static struct of_device_id tegra_ehci_of_match [ ] __devinitdata = {
{ . 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 ,
# ifdef CONFIG_PM
. suspend = tegra_ehci_suspend ,
. resume = tegra_ehci_resume ,
# endif
. shutdown = tegra_ehci_hcd_shutdown ,
. driver = {
. name = " tegra-ehci " ,
2011-11-04 13:12:40 +04:00
. of_match_table = tegra_ehci_of_match ,
2011-03-10 03:28:56 +03:00
}
} ;