2012-03-13 04:04:48 +04:00
/*
* Generic platform ehci driver
*
* Copyright 2007 Steven Brown < sbrown @ cortland . com >
* Copyright 2010 - 2012 Hauke Mehrtens < hauke @ hauke - m . de >
*
* Derived from the ohci - ssb driver
* Copyright 2007 Michael Buesch < m @ bues . ch >
*
* Derived from the EHCI - PCI driver
* Copyright ( c ) 2000 - 2004 by David Brownell
*
* Derived from the ohci - pci driver
* Copyright 1999 Roman Weissgaerber
* Copyright 2000 - 2002 David Brownell
* Copyright 1999 Linus Torvalds
* Copyright 1999 Gregory P . Smith
*
* Licensed under the GNU / GPL . See COPYING for details .
*/
# include <linux/platform_device.h>
# include <linux/usb/ehci_pdriver.h>
static int ehci_platform_reset ( struct usb_hcd * hcd )
{
struct platform_device * pdev = to_platform_device ( hcd - > self . controller ) ;
struct usb_ehci_pdata * pdata = pdev - > dev . platform_data ;
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
int retval ;
hcd - > has_tt = pdata - > has_tt ;
ehci - > has_synopsys_hc_bug = pdata - > has_synopsys_hc_bug ;
ehci - > big_endian_desc = pdata - > big_endian_desc ;
ehci - > big_endian_mmio = pdata - > big_endian_mmio ;
ehci - > caps = hcd - > regs + pdata - > caps_offset ;
retval = ehci_setup ( hcd ) ;
if ( retval )
return retval ;
if ( pdata - > port_power_on )
ehci_port_power ( ehci , 1 ) ;
if ( pdata - > port_power_off )
ehci_port_power ( ehci , 0 ) ;
return 0 ;
}
static const struct hc_driver ehci_platform_hc_driver = {
. description = hcd_name ,
. product_desc = " Generic Platform EHCI Controller " ,
. hcd_priv_size = sizeof ( struct ehci_hcd ) ,
. irq = ehci_irq ,
. flags = HCD_MEMORY | HCD_USB2 ,
. reset = ehci_platform_reset ,
. start = ehci_run ,
. stop = ehci_stop ,
. shutdown = ehci_shutdown ,
. 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 ,
. hub_control = ehci_hub_control ,
# if defined(CONFIG_PM)
. bus_suspend = ehci_bus_suspend ,
. bus_resume = ehci_bus_resume ,
# endif
. relinquish_port = ehci_relinquish_port ,
. port_handed_over = ehci_port_handed_over ,
. clear_tt_buffer_complete = ehci_clear_tt_buffer_complete ,
} ;
static int __devinit ehci_platform_probe ( struct platform_device * dev )
{
struct usb_hcd * hcd ;
struct resource * res_mem ;
int irq ;
int err = - ENOMEM ;
BUG_ON ( ! dev - > dev . platform_data ) ;
if ( usb_disabled ( ) )
return - ENODEV ;
irq = platform_get_irq ( dev , 0 ) ;
if ( irq < 0 ) {
2012-04-18 19:04:46 +04:00
pr_err ( " no irq provided " ) ;
2012-03-13 04:04:48 +04:00
return irq ;
}
res_mem = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
if ( ! res_mem ) {
2012-04-18 19:04:46 +04:00
pr_err ( " no memory recourse provided " ) ;
2012-03-13 04:04:48 +04:00
return - ENXIO ;
}
hcd = usb_create_hcd ( & ehci_platform_hc_driver , & dev - > dev ,
dev_name ( & dev - > dev ) ) ;
if ( ! hcd )
return - ENOMEM ;
hcd - > rsrc_start = res_mem - > start ;
hcd - > rsrc_len = resource_size ( res_mem ) ;
if ( ! request_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len , hcd_name ) ) {
pr_err ( " controller already in use " ) ;
err = - EBUSY ;
goto err_put_hcd ;
}
hcd - > regs = ioremap_nocache ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
if ( ! hcd - > regs )
goto err_release_region ;
err = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
if ( err )
goto err_iounmap ;
platform_set_drvdata ( dev , hcd ) ;
return err ;
err_iounmap :
iounmap ( hcd - > regs ) ;
err_release_region :
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
err_put_hcd :
usb_put_hcd ( hcd ) ;
return err ;
}
static int __devexit ehci_platform_remove ( struct platform_device * dev )
{
struct usb_hcd * hcd = platform_get_drvdata ( dev ) ;
usb_remove_hcd ( hcd ) ;
iounmap ( hcd - > regs ) ;
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
usb_put_hcd ( hcd ) ;
platform_set_drvdata ( dev , NULL ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int ehci_platform_suspend ( struct device * dev )
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2012-06-28 19:19:02 +04:00
bool do_wakeup = device_may_wakeup ( dev ) ;
2012-03-13 04:04:48 +04:00
2012-06-28 19:19:02 +04:00
return ehci_suspend ( hcd , do_wakeup ) ;
2012-03-13 04:04:48 +04:00
}
static int ehci_platform_resume ( struct device * dev )
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2012-06-28 19:19:02 +04:00
ehci_resume ( hcd , false ) ;
2012-03-13 04:04:48 +04:00
return 0 ;
}
# else /* !CONFIG_PM */
# define ehci_platform_suspend NULL
# define ehci_platform_resume NULL
# endif /* CONFIG_PM */
static const struct platform_device_id ehci_platform_table [ ] = {
{ " ehci-platform " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( platform , ehci_platform_table ) ;
static const struct dev_pm_ops ehci_platform_pm_ops = {
. suspend = ehci_platform_suspend ,
. resume = ehci_platform_resume ,
} ;
static struct platform_driver ehci_platform_driver = {
. id_table = ehci_platform_table ,
. probe = ehci_platform_probe ,
. remove = __devexit_p ( ehci_platform_remove ) ,
. shutdown = usb_hcd_platform_shutdown ,
. driver = {
. owner = THIS_MODULE ,
. name = " ehci-platform " ,
. pm = & ehci_platform_pm_ops ,
}
} ;