2005-04-16 15:20:36 -07: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 AMD Alchemy Au1xxx
*
* Written by Christopher Hoover < ch @ hpl . hp . com >
2008-07-10 17:30:46 -07:00
* Based on fragments of previous driver by Russell King et al .
2005-04-16 15:20:36 -07:00
*
* Modified for LH7A404 from ohci - sa1111 . c
* by Durgesh Pattamatta < pattamattad @ sharpsec . com >
* Modified for AMD Alchemy Au1xxx
* by Matt Porter < mporter @ kernel . crashing . org >
*
* This file is licenced under the GPL .
*/
2005-10-29 19:07:23 +01:00
# include <linux/platform_device.h>
2006-01-08 01:02:05 -08:00
# include <linux/signal.h>
2005-10-29 19:07:23 +01:00
2005-04-16 15:20:36 -07:00
# include <asm/mach-au1x00/au1000.h>
2006-01-20 14:09:54 -08:00
2005-04-16 15:20:36 -07:00
extern int usb_disabled ( void ) ;
2008-06-23 09:08:29 +02:00
static int __devinit ohci_au1xxx_start ( struct usb_hcd * hcd )
2005-04-16 15:20:36 -07:00
{
2008-06-23 09:08:29 +02:00
struct ohci_hcd * ohci = hcd_to_ohci ( hcd ) ;
int ret ;
2005-04-16 15:20:36 -07:00
2008-06-23 09:08:29 +02:00
ohci_dbg ( ohci , " ohci_au1xxx_start, ohci:%p " , ohci ) ;
2005-04-16 15:20:36 -07:00
2008-06-23 09:08:29 +02:00
if ( ( ret = ohci_init ( ohci ) ) < 0 )
2005-04-16 15:20:36 -07:00
return ret ;
2008-06-23 09:08:29 +02:00
if ( ( ret = ohci_run ( ohci ) ) < 0 ) {
2005-04-16 15:20:36 -07:00
err ( " can't start %s " , hcd - > self . bus_name ) ;
2008-06-23 09:08:29 +02:00
ohci_stop ( hcd ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
return 0 ;
}
static const struct hc_driver ohci_au1xxx_hc_driver = {
. description = hcd_name ,
. product_desc = " Au1xxx OHCI " ,
. hcd_priv_size = sizeof ( struct ohci_hcd ) ,
/*
* generic hardware linkage
*/
. irq = ohci_irq ,
. flags = HCD_USB11 | HCD_MEMORY ,
/*
* basic lifecycle operations
*/
. start = ohci_au1xxx_start ,
. stop = ohci_stop ,
2006-12-05 03:18:31 -08:00
. shutdown = ohci_shutdown ,
2005-04-16 15:20:36 -07:00
/*
* 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-22 22:32:11 -07:00
# ifdef CONFIG_PM
2005-10-13 17:08:02 -04:00
. bus_suspend = ohci_bus_suspend ,
. bus_resume = ohci_bus_resume ,
2005-09-22 22:32:11 -07:00
# endif
. start_port_reset = ohci_start_port_reset ,
2005-04-16 15:20:36 -07:00
} ;
2005-11-09 22:32:44 +00:00
static int ohci_hcd_au1xxx_drv_probe ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
int ret ;
2008-06-23 09:08:29 +02:00
struct usb_hcd * hcd ;
2005-04-16 15:20:36 -07:00
if ( usb_disabled ( ) )
return - ENODEV ;
2008-06-23 09:08:29 +02:00
if ( pdev - > resource [ 1 ] . flags ! = IORESOURCE_IRQ ) {
pr_debug ( " resource[1] is not IORESOURCE_IRQ \n " ) ;
return - ENOMEM ;
}
hcd = usb_create_hcd ( & ohci_au1xxx_hc_driver , & pdev - > dev , " au1xxx " ) ;
if ( ! hcd )
return - ENOMEM ;
hcd - > rsrc_start = pdev - > resource [ 0 ] . start ;
hcd - > rsrc_len = pdev - > resource [ 0 ] . end - pdev - > resource [ 0 ] . start + 1 ;
if ( ! request_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len , hcd_name ) ) {
pr_debug ( " request_mem_region failed \n " ) ;
ret = - EBUSY ;
goto err1 ;
}
hcd - > regs = ioremap ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
if ( ! hcd - > regs ) {
pr_debug ( " ioremap failed \n " ) ;
ret = - ENOMEM ;
goto err2 ;
}
2011-08-12 20:12:33 +02:00
if ( alchemy_usb_control ( ALCHEMY_USB_OHCI0 , 1 ) ) {
printk ( KERN_INFO " %s: controller init failed! \n " , pdev - > name ) ;
ret = - ENODEV ;
goto err3 ;
}
2008-06-23 09:08:29 +02:00
ohci_hcd_init ( hcd_to_ohci ( hcd ) ) ;
ret = usb_add_hcd ( hcd , pdev - > resource [ 1 ] . start ,
2011-09-07 16:10:52 +08:00
IRQF_SHARED ) ;
2008-06-23 09:08:29 +02:00
if ( ret = = 0 ) {
platform_set_drvdata ( pdev , hcd ) ;
return ret ;
}
2011-08-12 20:12:33 +02:00
alchemy_usb_control ( ALCHEMY_USB_OHCI0 , 0 ) ;
err3 :
2008-06-23 09:08:29 +02:00
iounmap ( hcd - > regs ) ;
err2 :
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
err1 :
usb_put_hcd ( hcd ) ;
2005-04-16 15:20:36 -07:00
return ret ;
}
2005-11-09 22:32:44 +00:00
static int ohci_hcd_au1xxx_drv_remove ( struct platform_device * pdev )
2005-04-16 15:20:36 -07:00
{
2005-11-09 22:32:44 +00:00
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
2005-04-16 15:20:36 -07:00
2008-06-23 09:08:29 +02:00
usb_remove_hcd ( hcd ) ;
2011-08-12 20:12:33 +02:00
alchemy_usb_control ( ALCHEMY_USB_OHCI0 , 0 ) ;
2008-06-23 09:08:29 +02:00
iounmap ( hcd - > regs ) ;
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
usb_put_hcd ( hcd ) ;
platform_set_drvdata ( pdev , NULL ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2008-06-23 09:08:29 +02:00
2008-06-23 09:09:37 +02:00
# ifdef CONFIG_PM
2009-07-29 19:13:13 +02:00
static int ohci_hcd_au1xxx_drv_suspend ( struct device * dev )
2005-04-16 15:20:36 -07:00
{
2009-07-29 19:13:13 +02:00
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2008-06-23 09:09:37 +02:00
struct ohci_hcd * ohci = hcd_to_ohci ( hcd ) ;
unsigned long flags ;
int rc ;
rc = 0 ;
/* Root hub was already suspended. Disable irq emission and
* mark HW unaccessible , bail out if RH has been resumed . Use
* the spinlock to properly synchronize with possible pending
* RH suspend or resume activity .
*/
spin_lock_irqsave ( & ohci - > lock , flags ) ;
2011-11-17 16:41:56 -05:00
if ( ohci - > rh_state ! = OHCI_RH_SUSPENDED ) {
2008-06-23 09:09:37 +02:00
rc = - EINVAL ;
goto bail ;
}
ohci_writel ( ohci , OHCI_INTR_MIE , & ohci - > regs - > intrdisable ) ;
( void ) ohci_readl ( ohci , & ohci - > regs - > intrdisable ) ;
2005-04-16 15:20:36 -07:00
2008-06-23 09:09:37 +02:00
clear_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ;
2011-08-12 20:12:33 +02:00
alchemy_usb_control ( ALCHEMY_USB_OHCI0 , 0 ) ;
2008-06-23 09:09:37 +02:00
bail :
spin_unlock_irqrestore ( & ohci - > lock , flags ) ;
return rc ;
2005-04-16 15:20:36 -07:00
}
2008-06-23 09:09:37 +02:00
2009-07-29 19:13:13 +02:00
static int ohci_hcd_au1xxx_drv_resume ( struct device * dev )
2005-04-16 15:20:36 -07:00
{
2009-07-29 19:13:13 +02:00
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2008-06-23 09:09:37 +02:00
2011-08-12 20:12:33 +02:00
alchemy_usb_control ( ALCHEMY_USB_OHCI0 , 1 ) ;
2008-06-23 09:09:37 +02:00
set_bit ( HCD_FLAG_HW_ACCESSIBLE , & hcd - > flags ) ;
ohci_finish_controller_resume ( hcd ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2009-07-29 19:13:13 +02:00
2009-12-14 18:00:08 -08:00
static const struct dev_pm_ops au1xxx_ohci_pmops = {
2009-07-29 19:13:13 +02:00
. suspend = ohci_hcd_au1xxx_drv_suspend ,
. resume = ohci_hcd_au1xxx_drv_resume ,
} ;
# define AU1XXX_OHCI_PMOPS &au1xxx_ohci_pmops
2008-06-23 09:09:37 +02:00
# else
2009-07-29 19:13:13 +02:00
# define AU1XXX_OHCI_PMOPS NULL
2008-06-23 09:09:37 +02:00
# endif
2005-04-16 15:20:36 -07:00
2005-11-09 22:32:44 +00:00
static struct platform_driver ohci_hcd_au1xxx_driver = {
2005-04-16 15:20:36 -07:00
. probe = ohci_hcd_au1xxx_drv_probe ,
. remove = ohci_hcd_au1xxx_drv_remove ,
2006-12-05 03:18:31 -08:00
. shutdown = usb_hcd_platform_shutdown ,
2005-11-09 22:32:44 +00:00
. driver = {
. name = " au1xxx-ohci " ,
. owner = THIS_MODULE ,
2009-07-29 19:13:13 +02:00
. pm = AU1XXX_OHCI_PMOPS ,
2005-11-09 22:32:44 +00:00
} ,
2005-04-16 15:20:36 -07:00
} ;
2008-04-10 21:29:22 -07:00
MODULE_ALIAS ( " platform:au1xxx-ohci " ) ;