2010-11-02 00:03:27 +03:00
/*
* SuperH EHCI host controller driver
*
* Copyright ( C ) 2010 Paul Mundt
*
* Based on ohci - sh . c and ehci - atmel . c .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/platform_device.h>
# include <linux/clk.h>
2012-04-12 10:48:49 +04:00
# include <linux/platform_data/ehci-sh.h>
2010-11-02 00:03:27 +03:00
struct ehci_sh_priv {
struct clk * iclk , * fclk ;
struct usb_hcd * hcd ;
} ;
static int ehci_sh_reset ( struct usb_hcd * hcd )
{
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
ehci - > caps = hcd - > regs ;
2012-10-31 21:21:06 +04:00
return ehci_setup ( hcd ) ;
2010-11-02 00:03:27 +03:00
}
static const struct hc_driver ehci_sh_hc_driver = {
. description = hcd_name ,
. product_desc = " SuperH EHCI " ,
. hcd_priv_size = sizeof ( struct ehci_hcd ) ,
/*
* generic hardware linkage
*/
. irq = ehci_irq ,
. flags = HCD_USB2 | HCD_MEMORY ,
/*
* basic lifecycle operations
*/
. reset = ehci_sh_reset ,
. start = ehci_run ,
. stop = ehci_stop ,
. shutdown = ehci_shutdown ,
/*
* managing i / o requests and associated device resources
*/
. urb_enqueue = ehci_urb_enqueue ,
. urb_dequeue = ehci_urb_dequeue ,
. endpoint_disable = ehci_endpoint_disable ,
2010-11-26 10:48:31 +03:00
. endpoint_reset = ehci_endpoint_reset ,
2010-11-02 00:03:27 +03:00
/*
* scheduling support
*/
. get_frame_number = ehci_get_frame ,
/*
* root hub support
*/
. hub_status_data = ehci_hub_status_data ,
. hub_control = ehci_hub_control ,
# ifdef 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 ,
2010-11-26 10:48:31 +03:00
. clear_tt_buffer_complete = ehci_clear_tt_buffer_complete ,
2010-11-02 00:03:27 +03:00
} ;
static int ehci_hcd_sh_probe ( struct platform_device * pdev )
{
const struct hc_driver * driver = & ehci_sh_hc_driver ;
struct resource * res ;
struct ehci_sh_priv * priv ;
2012-04-12 10:48:49 +04:00
struct ehci_sh_platdata * pdata ;
2010-11-02 00:03:27 +03:00
struct usb_hcd * hcd ;
int irq , ret ;
if ( usb_disabled ( ) )
return - ENODEV ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev ,
" Found HC with no register addr. Check %s setup! \n " ,
dev_name ( & pdev - > dev ) ) ;
ret = - ENODEV ;
goto fail_create_hcd ;
}
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < = 0 ) {
dev_err ( & pdev - > dev ,
" Found HC with no IRQ. Check %s setup! \n " ,
dev_name ( & pdev - > dev ) ) ;
ret = - ENODEV ;
goto fail_create_hcd ;
}
2012-06-12 04:34:33 +04:00
pdata = pdev - > dev . platform_data ;
2012-04-12 10:48:49 +04:00
2010-11-02 00:03:27 +03:00
/* initialize hcd */
hcd = usb_create_hcd ( & ehci_sh_hc_driver , & pdev - > dev ,
dev_name ( & pdev - > dev ) ) ;
if ( ! hcd ) {
ret = - ENOMEM ;
goto fail_create_hcd ;
}
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
2013-01-21 14:09:22 +04:00
hcd - > regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( hcd - > regs ) ) {
ret = PTR_ERR ( hcd - > regs ) ;
2012-07-30 18:43:42 +04:00
goto fail_request_resource ;
2010-11-02 00:03:27 +03:00
}
2012-07-30 18:43:42 +04:00
priv = devm_kzalloc ( & pdev - > dev , sizeof ( struct ehci_sh_priv ) ,
GFP_KERNEL ) ;
2010-11-02 00:03:27 +03:00
if ( ! priv ) {
dev_dbg ( & pdev - > dev , " error allocating priv data \n " ) ;
ret = - ENOMEM ;
2012-07-30 18:43:42 +04:00
goto fail_request_resource ;
2010-11-02 00:03:27 +03:00
}
/* These are optional, we don't care if they fail */
2012-07-30 18:43:42 +04:00
priv - > fclk = devm_clk_get ( & pdev - > dev , " usb_fck " ) ;
2010-11-02 00:03:27 +03:00
if ( IS_ERR ( priv - > fclk ) )
priv - > fclk = NULL ;
2012-07-30 18:43:42 +04:00
priv - > iclk = devm_clk_get ( & pdev - > dev , " usb_ick " ) ;
2010-11-02 00:03:27 +03:00
if ( IS_ERR ( priv - > iclk ) )
priv - > iclk = NULL ;
clk_enable ( priv - > fclk ) ;
clk_enable ( priv - > iclk ) ;
2012-04-12 10:48:49 +04:00
if ( pdata & & pdata - > phy_init )
pdata - > phy_init ( ) ;
2011-09-07 12:10:52 +04:00
ret = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
2010-11-02 00:03:27 +03:00
if ( ret ! = 0 ) {
dev_err ( & pdev - > dev , " Failed to add hcd " ) ;
goto fail_add_hcd ;
}
priv - > hcd = hcd ;
platform_set_drvdata ( pdev , priv ) ;
return ret ;
fail_add_hcd :
clk_disable ( priv - > iclk ) ;
clk_disable ( priv - > fclk ) ;
fail_request_resource :
usb_put_hcd ( hcd ) ;
fail_create_hcd :
dev_err ( & pdev - > dev , " init %s fail, %d \n " , dev_name ( & pdev - > dev ) , ret ) ;
return ret ;
}
2013-02-24 12:55:07 +04:00
static int ehci_hcd_sh_remove ( struct platform_device * pdev )
2010-11-02 00:03:27 +03:00
{
struct ehci_sh_priv * priv = platform_get_drvdata ( pdev ) ;
struct usb_hcd * hcd = priv - > hcd ;
usb_remove_hcd ( hcd ) ;
usb_put_hcd ( hcd ) ;
platform_set_drvdata ( pdev , NULL ) ;
clk_disable ( priv - > fclk ) ;
clk_disable ( priv - > iclk ) ;
return 0 ;
}
2010-11-26 10:43:38 +03:00
static void ehci_hcd_sh_shutdown ( struct platform_device * pdev )
{
struct ehci_sh_priv * priv = platform_get_drvdata ( pdev ) ;
struct usb_hcd * hcd = priv - > hcd ;
if ( hcd - > driver - > shutdown )
hcd - > driver - > shutdown ( hcd ) ;
}
2010-11-02 00:03:27 +03:00
static struct platform_driver ehci_hcd_sh_driver = {
. probe = ehci_hcd_sh_probe ,
2013-02-24 12:55:07 +04:00
. remove = ehci_hcd_sh_remove
2010-11-26 10:43:38 +03:00
. shutdown = ehci_hcd_sh_shutdown ,
2010-11-02 00:03:27 +03:00
. driver = {
. name = " sh_ehci " ,
. owner = THIS_MODULE ,
} ,
} ;
MODULE_ALIAS ( " platform:sh_ehci " ) ;