2010-09-28 22:55:20 +04:00
/*
* Setup platform devices needed by the Freescale multi - port host
* and / or dual - role USB controller modules based on the description
* in flat device tree .
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/fsl_devices.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of_platform.h>
2010-09-28 22:55:21 +04:00
# include <linux/clk.h>
2011-07-04 00:09:31 +04:00
# include <linux/module.h>
2015-12-17 14:32:35 +03:00
# include <linux/dma-mapping.h>
2010-09-28 22:55:20 +04:00
struct fsl_usb2_dev_data {
char * dr_mode ; /* controller mode */
char * drivers [ 3 ] ; /* drivers to instantiate for this mode */
enum fsl_usb2_operating_modes op_mode ; /* operating mode */
} ;
2013-09-16 15:03:52 +04:00
static struct fsl_usb2_dev_data dr_mode_data [ ] = {
2010-09-28 22:55:20 +04:00
{
. dr_mode = " host " ,
. drivers = { " fsl-ehci " , NULL , NULL , } ,
. op_mode = FSL_USB2_DR_HOST ,
} ,
{
. dr_mode = " otg " ,
. drivers = { " fsl-usb2-otg " , " fsl-ehci " , " fsl-usb2-udc " , } ,
. op_mode = FSL_USB2_DR_OTG ,
} ,
{
. dr_mode = " peripheral " ,
. drivers = { " fsl-usb2-udc " , NULL , NULL , } ,
. op_mode = FSL_USB2_DR_DEVICE ,
} ,
} ;
2013-09-16 15:03:52 +04:00
static struct fsl_usb2_dev_data * get_dr_mode_data ( struct device_node * np )
2010-09-28 22:55:20 +04:00
{
const unsigned char * prop ;
int i ;
prop = of_get_property ( np , " dr_mode " , NULL ) ;
if ( prop ) {
for ( i = 0 ; i < ARRAY_SIZE ( dr_mode_data ) ; i + + ) {
if ( ! strcmp ( prop , dr_mode_data [ i ] . dr_mode ) )
return & dr_mode_data [ i ] ;
}
}
pr_warn ( " %s: Invalid 'dr_mode' property, fallback to host mode \n " ,
np - > full_name ) ;
return & dr_mode_data [ 0 ] ; /* mode not specified, use host */
}
2012-11-19 22:21:48 +04:00
static enum fsl_usb2_phy_modes determine_usb_phy ( const char * phy_type )
2010-09-28 22:55:20 +04:00
{
if ( ! phy_type )
return FSL_USB2_PHY_NONE ;
if ( ! strcasecmp ( phy_type , " ulpi " ) )
return FSL_USB2_PHY_ULPI ;
if ( ! strcasecmp ( phy_type , " utmi " ) )
return FSL_USB2_PHY_UTMI ;
if ( ! strcasecmp ( phy_type , " utmi_wide " ) )
return FSL_USB2_PHY_UTMI_WIDE ;
2015-06-15 13:18:22 +03:00
if ( ! strcasecmp ( phy_type , " utmi_dual " ) )
return FSL_USB2_PHY_UTMI_DUAL ;
2010-09-28 22:55:20 +04:00
if ( ! strcasecmp ( phy_type , " serial " ) )
return FSL_USB2_PHY_SERIAL ;
return FSL_USB2_PHY_NONE ;
}
2013-09-16 15:03:52 +04:00
static struct platform_device * fsl_usb2_device_register (
2010-09-28 22:55:20 +04:00
struct platform_device * ofdev ,
struct fsl_usb2_platform_data * pdata ,
const char * name , int id )
{
struct platform_device * pdev ;
const struct resource * res = ofdev - > resource ;
unsigned int num = ofdev - > num_resources ;
int retval ;
pdev = platform_device_alloc ( name , id ) ;
if ( ! pdev ) {
retval = - ENOMEM ;
goto error ;
}
pdev - > dev . parent = & ofdev - > dev ;
pdev - > dev . coherent_dma_mask = ofdev - > dev . coherent_dma_mask ;
2015-12-17 14:32:35 +03:00
if ( ! pdev - > dev . dma_mask )
pdev - > dev . dma_mask = & ofdev - > dev . coherent_dma_mask ;
else
dma_set_mask ( & pdev - > dev , DMA_BIT_MASK ( 32 ) ) ;
2010-09-28 22:55:20 +04:00
retval = platform_device_add_data ( pdev , pdata , sizeof ( * pdata ) ) ;
if ( retval )
goto error ;
if ( num ) {
retval = platform_device_add_resources ( pdev , res , num ) ;
if ( retval )
goto error ;
}
retval = platform_device_add ( pdev ) ;
if ( retval )
goto error ;
return pdev ;
error :
platform_device_put ( pdev ) ;
return ERR_PTR ( retval ) ;
}
static const struct of_device_id fsl_usb2_mph_dr_of_match [ ] ;
2015-06-15 13:16:37 +03:00
static enum fsl_usb2_controller_ver usb_get_ver_info ( struct device_node * np )
2012-03-20 09:05:50 +04:00
{
2015-06-15 13:16:37 +03:00
enum fsl_usb2_controller_ver ver = FSL_USB_VER_NONE ;
2012-03-20 09:05:50 +04:00
/*
* returns 1 for usb controller version 1.6
* returns 2 for usb controller version 2.2
2015-05-26 14:44:42 +03:00
* returns 3 for usb controller version 2.4
2015-05-26 14:45:29 +03:00
* returns 4 for usb controller version 2.5
2012-03-20 09:05:50 +04:00
* returns 0 otherwise
*/
if ( of_device_is_compatible ( np , " fsl-usb2-dr " ) ) {
if ( of_device_is_compatible ( np , " fsl-usb2-dr-v1.6 " ) )
ver = FSL_USB_VER_1_6 ;
else if ( of_device_is_compatible ( np , " fsl-usb2-dr-v2.2 " ) )
ver = FSL_USB_VER_2_2 ;
2012-09-19 12:48:45 +04:00
else if ( of_device_is_compatible ( np , " fsl-usb2-dr-v2.4 " ) )
ver = FSL_USB_VER_2_4 ;
2015-05-26 14:45:29 +03:00
else if ( of_device_is_compatible ( np , " fsl-usb2-dr-v2.5 " ) )
ver = FSL_USB_VER_2_5 ;
2012-03-20 09:05:50 +04:00
else /* for previous controller versions */
ver = FSL_USB_VER_OLD ;
2015-06-15 13:16:37 +03:00
if ( ver > FSL_USB_VER_NONE )
2012-03-20 09:05:50 +04:00
return ver ;
}
2012-12-04 17:23:21 +04:00
if ( of_device_is_compatible ( np , " fsl,mpc5121-usb2-dr " ) )
return FSL_USB_VER_OLD ;
2012-03-20 09:05:50 +04:00
if ( of_device_is_compatible ( np , " fsl-usb2-mph " ) ) {
if ( of_device_is_compatible ( np , " fsl-usb2-mph-v1.6 " ) )
ver = FSL_USB_VER_1_6 ;
else if ( of_device_is_compatible ( np , " fsl-usb2-mph-v2.2 " ) )
ver = FSL_USB_VER_2_2 ;
2015-05-26 14:44:42 +03:00
else if ( of_device_is_compatible ( np , " fsl-usb2-mph-v2.4 " ) )
ver = FSL_USB_VER_2_4 ;
2015-05-26 14:45:29 +03:00
else if ( of_device_is_compatible ( np , " fsl-usb2-mph-v2.5 " ) )
ver = FSL_USB_VER_2_5 ;
2012-03-20 09:05:50 +04:00
else /* for previous controller versions */
ver = FSL_USB_VER_OLD ;
}
return ver ;
}
2012-11-19 22:21:48 +04:00
static int fsl_usb2_mph_dr_of_probe ( struct platform_device * ofdev )
2010-09-28 22:55:20 +04:00
{
struct device_node * np = ofdev - > dev . of_node ;
struct platform_device * usb_dev ;
struct fsl_usb2_platform_data data , * pdata ;
struct fsl_usb2_dev_data * dev_data ;
const struct of_device_id * match ;
const unsigned char * prop ;
static unsigned int idx ;
int i ;
if ( ! of_device_is_available ( np ) )
return - ENODEV ;
match = of_match_device ( fsl_usb2_mph_dr_of_match , & ofdev - > dev ) ;
if ( ! match )
return - ENODEV ;
pdata = & data ;
if ( match - > data )
memcpy ( pdata , match - > data , sizeof ( data ) ) ;
else
memset ( pdata , 0 , sizeof ( data ) ) ;
dev_data = get_dr_mode_data ( np ) ;
if ( of_device_is_compatible ( np , " fsl-usb2-mph " ) ) {
if ( of_get_property ( np , " port0 " , NULL ) )
pdata - > port_enables | = FSL_USB2_PORT0_ENABLED ;
if ( of_get_property ( np , " port1 " , NULL ) )
pdata - > port_enables | = FSL_USB2_PORT1_ENABLED ;
pdata - > operating_mode = FSL_USB2_MPH_HOST ;
} else {
2010-09-28 22:55:21 +04:00
if ( of_get_property ( np , " fsl,invert-drvvbus " , NULL ) )
pdata - > invert_drvvbus = 1 ;
if ( of_get_property ( np , " fsl,invert-pwr-fault " , NULL ) )
pdata - > invert_pwr_fault = 1 ;
2010-09-28 22:55:20 +04:00
/* setup mode selected in the device tree */
pdata - > operating_mode = dev_data - > op_mode ;
}
prop = of_get_property ( np , " phy_type " , NULL ) ;
pdata - > phy_mode = determine_usb_phy ( prop ) ;
2012-03-20 09:05:50 +04:00
pdata - > controller_ver = usb_get_ver_info ( np ) ;
2015-06-15 13:17:29 +03:00
/* Activate Erratum by reading property in device tree */
2016-08-05 14:26:53 +03:00
pdata - > has_fsl_erratum_a007792 =
of_property_read_bool ( np , " fsl,usb-erratum-a007792 " ) ;
pdata - > has_fsl_erratum_a005275 =
of_property_read_bool ( np , " fsl,usb-erratum-a005275 " ) ;
2015-06-15 13:17:29 +03:00
2015-07-14 14:58:10 +03:00
/*
* Determine whether phy_clk_valid needs to be checked
* by reading property in device tree
*/
2016-08-05 14:26:53 +03:00
pdata - > check_phy_clk_valid =
of_property_read_bool ( np , " phy-clk-valid " ) ;
2015-07-14 14:58:10 +03:00
2012-03-20 09:05:50 +04:00
if ( pdata - > have_sysif_regs ) {
2015-06-15 13:16:37 +03:00
if ( pdata - > controller_ver = = FSL_USB_VER_NONE ) {
2012-03-20 09:05:50 +04:00
dev_warn ( & ofdev - > dev , " Could not get controller version \n " ) ;
return - ENODEV ;
}
}
2010-09-28 22:55:20 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( dev_data - > drivers ) ; i + + ) {
if ( ! dev_data - > drivers [ i ] )
continue ;
usb_dev = fsl_usb2_device_register ( ofdev , pdata ,
dev_data - > drivers [ i ] , idx ) ;
if ( IS_ERR ( usb_dev ) ) {
dev_err ( & ofdev - > dev , " Can't register usb device \n " ) ;
return PTR_ERR ( usb_dev ) ;
}
}
idx + + ;
return 0 ;
}
2012-11-19 22:26:20 +04:00
static int __unregister_subdev ( struct device * dev , void * d )
2010-09-28 22:55:20 +04:00
{
platform_device_unregister ( to_platform_device ( dev ) ) ;
return 0 ;
}
2012-11-19 22:26:20 +04:00
static int fsl_usb2_mph_dr_of_remove ( struct platform_device * ofdev )
2010-09-28 22:55:20 +04:00
{
device_for_each_child ( & ofdev - > dev , NULL , __unregister_subdev ) ;
return 0 ;
}
2010-09-28 22:55:21 +04:00
# ifdef CONFIG_PPC_MPC512x
# define USBGENCTRL 0x200 /* NOTE: big endian */
# define GC_WU_INT_CLR (1 << 5) /* Wakeup int clear */
# define GC_ULPI_SEL (1 << 4) /* ULPI i/f select (usb0 only)*/
# define GC_PPP (1 << 3) /* Inv. Port Power Polarity */
# define GC_PFP (1 << 2) /* Inv. Power Fault Polarity */
# define GC_WU_ULPI_EN (1 << 1) /* Wakeup on ULPI event */
# define GC_WU_IE (1 << 1) /* Wakeup interrupt enable */
# define ISIPHYCTRL 0x204 /* NOTE: big endian */
# define PHYCTRL_PHYE (1 << 4) /* On-chip UTMI PHY enable */
# define PHYCTRL_BSENH (1 << 3) /* Bit Stuff Enable High */
# define PHYCTRL_BSEN (1 << 2) /* Bit Stuff Enable */
# define PHYCTRL_LSFE (1 << 1) /* Line State Filter Enable */
# define PHYCTRL_PXE (1 << 0) /* PHY oscillator enable */
int fsl_usb2_mpc5121_init ( struct platform_device * pdev )
{
2013-07-30 14:59:40 +04:00
struct fsl_usb2_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2010-09-28 22:55:21 +04:00
struct clk * clk ;
2013-07-19 16:18:31 +04:00
int err ;
2010-09-28 22:55:21 +04:00
2013-12-01 02:51:31 +04:00
clk = devm_clk_get ( pdev - > dev . parent , " ipg " ) ;
2010-09-28 22:55:21 +04:00
if ( IS_ERR ( clk ) ) {
dev_err ( & pdev - > dev , " failed to get clk \n " ) ;
return PTR_ERR ( clk ) ;
}
2013-07-19 16:18:31 +04:00
err = clk_prepare_enable ( clk ) ;
if ( err ) {
dev_err ( & pdev - > dev , " failed to enable clk \n " ) ;
return err ;
}
2010-09-28 22:55:21 +04:00
pdata - > clk = clk ;
if ( pdata - > phy_mode = = FSL_USB2_PHY_UTMI_WIDE ) {
u32 reg = 0 ;
if ( pdata - > invert_drvvbus )
reg | = GC_PPP ;
if ( pdata - > invert_pwr_fault )
reg | = GC_PFP ;
out_be32 ( pdata - > regs + ISIPHYCTRL , PHYCTRL_PHYE | PHYCTRL_PXE ) ;
out_be32 ( pdata - > regs + USBGENCTRL , reg ) ;
}
return 0 ;
}
static void fsl_usb2_mpc5121_exit ( struct platform_device * pdev )
{
2013-07-30 14:59:40 +04:00
struct fsl_usb2_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2010-09-28 22:55:21 +04:00
pdata - > regs = NULL ;
2013-07-19 16:18:31 +04:00
if ( pdata - > clk )
clk_disable_unprepare ( pdata - > clk ) ;
2010-09-28 22:55:21 +04:00
}
2011-01-11 02:34:14 +03:00
static struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = {
2010-09-28 22:55:21 +04:00
. big_endian_desc = 1 ,
. big_endian_mmio = 1 ,
. es = 1 ,
2011-01-11 02:34:14 +03:00
. have_sysif_regs = 0 ,
2010-09-28 22:55:21 +04:00
. le_setup_buf = 1 ,
. init = fsl_usb2_mpc5121_init ,
. exit = fsl_usb2_mpc5121_exit ,
} ;
# endif /* CONFIG_PPC_MPC512x */
2011-01-11 02:34:14 +03:00
static struct fsl_usb2_platform_data fsl_usb2_mpc8xxx_pd = {
. have_sysif_regs = 1 ,
} ;
2010-09-28 22:55:20 +04:00
static const struct of_device_id fsl_usb2_mph_dr_of_match [ ] = {
2011-01-11 02:34:14 +03:00
{ . compatible = " fsl-usb2-mph " , . data = & fsl_usb2_mpc8xxx_pd , } ,
{ . compatible = " fsl-usb2-dr " , . data = & fsl_usb2_mpc8xxx_pd , } ,
2010-09-28 22:55:21 +04:00
# ifdef CONFIG_PPC_MPC512x
{ . compatible = " fsl,mpc5121-usb2-dr " , . data = & fsl_usb2_mpc5121_pd , } ,
# endif
2010-09-28 22:55:20 +04:00
{ } ,
} ;
2015-09-19 16:10:57 +03:00
MODULE_DEVICE_TABLE ( of , fsl_usb2_mph_dr_of_match ) ;
2010-09-28 22:55:20 +04:00
static struct platform_driver fsl_usb2_mph_dr_driver = {
. driver = {
. name = " fsl-usb2-mph-dr " ,
. of_match_table = fsl_usb2_mph_dr_of_match ,
} ,
. probe = fsl_usb2_mph_dr_of_probe ,
2012-11-19 22:21:08 +04:00
. remove = fsl_usb2_mph_dr_of_remove ,
2010-09-28 22:55:20 +04:00
} ;
2011-11-27 16:16:27 +04:00
module_platform_driver ( fsl_usb2_mph_dr_driver ) ;
2010-09-28 22:55:20 +04:00
MODULE_DESCRIPTION ( " FSL MPH DR OF devices driver " ) ;
MODULE_AUTHOR ( " Anatolij Gustschin <agust@denx.de> " ) ;
MODULE_LICENSE ( " GPL " ) ;