2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0
2017-11-06 15:37:18 +01:00
/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. */
2010-12-07 17:54:03 +05:30
# include <linux/module.h>
# include <linux/platform_device.h>
2010-12-07 17:54:05 +05:30
# include <linux/pm_runtime.h>
2012-05-11 17:25:46 +03:00
# include <linux/usb/chipidea.h>
2016-12-28 14:57:01 -08:00
# include <linux/clk.h>
# include <linux/reset.h>
2016-12-28 14:57:02 -08:00
# include <linux/mfd/syscon.h>
# include <linux/regmap.h>
# include <linux/io.h>
2016-12-28 14:57:05 -08:00
# include <linux/reset-controller.h>
2016-12-28 14:57:03 -08:00
# include <linux/extcon.h>
# include <linux/of.h>
2010-12-07 17:54:03 +05:30
2012-05-11 17:25:46 +03:00
# include "ci.h"
2010-12-07 17:54:03 +05:30
2016-12-28 14:57:00 -08:00
# define HS_PHY_AHB_MODE 0x0098
2010-12-07 17:54:03 +05:30
2016-12-28 14:57:03 -08:00
# define HS_PHY_GENCONFIG 0x009c
# define HS_PHY_TXFIFO_IDLE_FORCE_DIS BIT(4)
# define HS_PHY_GENCONFIG_2 0x00a0
# define HS_PHY_SESS_VLD_CTRL_EN BIT(7)
# define HS_PHY_ULPI_TX_PKT_EN_CLR_FIX BIT(19)
# define HSPHY_SESS_VLD_CTRL BIT(25)
2016-12-28 14:57:02 -08:00
/* Vendor base starts at 0x200 beyond CI base */
2016-12-28 14:57:05 -08:00
# define HS_PHY_CTRL 0x0040
2016-12-28 14:57:02 -08:00
# define HS_PHY_SEC_CTRL 0x0078
# define HS_PHY_DIG_CLAMP_N BIT(16)
2016-12-28 14:57:05 -08:00
# define HS_PHY_POR_ASSERT BIT(0)
2016-12-28 14:57:02 -08:00
2016-12-28 14:57:01 -08:00
struct ci_hdrc_msm {
struct platform_device * ci ;
struct clk * core_clk ;
struct clk * iface_clk ;
struct clk * fs_clk ;
2016-12-28 14:57:04 -08:00
struct ci_hdrc_platform_data pdata ;
2016-12-28 14:57:05 -08:00
struct reset_controller_dev rcdev ;
2016-12-28 14:57:02 -08:00
bool secondary_phy ;
2016-12-28 14:57:03 -08:00
bool hsic ;
2016-12-28 14:57:02 -08:00
void __iomem * base ;
2016-12-28 14:57:01 -08:00
} ;
2016-12-28 14:57:05 -08:00
static int
ci_hdrc_msm_por_reset ( struct reset_controller_dev * r , unsigned long id )
{
struct ci_hdrc_msm * ci_msm = container_of ( r , struct ci_hdrc_msm , rcdev ) ;
void __iomem * addr = ci_msm - > base ;
u32 val ;
if ( id )
addr + = HS_PHY_SEC_CTRL ;
else
addr + = HS_PHY_CTRL ;
val = readl_relaxed ( addr ) ;
val | = HS_PHY_POR_ASSERT ;
writel ( val , addr ) ;
/*
* wait for minimum 10 microseconds as suggested by manual .
* Use a slightly larger value since the exact value didn ' t
* work 100 % of the time .
*/
udelay ( 12 ) ;
val & = ~ HS_PHY_POR_ASSERT ;
writel ( val , addr ) ;
return 0 ;
}
static const struct reset_control_ops ci_hdrc_msm_reset_ops = {
. reset = ci_hdrc_msm_por_reset ,
} ;
2016-12-28 14:57:06 -08:00
static int ci_hdrc_msm_notify_event ( struct ci_hdrc * ci , unsigned event )
2010-12-07 17:54:03 +05:30
{
2016-12-28 14:57:02 -08:00
struct device * dev = ci - > dev - > parent ;
struct ci_hdrc_msm * msm_ci = dev_get_drvdata ( dev ) ;
2016-12-28 14:57:06 -08:00
int ret ;
2010-12-07 17:54:03 +05:30
switch ( event ) {
2013-06-24 14:46:36 +03:00
case CI_HDRC_CONTROLLER_RESET_EVENT :
dev_dbg ( dev , " CI_HDRC_CONTROLLER_RESET_EVENT received \n " ) ;
2016-12-28 14:57:06 -08:00
hw_phymode_configure ( ci ) ;
2016-12-28 14:57:02 -08:00
if ( msm_ci - > secondary_phy ) {
u32 val = readl_relaxed ( msm_ci - > base + HS_PHY_SEC_CTRL ) ;
val | = HS_PHY_DIG_CLAMP_N ;
writel_relaxed ( val , msm_ci - > base + HS_PHY_SEC_CTRL ) ;
}
2016-12-28 14:57:06 -08:00
ret = phy_init ( ci - > phy ) ;
if ( ret )
return ret ;
ret = phy_power_on ( ci - > phy ) ;
if ( ret ) {
phy_exit ( ci - > phy ) ;
return ret ;
}
2015-11-06 00:04:06 -06:00
/* use AHB transactor, allow posted data writes */
2016-12-28 14:57:00 -08:00
hw_write_id_reg ( ci , HS_PHY_AHB_MODE , 0xffffffff , 0x8 ) ;
2016-12-28 14:57:03 -08:00
/* workaround for rx buffer collision issue */
hw_write_id_reg ( ci , HS_PHY_GENCONFIG ,
HS_PHY_TXFIFO_IDLE_FORCE_DIS , 0 ) ;
if ( ! msm_ci - > hsic )
hw_write_id_reg ( ci , HS_PHY_GENCONFIG_2 ,
HS_PHY_ULPI_TX_PKT_EN_CLR_FIX , 0 ) ;
2020-05-07 08:49:18 +08:00
if ( ! IS_ERR ( ci - > platdata - > vbus_extcon . edev ) | | ci - > role_switch ) {
2016-12-28 14:57:03 -08:00
hw_write_id_reg ( ci , HS_PHY_GENCONFIG_2 ,
HS_PHY_SESS_VLD_CTRL_EN ,
HS_PHY_SESS_VLD_CTRL_EN ) ;
hw_write ( ci , OP_USBCMD , HSPHY_SESS_VLD_CTRL ,
HSPHY_SESS_VLD_CTRL ) ;
}
2010-12-07 17:54:03 +05:30
break ;
2013-06-24 14:46:36 +03:00
case CI_HDRC_CONTROLLER_STOPPED_EVENT :
dev_dbg ( dev , " CI_HDRC_CONTROLLER_STOPPED_EVENT received \n " ) ;
2016-12-28 14:57:06 -08:00
phy_power_off ( ci - > phy ) ;
phy_exit ( ci - > phy ) ;
2010-12-07 17:54:03 +05:30
break ;
default :
2013-06-24 14:46:36 +03:00
dev_dbg ( dev , " unknown ci_hdrc event \n " ) ;
2010-12-07 17:54:03 +05:30
break ;
}
2016-12-28 14:57:06 -08:00
return 0 ;
2010-12-07 17:54:03 +05:30
}
2016-12-28 14:57:02 -08:00
static int ci_hdrc_msm_mux_phy ( struct ci_hdrc_msm * ci ,
struct platform_device * pdev )
{
struct regmap * regmap ;
struct device * dev = & pdev - > dev ;
struct of_phandle_args args ;
u32 val ;
int ret ;
ret = of_parse_phandle_with_fixed_args ( dev - > of_node , " phy-select " , 2 , 0 ,
& args ) ;
if ( ret )
return 0 ;
regmap = syscon_node_to_regmap ( args . np ) ;
of_node_put ( args . np ) ;
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
ret = regmap_write ( regmap , args . args [ 0 ] , args . args [ 1 ] ) ;
if ( ret )
return ret ;
ci - > secondary_phy = ! ! args . args [ 1 ] ;
if ( ci - > secondary_phy ) {
val = readl_relaxed ( ci - > base + HS_PHY_SEC_CTRL ) ;
val | = HS_PHY_DIG_CLAMP_N ;
writel_relaxed ( val , ci - > base + HS_PHY_SEC_CTRL ) ;
}
return 0 ;
}
2013-06-24 14:46:36 +03:00
static int ci_hdrc_msm_probe ( struct platform_device * pdev )
2010-12-07 17:54:03 +05:30
{
2016-12-28 14:57:01 -08:00
struct ci_hdrc_msm * ci ;
2012-05-08 23:29:01 +03:00
struct platform_device * plat_ci ;
2016-12-28 14:57:01 -08:00
struct clk * clk ;
struct reset_control * reset ;
int ret ;
2016-12-28 14:57:03 -08:00
struct device_node * ulpi_node , * phy_node ;
2010-12-07 17:54:03 +05:30
2013-06-24 14:46:36 +03:00
dev_dbg ( & pdev - > dev , " ci_hdrc_msm_probe \n " ) ;
2010-12-07 17:54:03 +05:30
2016-12-28 14:57:01 -08:00
ci = devm_kzalloc ( & pdev - > dev , sizeof ( * ci ) , GFP_KERNEL ) ;
if ( ! ci )
return - ENOMEM ;
platform_set_drvdata ( pdev , ci ) ;
2016-12-28 14:57:04 -08:00
ci - > pdata . name = " ci_hdrc_msm " ;
ci - > pdata . capoffset = DEF_CAPOFFSET ;
ci - > pdata . flags = CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING |
2016-12-28 14:57:06 -08:00
CI_HDRC_OVERRIDE_AHB_BURST |
CI_HDRC_OVERRIDE_PHY_CONTROL ;
2016-12-28 14:57:04 -08:00
ci - > pdata . notify_event = ci_hdrc_msm_notify_event ;
2014-05-04 09:24:41 +08:00
2016-12-28 14:57:01 -08:00
reset = devm_reset_control_get ( & pdev - > dev , " core " ) ;
if ( IS_ERR ( reset ) )
return PTR_ERR ( reset ) ;
ci - > core_clk = clk = devm_clk_get ( & pdev - > dev , " core " ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
ci - > iface_clk = clk = devm_clk_get ( & pdev - > dev , " iface " ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
2019-04-17 16:28:20 +08:00
ci - > fs_clk = clk = devm_clk_get_optional ( & pdev - > dev , " fs " ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
2016-12-28 14:57:01 -08:00
2019-06-05 14:37:38 -03:00
ci - > base = devm_platform_ioremap_resource ( pdev , 1 ) ;
2017-01-25 14:02:48 +00:00
if ( IS_ERR ( ci - > base ) )
return PTR_ERR ( ci - > base ) ;
2016-12-28 14:57:02 -08:00
2016-12-28 14:57:05 -08:00
ci - > rcdev . owner = THIS_MODULE ;
ci - > rcdev . ops = & ci_hdrc_msm_reset_ops ;
ci - > rcdev . of_node = pdev - > dev . of_node ;
ci - > rcdev . nr_resets = 2 ;
2019-09-02 09:43:23 +08:00
ret = devm_reset_controller_register ( & pdev - > dev , & ci - > rcdev ) ;
2016-12-28 14:57:01 -08:00
if ( ret )
return ret ;
2016-12-28 14:57:05 -08:00
ret = clk_prepare_enable ( ci - > fs_clk ) ;
if ( ret )
2019-09-02 09:43:23 +08:00
return ret ;
2016-12-28 14:57:05 -08:00
2016-12-28 14:57:01 -08:00
reset_control_assert ( reset ) ;
usleep_range ( 10000 , 12000 ) ;
reset_control_deassert ( reset ) ;
clk_disable_unprepare ( ci - > fs_clk ) ;
ret = clk_prepare_enable ( ci - > core_clk ) ;
if ( ret )
2019-09-02 09:43:23 +08:00
return ret ;
2016-12-28 14:57:01 -08:00
ret = clk_prepare_enable ( ci - > iface_clk ) ;
if ( ret )
goto err_iface ;
2016-12-28 14:57:02 -08:00
ret = ci_hdrc_msm_mux_phy ( ci , pdev ) ;
if ( ret )
goto err_mux ;
2017-11-13 11:12:58 +01:00
ulpi_node = of_get_child_by_name ( pdev - > dev . of_node , " ulpi " ) ;
2016-12-28 14:57:03 -08:00
if ( ulpi_node ) {
phy_node = of_get_next_available_child ( ulpi_node , NULL ) ;
ci - > hsic = of_device_is_compatible ( phy_node , " qcom,usb-hsic-phy " ) ;
of_node_put ( phy_node ) ;
}
of_node_put ( ulpi_node ) ;
2016-12-28 14:57:04 -08:00
plat_ci = ci_hdrc_add_device ( & pdev - > dev , pdev - > resource ,
pdev - > num_resources , & ci - > pdata ) ;
2012-07-07 22:56:41 +08:00
if ( IS_ERR ( plat_ci ) ) {
2016-12-28 14:57:01 -08:00
ret = PTR_ERR ( plat_ci ) ;
2016-12-28 14:57:07 -08:00
if ( ret ! = - EPROBE_DEFER )
dev_err ( & pdev - > dev , " ci_hdrc_add_device failed! \n " ) ;
2016-12-28 14:57:01 -08:00
goto err_mux ;
2010-12-07 17:54:03 +05:30
}
2016-12-28 14:57:01 -08:00
ci - > ci = plat_ci ;
2012-06-29 17:48:52 +08:00
2016-12-28 14:56:58 -08:00
pm_runtime_set_active ( & pdev - > dev ) ;
2010-12-07 17:54:05 +05:30
pm_runtime_no_callbacks ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2010-12-07 17:54:03 +05:30
return 0 ;
2016-12-28 14:57:01 -08:00
err_mux :
clk_disable_unprepare ( ci - > iface_clk ) ;
err_iface :
clk_disable_unprepare ( ci - > core_clk ) ;
return ret ;
2010-12-07 17:54:03 +05:30
}
2013-06-24 14:46:36 +03:00
static int ci_hdrc_msm_remove ( struct platform_device * pdev )
2012-06-29 17:48:52 +08:00
{
2016-12-28 14:57:01 -08:00
struct ci_hdrc_msm * ci = platform_get_drvdata ( pdev ) ;
2012-06-29 17:48:52 +08:00
pm_runtime_disable ( & pdev - > dev ) ;
2016-12-28 14:57:01 -08:00
ci_hdrc_remove_device ( ci - > ci ) ;
clk_disable_unprepare ( ci - > iface_clk ) ;
clk_disable_unprepare ( ci - > core_clk ) ;
2012-06-29 17:48:52 +08:00
return 0 ;
}
2014-05-04 09:24:41 +08:00
static const struct of_device_id msm_ci_dt_match [ ] = {
{ . compatible = " qcom,ci-hdrc " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , msm_ci_dt_match ) ;
2013-06-24 14:46:36 +03:00
static struct platform_driver ci_hdrc_msm_driver = {
. probe = ci_hdrc_msm_probe ,
. remove = ci_hdrc_msm_remove ,
2014-05-04 09:24:41 +08:00
. driver = {
. name = " msm_hsusb " ,
. of_match_table = msm_ci_dt_match ,
} ,
2010-12-07 17:54:03 +05:30
} ;
2013-06-24 14:46:36 +03:00
module_platform_driver ( ci_hdrc_msm_driver ) ;
2011-10-10 18:38:11 +02:00
2012-06-29 17:48:52 +08:00
MODULE_ALIAS ( " platform:msm_hsusb " ) ;
2013-06-24 14:46:36 +03:00
MODULE_ALIAS ( " platform:ci13xxx_msm " ) ;
2011-10-10 18:38:11 +02:00
MODULE_LICENSE ( " GPL v2 " ) ;