2007-12-01 19:07:04 +03:00
/*
* drivers / usb / host / ehci - orion . c
*
* Tzachi Perelstein < tzachi @ marvell . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2008-03-27 21:51:39 +03:00
# include <linux/mbus.h>
2008-08-09 15:44:58 +04:00
# include <plat/ehci-orion.h>
2007-12-01 19:07:04 +03:00
# define rdl(off) __raw_readl(hcd->regs + (off))
# define wrl(off, val) __raw_writel((val), hcd->regs + (off))
# define USB_CMD 0x140
# define USB_MODE 0x1a8
2008-03-27 21:51:39 +03:00
# define USB_CAUSE 0x310
# define USB_MASK 0x314
# define USB_WINDOW_CTRL(i) (0x320 + ((i) << 4))
# define USB_WINDOW_BASE(i) (0x324 + ((i) << 4))
2007-12-01 19:07:04 +03:00
# define USB_IPG 0x360
# define USB_PHY_PWR_CTRL 0x400
# define USB_PHY_TX_CTRL 0x420
# define USB_PHY_RX_CTRL 0x430
# define USB_PHY_IVREF_CTRL 0x440
# define USB_PHY_TST_GRP_CTRL 0x450
/*
* Implement Orion USB controller specification guidelines
*/
static void orion_usb_setup ( struct usb_hcd * hcd )
{
/*
* Clear interrupt cause and mask
*/
wrl ( USB_CAUSE , 0 ) ;
wrl ( USB_MASK , 0 ) ;
/*
* Reset controller
*/
wrl ( USB_CMD , rdl ( USB_CMD ) | 0x2 ) ;
while ( rdl ( USB_CMD ) & 0x2 ) ;
/*
* GL # USB - 10 : Set IPG for non start of frame packets
* Bits [ 14 : 8 ] = 0xc
*/
wrl ( USB_IPG , ( rdl ( USB_IPG ) & ~ 0x7f00 ) | 0xc00 ) ;
/*
* GL # USB - 9 : USB 2.0 Power Control
* BG_VSEL [ 7 : 6 ] = 0x1
*/
wrl ( USB_PHY_PWR_CTRL , ( rdl ( USB_PHY_PWR_CTRL ) & ~ 0xc0 ) | 0x40 ) ;
/*
* GL # USB - 1 : USB PHY Tx Control - force calibration to ' 8 '
* TXDATA_BLOCK_EN [ 21 ] = 0x1 , EXT_RCAL_EN [ 13 ] = 0x1 , IMP_CAL [ 6 : 3 ] = 0x8
*/
wrl ( USB_PHY_TX_CTRL , ( rdl ( USB_PHY_TX_CTRL ) & ~ 0x78 ) | 0x202040 ) ;
/*
* GL # USB - 3 GL # USB - 9 : USB PHY Rx Control
* RXDATA_BLOCK_LENGHT [ 31 : 30 ] = 0x3 , EDGE_DET_SEL [ 27 : 26 ] = 0 ,
* CDR_FASTLOCK_EN [ 21 ] = 0 , DISCON_THRESHOLD [ 9 : 8 ] = 0 , SQ_THRESH [ 7 : 4 ] = 0x1
*/
wrl ( USB_PHY_RX_CTRL , ( rdl ( USB_PHY_RX_CTRL ) & ~ 0xc2003f0 ) | 0xc0000010 ) ;
/*
* GL # USB - 3 GL # USB - 9 : USB PHY IVREF Control
* PLLVDD12 [ 1 : 0 ] = 0x2 , RXVDD [ 5 : 4 ] = 0x3 , Reserved [ 19 ] = 0
*/
wrl ( USB_PHY_IVREF_CTRL , ( rdl ( USB_PHY_IVREF_CTRL ) & ~ 0x80003 ) | 0x32 ) ;
/*
* GL # USB - 3 GL # USB - 9 : USB PHY Test Group Control
* REG_FIFO_SQ_RST [ 15 ] = 0
*/
wrl ( USB_PHY_TST_GRP_CTRL , rdl ( USB_PHY_TST_GRP_CTRL ) & ~ 0x8000 ) ;
/*
* Stop and reset controller
*/
wrl ( USB_CMD , rdl ( USB_CMD ) & ~ 0x1 ) ;
wrl ( USB_CMD , rdl ( USB_CMD ) | 0x2 ) ;
while ( rdl ( USB_CMD ) & 0x2 ) ;
/*
* GL # USB - 5 Streaming disable REG_USB_MODE [ 4 ] = 1
* TBD : This need to be done after each reset !
* GL # USB - 4 Setup USB Host mode
*/
wrl ( USB_MODE , 0x13 ) ;
}
static int ehci_orion_setup ( struct usb_hcd * hcd )
{
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
int retval ;
retval = ehci_halt ( ehci ) ;
if ( retval )
return retval ;
/*
* data structure init
*/
retval = ehci_init ( hcd ) ;
if ( retval )
return retval ;
2008-05-20 21:08:53 +04:00
hcd - > has_tt = 1 ;
2007-12-01 19:07:04 +03:00
ehci_reset ( ehci ) ;
ehci_port_power ( ehci , 0 ) ;
return retval ;
}
static const struct hc_driver ehci_orion_hc_driver = {
. description = hcd_name ,
. product_desc = " Marvell Orion EHCI " ,
. hcd_priv_size = sizeof ( struct ehci_hcd ) ,
/*
* generic hardware linkage
*/
. irq = ehci_irq ,
. flags = HCD_MEMORY | HCD_USB2 ,
/*
* basic lifecycle operations
*/
. reset = ehci_orion_setup ,
. 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 ,
/*
* scheduling support
*/
. get_frame_number = ehci_get_frame ,
/*
* root hub support
*/
. hub_status_data = ehci_hub_status_data ,
. hub_control = ehci_hub_control ,
. bus_suspend = ehci_bus_suspend ,
. bus_resume = ehci_bus_resume ,
2008-05-21 00:58:11 +04:00
. relinquish_port = ehci_relinquish_port ,
2008-05-21 00:58:29 +04:00
. port_handed_over = ehci_port_handed_over ,
2007-12-01 19:07:04 +03:00
} ;
2008-03-27 21:51:39 +03:00
static void __init
ehci_orion_conf_mbus_windows ( struct usb_hcd * hcd ,
struct mbus_dram_target_info * dram )
{
int i ;
for ( i = 0 ; i < 4 ; i + + ) {
wrl ( USB_WINDOW_CTRL ( i ) , 0 ) ;
wrl ( USB_WINDOW_BASE ( i ) , 0 ) ;
}
for ( i = 0 ; i < dram - > num_cs ; i + + ) {
struct mbus_dram_window * cs = dram - > cs + i ;
wrl ( USB_WINDOW_CTRL ( i ) , ( ( cs - > size - 1 ) & 0xffff0000 ) |
( cs - > mbus_attr < < 8 ) |
( dram - > mbus_dram_target_id < < 4 ) | 1 ) ;
wrl ( USB_WINDOW_BASE ( i ) , cs - > base ) ;
}
}
2007-12-01 19:07:04 +03:00
static int __init ehci_orion_drv_probe ( struct platform_device * pdev )
{
2008-03-27 21:51:39 +03:00
struct orion_ehci_data * pd = pdev - > dev . platform_data ;
2007-12-01 19:07:04 +03:00
struct resource * res ;
struct usb_hcd * hcd ;
struct ehci_hcd * ehci ;
void __iomem * regs ;
int irq , err ;
if ( usb_disabled ( ) )
return - ENODEV ;
pr_debug ( " Initializing Orion-SoC USB Host Controller \n " ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < = 0 ) {
dev_err ( & pdev - > dev ,
" Found HC with no IRQ. Check %s setup! \n " ,
2008-05-02 08:02:41 +04:00
dev_name ( & pdev - > dev ) ) ;
2007-12-01 19:07:04 +03:00
err = - ENODEV ;
goto err1 ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev ,
" Found HC with no register addr. Check %s setup! \n " ,
2008-05-02 08:02:41 +04:00
dev_name ( & pdev - > dev ) ) ;
2007-12-01 19:07:04 +03:00
err = - ENODEV ;
goto err1 ;
}
if ( ! request_mem_region ( res - > start , res - > end - res - > start + 1 ,
ehci_orion_hc_driver . description ) ) {
dev_dbg ( & pdev - > dev , " controller already in use \n " ) ;
err = - EBUSY ;
goto err1 ;
}
regs = ioremap ( res - > start , res - > end - res - > start + 1 ) ;
if ( regs = = NULL ) {
dev_dbg ( & pdev - > dev , " error mapping memory \n " ) ;
err = - EFAULT ;
goto err2 ;
}
hcd = usb_create_hcd ( & ehci_orion_hc_driver ,
2008-05-02 08:02:41 +04:00
& pdev - > dev , dev_name ( & pdev - > dev ) ) ;
2007-12-01 19:07:04 +03:00
if ( ! hcd ) {
err = - ENOMEM ;
goto err3 ;
}
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = res - > end - res - > start + 1 ;
hcd - > regs = regs ;
ehci = hcd_to_ehci ( hcd ) ;
ehci - > caps = hcd - > regs + 0x100 ;
ehci - > regs = hcd - > regs + 0x100 +
HC_LENGTH ( ehci_readl ( ehci , & ehci - > caps - > hc_capbase ) ) ;
ehci - > hcs_params = ehci_readl ( ehci , & ehci - > caps - > hcs_params ) ;
2008-05-21 00:58:11 +04:00
hcd - > has_tt = 1 ;
2007-12-01 19:07:04 +03:00
ehci - > sbrn = 0x20 ;
2008-03-27 21:51:39 +03:00
/*
* ( Re - ) program MBUS remapping windows if we are asked to .
*/
if ( pd ! = NULL & & pd - > dram ! = NULL )
ehci_orion_conf_mbus_windows ( hcd , pd - > dram ) ;
2007-12-01 19:07:04 +03:00
/*
* setup Orion USB controller
*/
orion_usb_setup ( hcd ) ;
err = usb_add_hcd ( hcd , irq , IRQF_SHARED | IRQF_DISABLED ) ;
if ( err )
goto err4 ;
return 0 ;
err4 :
usb_put_hcd ( hcd ) ;
err3 :
iounmap ( regs ) ;
err2 :
release_mem_region ( res - > start , res - > end - res - > start + 1 ) ;
err1 :
dev_err ( & pdev - > dev , " init %s fail, %d \n " ,
2008-05-02 08:02:41 +04:00
dev_name ( & pdev - > dev ) , err ) ;
2007-12-01 19:07:04 +03:00
return err ;
}
static int __exit ehci_orion_drv_remove ( struct platform_device * pdev )
{
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
usb_remove_hcd ( hcd ) ;
iounmap ( hcd - > regs ) ;
release_mem_region ( hcd - > rsrc_start , hcd - > rsrc_len ) ;
usb_put_hcd ( hcd ) ;
return 0 ;
}
MODULE_ALIAS ( " platform:orion-ehci " ) ;
static struct platform_driver ehci_orion_driver = {
. probe = ehci_orion_drv_probe ,
. remove = __exit_p ( ehci_orion_drv_remove ) ,
. shutdown = usb_hcd_platform_shutdown ,
. driver . name = " orion-ehci " ,
} ;