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>
2012-04-15 14:53:47 +04:00
# include <linux/clk.h>
2012-08-24 17:21:54 +04:00
# include <linux/platform_data/usb-ehci-orion.h>
2012-10-20 15:10:00 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_irq.h>
2013-04-02 20:23:59 +04:00
# include <linux/usb.h>
# include <linux/usb/hcd.h>
# include <linux/io.h>
# include <linux/dma-mapping.h>
# include "ehci.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
2013-04-02 20:23:59 +04:00
# define DRIVER_DESC "EHCI orion driver"
static const char hcd_name [ ] = " ehci-orion " ;
static struct hc_driver __read_mostly ehci_orion_hc_driver ;
2007-12-01 19:07:04 +03:00
/*
* Implement Orion USB controller specification guidelines
*/
2008-09-17 11:08:05 +04:00
static void orion_usb_phy_v1_setup ( struct usb_hcd * hcd )
2007-12-01 19:07:04 +03:00
{
2008-09-17 11:08:05 +04:00
/* The below GLs are according to the Orion Errata document */
2007-12-01 19:07:04 +03:00
/*
* 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 ) ;
}
2012-11-19 22:21:48 +04:00
static void
2008-03-27 21:51:39 +03:00
ehci_orion_conf_mbus_windows ( struct usb_hcd * hcd ,
2011-12-08 00:48:07 +04:00
const struct mbus_dram_target_info * dram )
2008-03-27 21:51:39 +03:00
{
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 + + ) {
2011-12-08 00:48:07 +04:00
const struct mbus_dram_window * cs = dram - > cs + i ;
2008-03-27 21:51:39 +03:00
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 ) ;
}
}
2012-11-19 22:21:48 +04:00
static int ehci_orion_drv_probe ( struct platform_device * pdev )
2007-12-01 19:07:04 +03:00
{
2013-07-30 14:59:40 +04:00
struct orion_ehci_data * pd = dev_get_platdata ( & pdev - > dev ) ;
2011-12-08 00:48:07 +04:00
const struct mbus_dram_target_info * dram ;
2007-12-01 19:07:04 +03:00
struct resource * res ;
struct usb_hcd * hcd ;
struct ehci_hcd * ehci ;
2012-04-15 14:53:47 +04:00
struct clk * clk ;
2007-12-01 19:07:04 +03:00
void __iomem * regs ;
int irq , err ;
2012-10-20 15:10:00 +04:00
enum orion_ehci_phy_ver phy_version ;
2007-12-01 19:07:04 +03:00
if ( usb_disabled ( ) )
return - ENODEV ;
pr_debug ( " Initializing Orion-SoC USB Host Controller \n " ) ;
2012-10-20 15:10:00 +04:00
if ( pdev - > dev . of_node )
irq = irq_of_parse_and_map ( pdev - > dev . of_node , 0 ) ;
else
irq = platform_get_irq ( pdev , 0 ) ;
2007-12-01 19:07:04 +03:00
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 ;
}
2012-10-20 15:10:00 +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 .
*/
2013-06-27 15:36:37 +04:00
err = dma_coerce_mask_and_coherent ( & pdev - > dev , DMA_BIT_MASK ( 32 ) ) ;
2013-06-10 19:28:49 +04:00
if ( err )
goto err1 ;
2012-10-20 15:10:00 +04:00
2013-12-11 11:16:33 +04:00
regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( regs ) ) {
err = PTR_ERR ( regs ) ;
2007-12-01 19:07:04 +03:00
goto err1 ;
}
2012-04-15 14:53:47 +04:00
/* Not all platforms can gate the clock, so it is not
an error if the clock does not exists . */
2013-12-11 11:16:33 +04:00
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( ! IS_ERR ( clk ) )
2012-04-15 14:53:47 +04:00
clk_prepare_enable ( clk ) ;
2007-12-01 19:07:04 +03:00
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 ;
2013-12-11 11:16:33 +04:00
goto err2 ;
2007-12-01 19:07:04 +03:00
}
hcd - > rsrc_start = res - > start ;
2009-12-15 02:13:00 +03:00
hcd - > rsrc_len = resource_size ( res ) ;
2007-12-01 19:07:04 +03:00
hcd - > regs = regs ;
ehci = hcd_to_ehci ( hcd ) ;
ehci - > caps = hcd - > regs + 0x100 ;
2008-05-21 00:58:11 +04:00
hcd - > has_tt = 1 ;
2007-12-01 19:07:04 +03:00
2008-03-27 21:51:39 +03:00
/*
* ( Re - ) program MBUS remapping windows if we are asked to .
*/
2011-12-08 00:48:07 +04:00
dram = mv_mbus_dram_info ( ) ;
if ( dram )
ehci_orion_conf_mbus_windows ( hcd , dram ) ;
2008-03-27 21:51:39 +03:00
2007-12-01 19:07:04 +03:00
/*
2008-09-17 11:08:05 +04:00
* setup Orion USB controller .
2007-12-01 19:07:04 +03:00
*/
2012-10-20 15:10:00 +04:00
if ( pdev - > dev . of_node )
phy_version = EHCI_PHY_NA ;
else
phy_version = pd - > phy_version ;
switch ( phy_version ) {
2008-09-17 11:08:05 +04:00
case EHCI_PHY_NA : /* dont change USB phy settings */
break ;
case EHCI_PHY_ORION :
orion_usb_phy_v1_setup ( hcd ) ;
break ;
case EHCI_PHY_DD :
case EHCI_PHY_KW :
default :
2013-12-10 16:20:30 +04:00
dev_warn ( & pdev - > dev , " USB phy version isn't supported. \n " ) ;
2008-09-17 11:08:05 +04:00
}
2007-12-01 19:07:04 +03:00
2011-09-07 12:10:52 +04:00
err = usb_add_hcd ( hcd , irq , IRQF_SHARED ) ;
2007-12-01 19:07:04 +03:00
if ( err )
2013-12-11 11:16:33 +04:00
goto err3 ;
2007-12-01 19:07:04 +03:00
2013-11-05 06:46:02 +04:00
device_wakeup_enable ( hcd - > self . controller ) ;
2007-12-01 19:07:04 +03:00
return 0 ;
err3 :
2013-12-11 11:16:33 +04:00
usb_put_hcd ( hcd ) ;
2007-12-01 19:07:04 +03:00
err2 :
2013-12-11 11:16:33 +04:00
if ( ! IS_ERR ( clk ) )
clk_disable_unprepare ( clk ) ;
2007-12-01 19:07:04 +03:00
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 ;
}
2013-02-24 12:55:07 +04:00
static int ehci_orion_drv_remove ( struct platform_device * pdev )
2007-12-01 19:07:04 +03:00
{
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
2012-04-15 14:53:47 +04:00
struct clk * clk ;
2007-12-01 19:07:04 +03:00
usb_remove_hcd ( hcd ) ;
usb_put_hcd ( hcd ) ;
2013-12-11 11:16:33 +04:00
clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( ! IS_ERR ( clk ) )
2012-04-15 14:53:47 +04:00
clk_disable_unprepare ( clk ) ;
2007-12-01 19:07:04 +03:00
return 0 ;
}
2012-12-22 01:14:52 +04:00
static const struct of_device_id ehci_orion_dt_ids [ ] = {
2012-10-20 15:10:00 +04:00
{ . compatible = " marvell,orion-ehci " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ehci_orion_dt_ids ) ;
2007-12-01 19:07:04 +03:00
static struct platform_driver ehci_orion_driver = {
. probe = ehci_orion_drv_probe ,
2013-02-24 12:55:07 +04:00
. remove = ehci_orion_drv_remove ,
2007-12-01 19:07:04 +03:00
. shutdown = usb_hcd_platform_shutdown ,
2012-10-20 15:10:00 +04:00
. driver = {
. name = " orion-ehci " ,
. owner = THIS_MODULE ,
2013-05-21 15:47:18 +04:00
. of_match_table = ehci_orion_dt_ids ,
2012-10-20 15:10:00 +04:00
} ,
2007-12-01 19:07:04 +03:00
} ;
2013-04-02 20:23:59 +04:00
static int __init ehci_orion_init ( void )
{
if ( usb_disabled ( ) )
return - ENODEV ;
pr_info ( " %s: " DRIVER_DESC " \n " , hcd_name ) ;
ehci_init_driver ( & ehci_orion_hc_driver , NULL ) ;
return platform_driver_register ( & ehci_orion_driver ) ;
}
module_init ( ehci_orion_init ) ;
static void __exit ehci_orion_cleanup ( void )
{
platform_driver_unregister ( & ehci_orion_driver ) ;
}
module_exit ( ehci_orion_cleanup ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_ALIAS ( " platform:orion-ehci " ) ;
MODULE_AUTHOR ( " Tzachi Perelstein " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;