2011-05-03 20:11:58 +02:00
/*
* Driver for Aeroflex Gaisler GRLIB GRUSBHC EHCI host controller
*
* GRUSBHC is typically found on LEON / GRLIB SoCs
*
* ( c ) Jan Andersson < jan @ gaisler . com >
*
* Based on ehci - ppc - of . c which is :
* ( c ) Valentine Barshak < vbarshak @ ru . mvista . com >
* and in turn based on " ehci-ppc-soc.c " by Stefan Roese < sr @ denx . de >
* and " ohci-ppc-of.c " by Sylvain Munaut < tnt @ 246 tNt . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2013-01-21 11:09:22 +01:00
# include <linux/err.h>
2011-05-03 20:11:58 +02:00
# include <linux/signal.h>
# include <linux/of_irq.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# define GRUSBHC_HCIVERSION 0x0100 /* Known value of cap. reg. HCIVERSION */
static const struct hc_driver ehci_grlib_hc_driver = {
. description = hcd_name ,
. product_desc = " GRLIB GRUSBHC EHCI " ,
. hcd_priv_size = sizeof ( struct ehci_hcd ) ,
/*
* generic hardware linkage
*/
. irq = ehci_irq ,
. flags = HCD_MEMORY | HCD_USB2 ,
/*
* basic lifecycle operations
*/
2012-10-31 13:21:06 -04:00
. reset = ehci_setup ,
2011-05-03 20:11:58 +02:00
. 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 ,
. endpoint_reset = ehci_endpoint_reset ,
/*
* 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 ,
. clear_tt_buffer_complete = ehci_clear_tt_buffer_complete ,
} ;
2012-11-19 13:21:48 -05:00
static int ehci_hcd_grlib_probe ( struct platform_device * op )
2011-05-03 20:11:58 +02:00
{
struct device_node * dn = op - > dev . of_node ;
struct usb_hcd * hcd ;
struct ehci_hcd * ehci = NULL ;
struct resource res ;
u32 hc_capbase ;
int irq ;
int rv ;
if ( usb_disabled ( ) )
return - ENODEV ;
dev_dbg ( & op - > dev , " initializing GRUSBHC EHCI USB Controller \n " ) ;
rv = of_address_to_resource ( dn , 0 , & res ) ;
if ( rv )
return rv ;
/* usb_create_hcd requires dma_mask != NULL */
op - > dev . dma_mask = & op - > dev . coherent_dma_mask ;
hcd = usb_create_hcd ( & ehci_grlib_hc_driver , & op - > dev ,
" GRUSBHC EHCI USB " ) ;
if ( ! hcd )
return - ENOMEM ;
hcd - > rsrc_start = res . start ;
2011-06-09 09:13:32 -07:00
hcd - > rsrc_len = resource_size ( & res ) ;
2011-05-03 20:11:58 +02:00
irq = irq_of_parse_and_map ( dn , 0 ) ;
if ( irq = = NO_IRQ ) {
printk ( KERN_ERR " %s: irq_of_parse_and_map failed \n " , __FILE__ ) ;
rv = - EBUSY ;
goto err_irq ;
}
2013-01-21 11:09:22 +01:00
hcd - > regs = devm_ioremap_resource ( & op - > dev , & res ) ;
if ( IS_ERR ( hcd - > regs ) ) {
rv = PTR_ERR ( hcd - > regs ) ;
2011-05-03 20:11:58 +02:00
goto err_ioremap ;
}
ehci = hcd_to_ehci ( hcd ) ;
ehci - > caps = hcd - > regs ;
/* determine endianness of this implementation */
hc_capbase = ehci_readl ( ehci , & ehci - > caps - > hc_capbase ) ;
if ( HC_VERSION ( ehci , hc_capbase ) ! = GRUSBHC_HCIVERSION ) {
ehci - > big_endian_mmio = 1 ;
ehci - > big_endian_desc = 1 ;
ehci - > big_endian_capbase = 1 ;
}
rv = usb_add_hcd ( hcd , irq , 0 ) ;
if ( rv )
2012-07-29 21:46:08 +02:00
goto err_ioremap ;
2011-05-03 20:11:58 +02:00
return 0 ;
err_ioremap :
irq_dispose_mapping ( irq ) ;
err_irq :
usb_put_hcd ( hcd ) ;
return rv ;
}
static int ehci_hcd_grlib_remove ( struct platform_device * op )
{
2013-05-23 19:18:39 +09:00
struct usb_hcd * hcd = platform_get_drvdata ( op ) ;
2011-05-03 20:11:58 +02:00
dev_dbg ( & op - > dev , " stopping GRLIB GRUSBHC EHCI USB Controller \n " ) ;
usb_remove_hcd ( hcd ) ;
irq_dispose_mapping ( hcd - > irq ) ;
usb_put_hcd ( hcd ) ;
return 0 ;
}
static void ehci_hcd_grlib_shutdown ( struct platform_device * op )
{
2013-05-23 19:18:39 +09:00
struct usb_hcd * hcd = platform_get_drvdata ( op ) ;
2011-05-03 20:11:58 +02:00
if ( hcd - > driver - > shutdown )
hcd - > driver - > shutdown ( hcd ) ;
}
static const struct of_device_id ehci_hcd_grlib_of_match [ ] = {
{
. name = " GAISLER_EHCI " ,
} ,
{
. name = " 01_026 " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ehci_hcd_grlib_of_match ) ;
static struct platform_driver ehci_grlib_driver = {
. probe = ehci_hcd_grlib_probe ,
. remove = ehci_hcd_grlib_remove ,
. shutdown = ehci_hcd_grlib_shutdown ,
. driver = {
. name = " grlib-ehci " ,
. owner = THIS_MODULE ,
. of_match_table = ehci_hcd_grlib_of_match ,
} ,
} ;