2012-03-13 01:04:48 +01: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 .
*/
2013-03-27 21:44:22 +00:00
# include <linux/dma-mapping.h>
2013-01-21 11:09:22 +01:00
# include <linux/err.h>
2012-11-01 11:13:08 -04:00
# include <linux/kernel.h>
2012-11-02 10:13:24 -04:00
# include <linux/hrtimer.h>
# include <linux/io.h>
2012-11-01 11:13:08 -04:00
# include <linux/module.h>
2013-03-27 21:44:22 +00:00
# include <linux/of.h>
2012-03-13 01:04:48 +01:00
# include <linux/platform_device.h>
2012-11-01 11:13:08 -04:00
# include <linux/usb.h>
# include <linux/usb/hcd.h>
2012-03-13 01:04:48 +01:00
# include <linux/usb/ehci_pdriver.h>
2012-11-01 11:13:08 -04:00
# include "ehci.h"
# define DRIVER_DESC "EHCI generic platform driver"
static const char hcd_name [ ] = " ehci-platform " ;
2012-03-13 01:04:48 +01:00
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 ;
2012-10-08 15:11:21 +02:00
if ( pdata - > no_io_watchdog )
ehci - > need_io_watchdog = 0 ;
2012-03-13 01:04:48 +01:00
return 0 ;
}
2012-11-01 11:13:08 -04:00
static struct hc_driver __read_mostly ehci_platform_hc_driver ;
2012-03-13 01:04:48 +01:00
2013-04-22 09:44:56 -07:00
static const struct ehci_driver_overrides platform_overrides __initconst = {
2012-11-01 11:13:08 -04:00
. reset = ehci_platform_reset ,
2012-03-13 01:04:48 +01:00
} ;
2013-03-27 21:44:22 +00:00
static struct usb_ehci_pdata ehci_platform_defaults ;
2012-11-19 13:21:48 -05:00
static int ehci_platform_probe ( struct platform_device * dev )
2012-03-13 01:04:48 +01:00
{
struct usb_hcd * hcd ;
struct resource * res_mem ;
2013-03-27 21:44:22 +00:00
struct usb_ehci_pdata * pdata ;
2012-03-13 01:04:48 +01:00
int irq ;
int err = - ENOMEM ;
if ( usb_disabled ( ) )
return - ENODEV ;
2013-03-27 21:44:22 +00:00
/*
* use reasonable defaults so platforms don ' t have to provide these .
* with DT probing on ARM , none of these are set .
*/
if ( ! dev - > dev . platform_data )
dev - > dev . platform_data = & ehci_platform_defaults ;
if ( ! dev - > dev . dma_mask )
dev - > dev . dma_mask = & dev - > dev . coherent_dma_mask ;
if ( ! dev - > dev . coherent_dma_mask )
dev - > dev . coherent_dma_mask = DMA_BIT_MASK ( 32 ) ;
pdata = dev - > dev . platform_data ;
2012-03-13 01:04:48 +01:00
irq = platform_get_irq ( dev , 0 ) ;
if ( irq < 0 ) {
2012-10-08 15:11:41 +02:00
dev_err ( & dev - > dev , " no irq provided " ) ;
2012-03-13 01:04:48 +01:00
return irq ;
}
res_mem = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
if ( ! res_mem ) {
2012-10-08 15:11:43 +02:00
dev_err ( & dev - > dev , " no memory resource provided " ) ;
2012-03-13 01:04:48 +01:00
return - ENXIO ;
}
2012-08-06 18:08:39 -07:00
if ( pdata - > power_on ) {
err = pdata - > power_on ( dev ) ;
if ( err < 0 )
return err ;
}
2012-03-13 01:04:48 +01:00
hcd = usb_create_hcd ( & ehci_platform_hc_driver , & dev - > dev ,
dev_name ( & dev - > dev ) ) ;
2012-08-06 18:08:39 -07:00
if ( ! hcd ) {
err = - ENOMEM ;
goto err_power ;
}
2012-03-13 01:04:48 +01:00
hcd - > rsrc_start = res_mem - > start ;
hcd - > rsrc_len = resource_size ( res_mem ) ;
2013-01-21 11:09:22 +01:00
hcd - > regs = devm_ioremap_resource ( & dev - > dev , res_mem ) ;
if ( IS_ERR ( hcd - > regs ) ) {
err = PTR_ERR ( hcd - > regs ) ;
2012-10-08 15:11:45 +02:00
goto err_put_hcd ;
2012-08-14 08:47:38 +02:00
}
2012-03-13 01:04:48 +01:00
err = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
if ( err )
2012-10-08 15:11:45 +02:00
goto err_put_hcd ;
2012-03-13 01:04:48 +01:00
platform_set_drvdata ( dev , hcd ) ;
return err ;
err_put_hcd :
usb_put_hcd ( hcd ) ;
2012-08-06 18:08:39 -07:00
err_power :
if ( pdata - > power_off )
pdata - > power_off ( dev ) ;
2012-03-13 01:04:48 +01:00
return err ;
}
2012-11-19 13:26:20 -05:00
static int ehci_platform_remove ( struct platform_device * dev )
2012-03-13 01:04:48 +01:00
{
struct usb_hcd * hcd = platform_get_drvdata ( dev ) ;
2012-08-06 18:08:39 -07:00
struct usb_ehci_pdata * pdata = dev - > dev . platform_data ;
2012-03-13 01:04:48 +01:00
usb_remove_hcd ( hcd ) ;
usb_put_hcd ( hcd ) ;
platform_set_drvdata ( dev , NULL ) ;
2012-08-06 18:08:39 -07:00
if ( pdata - > power_off )
pdata - > power_off ( dev ) ;
2013-03-27 21:44:22 +00:00
if ( pdata = = & ehci_platform_defaults )
dev - > dev . platform_data = NULL ;
2012-03-13 01:04:48 +01:00
return 0 ;
}
# ifdef CONFIG_PM
static int ehci_platform_suspend ( struct device * dev )
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2012-08-06 18:08:39 -07:00
struct usb_ehci_pdata * pdata = dev - > platform_data ;
struct platform_device * pdev =
container_of ( dev , struct platform_device , dev ) ;
2012-06-28 11:19:02 -04:00
bool do_wakeup = device_may_wakeup ( dev ) ;
2012-08-06 18:08:39 -07:00
int ret ;
ret = ehci_suspend ( hcd , do_wakeup ) ;
2012-03-13 01:04:48 +01:00
2012-08-06 18:08:39 -07:00
if ( pdata - > power_suspend )
pdata - > power_suspend ( pdev ) ;
return ret ;
2012-03-13 01:04:48 +01:00
}
static int ehci_platform_resume ( struct device * dev )
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2012-08-06 18:08:39 -07:00
struct usb_ehci_pdata * pdata = dev - > platform_data ;
struct platform_device * pdev =
container_of ( dev , struct platform_device , dev ) ;
if ( pdata - > power_on ) {
int err = pdata - > power_on ( pdev ) ;
if ( err < 0 )
return err ;
}
2012-03-13 01:04:48 +01:00
2012-06-28 11:19:02 -04:00
ehci_resume ( hcd , false ) ;
2012-03-13 01:04:48 +01:00
return 0 ;
}
# else /* !CONFIG_PM */
# define ehci_platform_suspend NULL
# define ehci_platform_resume NULL
# endif /* CONFIG_PM */
2013-03-27 21:44:22 +00:00
static const struct of_device_id vt8500_ehci_ids [ ] = {
{ . compatible = " via,vt8500-ehci " , } ,
{ . compatible = " wm,prizm-ehci " , } ,
{ }
} ;
2012-03-13 01:04:48 +01:00
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 ,
2012-11-19 13:21:08 -05:00
. remove = ehci_platform_remove ,
2012-03-13 01:04:48 +01:00
. shutdown = usb_hcd_platform_shutdown ,
. driver = {
. owner = THIS_MODULE ,
. name = " ehci-platform " ,
. pm = & ehci_platform_pm_ops ,
2013-03-27 21:44:22 +00:00
. of_match_table = of_match_ptr ( vt8500_ehci_ids ) ,
2012-03-13 01:04:48 +01:00
}
} ;
2012-11-01 11:13:08 -04:00
static int __init ehci_platform_init ( void )
{
if ( usb_disabled ( ) )
return - ENODEV ;
pr_info ( " %s: " DRIVER_DESC " \n " , hcd_name ) ;
ehci_init_driver ( & ehci_platform_hc_driver , & platform_overrides ) ;
return platform_driver_register ( & ehci_platform_driver ) ;
}
module_init ( ehci_platform_init ) ;
static void __exit ehci_platform_cleanup ( void )
{
platform_driver_unregister ( & ehci_platform_driver ) ;
}
module_exit ( ehci_platform_cleanup ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_AUTHOR ( " Hauke Mehrtens " ) ;
MODULE_AUTHOR ( " Alan Stern " ) ;
MODULE_LICENSE ( " GPL " ) ;