2010-11-10 12:03:18 +03:00
/*
2013-04-02 20:24:00 +04:00
* Driver for EHCI HCD on SPEAr SOC
2010-11-10 12:03:18 +03:00
*
* Copyright ( C ) 2010 ST Micro Electronics ,
* Deepak Sikri < deepak . sikri @ st . com >
*
* Based on various ehci - * . c drivers
*
* 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/clk.h>
2013-04-02 20:24:00 +04:00
# include <linux/dma-mapping.h>
# include <linux/io.h>
2012-02-24 13:19:31 +04:00
# include <linux/jiffies.h>
2013-04-02 20:24:00 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
2012-04-16 07:38:08 +04:00
# include <linux/of.h>
2012-02-24 13:19:31 +04:00
# include <linux/platform_device.h>
# include <linux/pm.h>
2013-04-02 20:24:00 +04:00
# include <linux/usb.h>
# include <linux/usb/hcd.h>
2010-11-10 12:03:18 +03:00
2013-04-02 20:24:00 +04:00
# include "ehci.h"
2010-11-10 12:03:18 +03:00
2013-04-02 20:24:00 +04:00
# define DRIVER_DESC "EHCI SPEAr driver"
2010-11-10 12:03:18 +03:00
2013-04-02 20:24:00 +04:00
static const char hcd_name [ ] = " SPEAr-ehci " ;
2010-11-10 12:03:18 +03:00
2013-04-02 20:24:00 +04:00
struct spear_ehci {
struct clk * clk ;
} ;
2010-11-10 12:03:18 +03:00
2013-04-02 20:24:00 +04:00
# define to_spear_ehci(hcd) (struct spear_ehci *)(hcd_to_ehci(hcd)->priv)
2010-11-10 12:03:18 +03:00
2013-04-02 20:24:00 +04:00
static struct hc_driver __read_mostly ehci_spear_hc_driver ;
2010-11-10 12:03:18 +03:00
2013-03-26 10:48:08 +04:00
# ifdef CONFIG_PM_SLEEP
2012-02-24 13:19:31 +04:00
static int ehci_spear_drv_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 ) ;
return ehci_suspend ( hcd , do_wakeup ) ;
2012-02-24 13:19:31 +04:00
}
static int ehci_spear_drv_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-02-24 13:19:31 +04:00
return 0 ;
}
2013-03-26 10:48:08 +04:00
# endif /* CONFIG_PM_SLEEP */
2012-02-24 13:19:31 +04:00
static SIMPLE_DEV_PM_OPS ( ehci_spear_pm_ops , ehci_spear_drv_suspend ,
ehci_spear_drv_resume ) ;
2010-11-10 12:03:18 +03:00
static int spear_ehci_hcd_drv_probe ( struct platform_device * pdev )
{
struct usb_hcd * hcd ;
2013-04-02 20:24:00 +04:00
struct spear_ehci * sehci ;
2010-11-10 12:03:18 +03:00
struct resource * res ;
struct clk * usbh_clk ;
const struct hc_driver * driver = & ehci_spear_hc_driver ;
int irq , retval ;
if ( usb_disabled ( ) )
return - ENODEV ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
retval = irq ;
2012-11-08 19:07:59 +04:00
goto fail ;
2010-11-10 12:03:18 +03:00
}
2012-04-16 07:38:08 +04:00
/*
* Right now device - tree probed devices don ' t get dma_mask set .
* Since shared usb code relies on it , set it here for now .
* Once we have dma capability bindings this can go away .
*/
if ( ! pdev - > dev . dma_mask )
2013-05-08 02:53:52 +04:00
pdev - > dev . dma_mask = & pdev - > dev . coherent_dma_mask ;
if ( ! pdev - > dev . coherent_dma_mask )
pdev - > dev . coherent_dma_mask = DMA_BIT_MASK ( 32 ) ;
2012-04-16 07:38:08 +04:00
2012-11-08 19:07:59 +04:00
usbh_clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2010-11-10 12:03:18 +03:00
if ( IS_ERR ( usbh_clk ) ) {
dev_err ( & pdev - > dev , " Error getting interface clock \n " ) ;
retval = PTR_ERR ( usbh_clk ) ;
2012-11-08 19:07:59 +04:00
goto fail ;
2010-11-10 12:03:18 +03:00
}
hcd = usb_create_hcd ( driver , & pdev - > dev , dev_name ( & pdev - > dev ) ) ;
if ( ! hcd ) {
retval = - ENOMEM ;
2012-11-08 19:07:59 +04:00
goto fail ;
2010-11-10 12:03:18 +03:00
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
retval = - ENODEV ;
2012-11-08 19:07:59 +04:00
goto err_put_hcd ;
2010-11-10 12:03:18 +03:00
}
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
2012-11-08 19:07:59 +04:00
if ( ! devm_request_mem_region ( & pdev - > dev , hcd - > rsrc_start , hcd - > rsrc_len ,
2010-11-10 12:03:18 +03:00
driver - > description ) ) {
retval = - EBUSY ;
2012-11-08 19:07:59 +04:00
goto err_put_hcd ;
2010-11-10 12:03:18 +03:00
}
2012-11-08 19:07:59 +04:00
hcd - > regs = devm_ioremap ( & pdev - > dev , hcd - > rsrc_start , hcd - > rsrc_len ) ;
2010-11-10 12:03:18 +03:00
if ( hcd - > regs = = NULL ) {
dev_dbg ( & pdev - > dev , " error mapping memory \n " ) ;
retval = - ENOMEM ;
2012-11-08 19:07:59 +04:00
goto err_put_hcd ;
2010-11-10 12:03:18 +03:00
}
2013-04-02 20:24:00 +04:00
sehci = to_spear_ehci ( hcd ) ;
sehci - > clk = usbh_clk ;
/* registers start at offset 0x0 */
hcd_to_ehci ( hcd ) - > caps = hcd - > regs ;
2010-11-10 12:03:18 +03:00
2013-04-02 20:24:00 +04:00
clk_prepare_enable ( sehci - > clk ) ;
2011-09-07 12:10:52 +04:00
retval = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
2010-11-10 12:03:18 +03:00
if ( retval )
2012-11-08 19:07:59 +04:00
goto err_stop_ehci ;
2010-11-10 12:03:18 +03:00
return retval ;
2012-11-08 19:07:59 +04:00
err_stop_ehci :
2013-04-02 20:24:00 +04:00
clk_disable_unprepare ( sehci - > clk ) ;
2012-11-08 19:07:59 +04:00
err_put_hcd :
2010-11-10 12:03:18 +03:00
usb_put_hcd ( hcd ) ;
2012-11-08 19:07:59 +04:00
fail :
2010-11-10 12:03:18 +03:00
dev_err ( & pdev - > dev , " init fail, %d \n " , retval ) ;
return retval ;
}
static int spear_ehci_hcd_drv_remove ( struct platform_device * pdev )
{
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
2013-04-02 20:24:00 +04:00
struct spear_ehci * sehci = to_spear_ehci ( hcd ) ;
2010-11-10 12:03:18 +03:00
usb_remove_hcd ( hcd ) ;
2013-04-02 20:24:00 +04:00
if ( sehci - > clk )
clk_disable_unprepare ( sehci - > clk ) ;
2010-11-10 12:03:18 +03:00
usb_put_hcd ( hcd ) ;
return 0 ;
}
2012-11-19 22:24:34 +04:00
static struct of_device_id spear_ehci_id_table [ ] = {
2012-04-16 07:38:08 +04:00
{ . compatible = " st,spear600-ehci " , } ,
{ } ,
} ;
2010-11-10 12:03:18 +03:00
static struct platform_driver spear_ehci_hcd_driver = {
. probe = spear_ehci_hcd_drv_probe ,
. remove = spear_ehci_hcd_drv_remove ,
. shutdown = usb_hcd_platform_shutdown ,
. driver = {
. name = " spear-ehci " ,
2012-02-24 13:19:31 +04:00
. bus = & platform_bus_type ,
. pm = & ehci_spear_pm_ops ,
2013-05-21 15:47:17 +04:00
. of_match_table = spear_ehci_id_table ,
2010-11-10 12:03:18 +03:00
}
} ;
2013-04-02 20:24:00 +04:00
static const struct ehci_driver_overrides spear_overrides __initdata = {
. extra_priv_size = sizeof ( struct spear_ehci ) ,
} ;
static int __init ehci_spear_init ( void )
{
if ( usb_disabled ( ) )
return - ENODEV ;
pr_info ( " %s: " DRIVER_DESC " \n " , hcd_name ) ;
ehci_init_driver ( & ehci_spear_hc_driver , & spear_overrides ) ;
return platform_driver_register ( & spear_ehci_hcd_driver ) ;
}
module_init ( ehci_spear_init ) ;
static void __exit ehci_spear_cleanup ( void )
{
platform_driver_unregister ( & spear_ehci_hcd_driver ) ;
}
module_exit ( ehci_spear_cleanup ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2010-11-10 12:03:18 +03:00
MODULE_ALIAS ( " platform:spear-ehci " ) ;
2013-04-02 20:24:00 +04:00
MODULE_AUTHOR ( " Deepak Sikri " ) ;
MODULE_LICENSE ( " GPL " ) ;