2005-04-17 02:20:36 +04:00
/*
* OHCI HCD ( Host Controller Driver ) for USB .
*
* ( C ) Copyright 1999 Roman Weissgaerber < weissg @ vienna . at >
* ( C ) Copyright 2000 - 2002 David Brownell < dbrownell @ users . sourceforge . net >
* ( C ) Copyright 2002 Hewlett - Packard Company
*
* Bus Glue for pxa27x
*
* Written by Christopher Hoover < ch @ hpl . hp . com >
* Based on fragments of previous driver by Russell King et al .
*
* Modified for LH7A404 from ohci - sa1111 . c
* by Durgesh Pattamatta < pattamattad @ sharpsec . com >
*
* Modified for pxa27x from ohci - lh7a404 . c
* by Nick Bane < nick @ cecomputing . co . uk > 26 - 8 - 2004
*
* This file is licenced under the GPL .
*/
# include <linux/device.h>
2005-10-31 02:03:48 +03:00
# include <linux/signal.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-10-31 18:32:56 +03:00
2005-04-17 02:20:36 +04:00
# include <asm/mach-types.h>
# include <asm/hardware.h>
# include <asm/arch/pxa-regs.h>
2005-11-12 17:22:11 +03:00
# include <asm/arch/ohci.h>
2005-04-17 02:20:36 +04:00
# define PXA_UHC_MAX_PORTNUM 3
# define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 )
/*
PMM_NPS_MODE - - PMM Non - power switching mode
Ports are powered continuously .
PMM_GLOBAL_MODE - - PMM global switching mode
All ports are powered at the same time .
PMM_PERPORT_MODE - - PMM per port switching mode
Ports are powered individually .
*/
static int pxa27x_ohci_select_pmm ( int mode )
{
switch ( mode ) {
case PMM_NPS_MODE :
UHCRHDA | = RH_A_NPS ;
break ;
case PMM_GLOBAL_MODE :
UHCRHDA & = ~ ( RH_A_NPS & RH_A_PSM ) ;
break ;
case PMM_PERPORT_MODE :
UHCRHDA & = ~ ( RH_A_NPS ) ;
UHCRHDA | = RH_A_PSM ;
/* Set port power control mask bits, only 3 ports. */
UHCRHDB | = ( 0x7 < < 17 ) ;
break ;
default :
printk ( KERN_ERR
" Invalid mode %d, set to non-power switch mode. \n " ,
mode ) ;
UHCRHDA | = RH_A_NPS ;
}
return 0 ;
}
extern int usb_disabled ( void ) ;
/*-------------------------------------------------------------------------*/
2005-11-12 17:22:11 +03:00
static int pxa27x_start_hc ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-12 17:22:11 +03:00
int retval = 0 ;
struct pxaohci_platform_data * inf ;
inf = dev - > platform_data ;
2005-04-17 02:20:36 +04:00
pxa_set_cken ( CKEN10_USBHOST , 1 ) ;
UHCHR | = UHCHR_FHR ;
udelay ( 11 ) ;
UHCHR & = ~ UHCHR_FHR ;
UHCHR | = UHCHR_FSBIR ;
while ( UHCHR & UHCHR_FSBIR )
cpu_relax ( ) ;
2005-11-12 17:22:11 +03:00
if ( inf - > init )
retval = inf - > init ( dev ) ;
2005-08-31 22:54:09 +04:00
2005-11-12 17:22:11 +03:00
if ( retval < 0 )
return retval ;
2005-04-17 02:20:36 +04:00
UHCHR & = ~ UHCHR_SSE ;
UHCHIE = ( UHCHIE_UPRIE | UHCHIE_RWIE ) ;
2005-08-31 22:54:09 +04:00
/* Clear any OTG Pin Hold */
if ( PSSR & PSSR_OTGPH )
PSSR | = PSSR_OTGPH ;
2005-11-12 17:22:11 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-11-12 17:22:11 +03:00
static void pxa27x_stop_hc ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-12 17:22:11 +03:00
struct pxaohci_platform_data * inf ;
inf = dev - > platform_data ;
if ( inf - > exit )
inf - > exit ( dev ) ;
2005-04-17 02:20:36 +04:00
UHCHR | = UHCHR_FHR ;
udelay ( 11 ) ;
UHCHR & = ~ UHCHR_FHR ;
UHCCOMS | = 1 ;
udelay ( 10 ) ;
pxa_set_cken ( CKEN10_USBHOST , 0 ) ;
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_pxa27x_probe - initialize pxa27x - based HCDs
* Context : ! in_interrupt ( )
*
* Allocates basic resources for this USB host controller , and
* then invokes the start ( ) method for the HCD associated with it
* through the hotplug entry ' s driver_data .
*
*/
2005-11-12 17:22:11 +03:00
int usb_hcd_pxa27x_probe ( const struct hc_driver * driver , struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
int retval ;
struct usb_hcd * hcd ;
2005-11-12 17:22:11 +03:00
struct pxaohci_platform_data * inf ;
inf = pdev - > dev . platform_data ;
2005-04-17 02:20:36 +04:00
2005-11-12 17:22:11 +03:00
if ( ! inf )
return - ENODEV ;
if ( pdev - > resource [ 1 ] . flags ! = IORESOURCE_IRQ ) {
2005-04-17 02:20:36 +04:00
pr_debug ( " resource[1] is not IORESOURCE_IRQ " ) ;
return - ENOMEM ;
}
2005-11-12 17:22:11 +03:00
hcd = usb_create_hcd ( driver , & pdev - > dev , " pxa27x " ) ;
2005-04-17 02:20:36 +04:00
if ( ! hcd )
return - ENOMEM ;
2005-11-12 17:22:11 +03:00
hcd - > rsrc_start = pdev - > resource [ 0 ] . start ;
hcd - > rsrc_len = pdev - > resource [ 0 ] . end - pdev - > resource [ 0 ] . start + 1 ;
2005-04-17 02:20:36 +04:00
if ( ! request_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len , hcd_name ) ) {
pr_debug ( " request_mem_region failed " ) ;
retval = - EBUSY ;
goto err1 ;
}
hcd - > regs = ioremap ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
if ( ! hcd - > regs ) {
pr_debug ( " ioremap failed " ) ;
retval = - ENOMEM ;
goto err2 ;
}
2005-11-12 17:22:11 +03:00
if ( ( retval = pxa27x_start_hc ( & pdev - > dev ) ) < 0 ) {
pr_debug ( " pxa27x_start_hc failed " ) ;
goto err3 ;
}
2005-04-17 02:20:36 +04:00
/* Select Power Management Mode */
2005-11-12 17:22:11 +03:00
pxa27x_ohci_select_pmm ( inf - > port_mode ) ;
2005-04-17 02:20:36 +04:00
ohci_hcd_init ( hcd_to_ohci ( hcd ) ) ;
2005-11-12 17:22:11 +03:00
retval = usb_add_hcd ( hcd , pdev - > resource [ 1 ] . start , SA_INTERRUPT ) ;
2005-04-17 02:20:36 +04:00
if ( retval = = 0 )
return retval ;
2005-11-12 17:22:11 +03:00
pxa27x_stop_hc ( & pdev - > dev ) ;
err3 :
2005-04-17 02:20:36 +04:00
iounmap ( hcd - > regs ) ;
err2 :
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
err1 :
usb_put_hcd ( hcd ) ;
return retval ;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_pxa27x_remove - shutdown processing for pxa27x - based HCDs
* @ dev : USB Host Controller being removed
* Context : ! in_interrupt ( )
*
* Reverses the effect of usb_hcd_pxa27x_probe ( ) , first invoking
* the HCD ' s stop ( ) method . It is always called from a thread
* context , normally " rmmod " , " apmd " , or something similar .
*
*/
2005-11-12 17:22:11 +03:00
void usb_hcd_pxa27x_remove ( struct usb_hcd * hcd , struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
usb_remove_hcd ( hcd ) ;
2005-11-12 17:22:11 +03:00
pxa27x_stop_hc ( & pdev - > dev ) ;
2005-04-17 02:20:36 +04:00
iounmap ( hcd - > regs ) ;
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
usb_put_hcd ( hcd ) ;
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_pxa27x_start ( struct usb_hcd * hcd )
{
struct ohci_hcd * ohci = hcd_to_ohci ( hcd ) ;
int ret ;
ohci_dbg ( ohci , " ohci_pxa27x_start, ohci:%p " , ohci ) ;
2005-08-31 22:52:57 +04:00
/* The value of NDP in roothub_a is incorrect on this hardware */
ohci - > num_ports = 3 ;
2005-04-17 02:20:36 +04:00
if ( ( ret = ohci_init ( ohci ) ) < 0 )
return ret ;
if ( ( ret = ohci_run ( ohci ) ) < 0 ) {
err ( " can't start %s " , hcd - > self . bus_name ) ;
ohci_stop ( hcd ) ;
return ret ;
}
return 0 ;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_pxa27x_hc_driver = {
. description = hcd_name ,
. product_desc = " PXA27x OHCI " ,
. hcd_priv_size = sizeof ( struct ohci_hcd ) ,
/*
* generic hardware linkage
*/
. irq = ohci_irq ,
. flags = HCD_USB11 | HCD_MEMORY ,
/*
* basic lifecycle operations
*/
. start = ohci_pxa27x_start ,
. stop = ohci_stop ,
/*
* managing i / o requests and associated device resources
*/
. urb_enqueue = ohci_urb_enqueue ,
. urb_dequeue = ohci_urb_dequeue ,
. endpoint_disable = ohci_endpoint_disable ,
/*
* scheduling support
*/
. get_frame_number = ohci_get_frame ,
/*
* root hub support
*/
. hub_status_data = ohci_hub_status_data ,
. hub_control = ohci_hub_control ,
2005-09-14 06:59:11 +04:00
# ifdef CONFIG_PM
2005-10-14 01:08:02 +04:00
. bus_suspend = ohci_bus_suspend ,
. bus_resume = ohci_bus_resume ,
2005-04-17 02:20:36 +04:00
# endif
2005-09-23 09:32:11 +04:00
. start_port_reset = ohci_start_port_reset ,
2005-04-17 02:20:36 +04:00
} ;
/*-------------------------------------------------------------------------*/
2005-11-10 01:32:44 +03:00
static int ohci_hcd_pxa27x_drv_probe ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
pr_debug ( " In ohci_hcd_pxa27x_drv_probe " ) ;
if ( usb_disabled ( ) )
return - ENODEV ;
2005-11-12 17:22:11 +03:00
return usb_hcd_pxa27x_probe ( & ohci_pxa27x_hc_driver , pdev ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-10 01:32:44 +03:00
static int ohci_hcd_pxa27x_drv_remove ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
2005-04-17 02:20:36 +04:00
usb_hcd_pxa27x_remove ( hcd , pdev ) ;
2005-11-29 01:15:46 +03:00
platform_set_drvdata ( pdev , NULL ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-12 17:22:14 +03:00
# ifdef CONFIG_PM
static int ohci_hcd_pxa27x_drv_suspend ( struct platform_device * pdev , pm_message_t state )
2005-04-17 02:20:36 +04:00
{
2005-11-29 01:15:46 +03:00
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
struct ohci_hcd * ohci = hcd_to_ohci ( hcd ) ;
2005-11-12 17:22:14 +03:00
if ( time_before ( jiffies , ohci - > next_statechange ) )
msleep ( 5 ) ;
ohci - > next_statechange = jiffies ;
pxa27x_stop_hc ( & pdev - > dev ) ;
2005-11-29 01:15:46 +03:00
hcd - > state = HC_STATE_SUSPENDED ;
2005-11-12 17:22:14 +03:00
pdev - > dev . power . power_state = PMSG_SUSPEND ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-12 17:22:14 +03:00
static int ohci_hcd_pxa27x_drv_resume ( struct platform_device * pdev )
2005-04-17 02:20:36 +04:00
{
2005-11-29 01:15:46 +03:00
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
struct ohci_hcd * ohci = hcd_to_ohci ( hcd ) ;
2005-11-12 17:22:14 +03:00
int status ;
if ( time_before ( jiffies , ohci - > next_statechange ) )
msleep ( 5 ) ;
ohci - > next_statechange = jiffies ;
if ( ( status = pxa27x_start_hc ( & pdev - > dev ) ) < 0 )
return status ;
pdev - > dev . power . power_state = PMSG_ON ;
2005-11-29 01:15:46 +03:00
usb_hcd_resume_root_hub ( hcd ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-12 17:22:14 +03:00
# endif
2005-04-17 02:20:36 +04:00
2005-11-10 01:32:44 +03:00
static struct platform_driver ohci_hcd_pxa27x_driver = {
2005-04-17 02:20:36 +04:00
. probe = ohci_hcd_pxa27x_drv_probe ,
. remove = ohci_hcd_pxa27x_drv_remove ,
2005-11-12 17:22:14 +03:00
# ifdef CONFIG_PM
2005-04-17 02:20:36 +04:00
. suspend = ohci_hcd_pxa27x_drv_suspend ,
2005-11-10 01:32:44 +03:00
. resume = ohci_hcd_pxa27x_drv_resume ,
2005-11-12 17:22:14 +03:00
# endif
2005-11-10 01:32:44 +03:00
. driver = {
. name = " pxa27x-ohci " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
static int __init ohci_hcd_pxa27x_init ( void )
{
pr_debug ( DRIVER_INFO " (pxa27x) " ) ;
pr_debug ( " block sizes: ed %d td %d \n " ,
sizeof ( struct ed ) , sizeof ( struct td ) ) ;
2005-11-10 01:32:44 +03:00
return platform_driver_register ( & ohci_hcd_pxa27x_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit ohci_hcd_pxa27x_cleanup ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & ohci_hcd_pxa27x_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( ohci_hcd_pxa27x_init ) ;
module_exit ( ohci_hcd_pxa27x_cleanup ) ;