2017-11-03 11:28:30 +01:00
// SPDX-License-Identifier: GPL-2.0+
2012-07-07 22:56:48 +08:00
/*
* Copyright 2012 Freescale Semiconductor , Inc .
* Copyright ( C ) 2012 Marek Vasut < marex @ denx . de >
* on behalf of DENX Software Engineering GmbH
*/
# include <linux/module.h>
2023-07-18 08:30:23 -06:00
# include <linux/of.h>
2012-07-07 22:56:48 +08:00
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/usb/chipidea.h>
2016-09-26 13:14:19 +02:00
# include <linux/usb/of.h>
2012-07-07 22:56:48 +08:00
# include <linux/clk.h>
2018-10-16 09:17:02 +08:00
# include <linux/pinctrl/consumer.h>
2019-04-28 10:35:31 +08:00
# include <linux/pm_qos.h>
2012-07-07 22:56:48 +08:00
# include "ci.h"
2013-06-24 14:46:36 +03:00
# include "ci_hdrc_imx.h"
2012-07-07 22:56:48 +08:00
2014-01-10 13:51:28 +08:00
struct ci_hdrc_imx_platform_flag {
unsigned int flags ;
} ;
2015-12-29 16:41:08 +00:00
static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
. flags = CI_HDRC_TURN_VBUS_EARLY_ON |
CI_HDRC_DISABLE_STREAMING ,
} ;
2014-01-10 13:51:28 +08:00
static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
2018-03-29 17:52:48 +08:00
. flags = CI_HDRC_DISABLE_STREAMING ,
2014-01-10 13:51:28 +08:00
} ;
static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
2015-02-11 12:45:01 +08:00
. flags = CI_HDRC_IMX28_WRITE_FIX |
2014-10-30 11:10:04 +08:00
CI_HDRC_TURN_VBUS_EARLY_ON |
CI_HDRC_DISABLE_STREAMING ,
2014-01-10 13:51:28 +08:00
} ;
2015-02-11 12:44:47 +08:00
static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
2015-02-11 12:45:01 +08:00
. flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
2014-10-30 11:10:04 +08:00
CI_HDRC_TURN_VBUS_EARLY_ON |
CI_HDRC_DISABLE_STREAMING ,
2015-02-11 12:44:47 +08:00
} ;
static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
2015-02-11 12:45:01 +08:00
. flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
2014-10-30 11:10:04 +08:00
CI_HDRC_TURN_VBUS_EARLY_ON |
CI_HDRC_DISABLE_HOST_STREAMING ,
2015-02-11 12:44:47 +08:00
} ;
static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
2015-02-11 12:45:01 +08:00
. flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
2014-10-30 11:10:04 +08:00
CI_HDRC_TURN_VBUS_EARLY_ON |
CI_HDRC_DISABLE_HOST_STREAMING ,
2015-02-11 12:44:47 +08:00
} ;
2015-09-16 15:52:32 +08:00
static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
. flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
2020-12-07 10:09:09 +08:00
CI_HDRC_TURN_VBUS_EARLY_ON |
CI_HDRC_DISABLE_DEVICE_STREAMING ,
2015-09-16 15:52:32 +08:00
} ;
2015-09-09 16:33:02 +08:00
static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
. flags = CI_HDRC_SUPPORTS_RUNTIME_PM ,
} ;
2019-04-28 10:35:31 +08:00
static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = {
. flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
2023-08-09 10:44:32 +08:00
CI_HDRC_HAS_PORTSC_PEC_MISSED |
2019-04-28 10:35:31 +08:00
CI_HDRC_PMQOS ,
} ;
2023-05-30 18:40:07 +08:00
static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = {
2023-08-09 10:44:32 +08:00
. flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
CI_HDRC_HAS_PORTSC_PEC_MISSED ,
2023-05-30 18:40:07 +08:00
} ;
2014-01-10 13:51:28 +08:00
static const struct of_device_id ci_hdrc_imx_dt_ids [ ] = {
2015-12-29 16:41:08 +00:00
{ . compatible = " fsl,imx23-usb " , . data = & imx23_usb_data } ,
2014-01-10 13:51:28 +08:00
{ . compatible = " fsl,imx28-usb " , . data = & imx28_usb_data } ,
{ . compatible = " fsl,imx27-usb " , . data = & imx27_usb_data } ,
2015-02-11 12:44:47 +08:00
{ . compatible = " fsl,imx6q-usb " , . data = & imx6q_usb_data } ,
{ . compatible = " fsl,imx6sl-usb " , . data = & imx6sl_usb_data } ,
2015-09-16 14:46:32 +08:00
{ . compatible = " fsl,imx6sx-usb " , . data = & imx6sx_usb_data } ,
2015-09-16 15:52:32 +08:00
{ . compatible = " fsl,imx6ul-usb " , . data = & imx6ul_usb_data } ,
2015-09-09 16:33:02 +08:00
{ . compatible = " fsl,imx7d-usb " , . data = & imx7d_usb_data } ,
2019-04-28 10:35:31 +08:00
{ . compatible = " fsl,imx7ulp-usb " , . data = & imx7ulp_usb_data } ,
2023-05-30 18:40:07 +08:00
{ . compatible = " fsl,imx8ulp-usb " , . data = & imx8ulp_usb_data } ,
2014-01-10 13:51:28 +08:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , ci_hdrc_imx_dt_ids ) ;
2013-06-24 14:46:36 +03:00
struct ci_hdrc_imx_data {
2012-07-07 22:56:48 +08:00
struct usb_phy * phy ;
struct platform_device * ci_pdev ;
struct clk * clk ;
2023-12-28 19:07:51 +08:00
struct clk * clk_wakeup ;
2013-08-14 12:44:16 +03:00
struct imx_usbmisc_data * usbmisc_data ;
2015-02-11 12:44:47 +08:00
bool supports_runtime_pm ;
2018-03-29 17:52:49 +08:00
bool override_phy_control ;
2015-02-11 12:44:47 +08:00
bool in_lpm ;
2018-10-16 09:17:02 +08:00
struct pinctrl * pinctrl ;
struct pinctrl_state * pinctrl_hsic_active ;
struct regulator * hsic_pad_regulator ;
2015-09-16 09:40:51 +08:00
/* SoC before i.mx6 (except imx23/imx28) needs three clks */
bool need_three_clks ;
struct clk * clk_ipg ;
struct clk * clk_ahb ;
struct clk * clk_per ;
/* --------------------------------- */
2019-04-28 10:35:31 +08:00
struct pm_qos_request pm_qos_req ;
const struct ci_hdrc_imx_platform_flag * plat_data ;
2012-07-07 22:56:48 +08:00
} ;
2012-09-12 14:58:05 +03:00
/* Common functions shared by usbmisc drivers */
2013-08-14 12:44:16 +03:00
static struct imx_usbmisc_data * usbmisc_get_init_data ( struct device * dev )
2012-09-12 14:58:05 +03:00
{
2014-09-22 08:14:15 +08:00
struct platform_device * misc_pdev ;
2012-09-12 14:58:05 +03:00
struct device_node * np = dev - > of_node ;
struct of_phandle_args args ;
2013-08-14 12:44:16 +03:00
struct imx_usbmisc_data * data ;
2012-09-12 14:58:05 +03:00
int ret ;
2013-08-14 12:44:16 +03:00
/*
* In case the fsl , usbmisc property is not present this device doesn ' t
* need usbmisc . Return NULL ( which is no error here )
*/
if ( ! of_get_property ( np , " fsl,usbmisc " , NULL ) )
return NULL ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return ERR_PTR ( - ENOMEM ) ;
2012-09-12 14:58:05 +03:00
ret = of_parse_phandle_with_args ( np , " fsl,usbmisc " , " #index-cells " ,
0 , & args ) ;
if ( ret ) {
dev_err ( dev , " Failed to parse property fsl,usbmisc, errno %d \n " ,
ret ) ;
2013-08-14 12:44:16 +03:00
return ERR_PTR ( ret ) ;
2012-09-12 14:58:05 +03:00
}
2013-08-14 12:44:16 +03:00
data - > index = args . args [ 0 ] ;
2014-09-22 08:14:15 +08:00
misc_pdev = of_find_device_by_node ( args . np ) ;
2012-09-12 14:58:05 +03:00
of_node_put ( args . np ) ;
2020-11-17 09:14:30 +08:00
if ( ! misc_pdev )
2014-09-22 08:14:15 +08:00
return ERR_PTR ( - EPROBE_DEFER ) ;
2020-11-17 09:14:30 +08:00
if ( ! platform_get_drvdata ( misc_pdev ) ) {
put_device ( & misc_pdev - > dev ) ;
return ERR_PTR ( - EPROBE_DEFER ) ;
}
2014-09-22 08:14:15 +08:00
data - > dev = & misc_pdev - > dev ;
2018-12-04 09:31:29 +01:00
/*
* Check the various over current related properties . If over current
* detection is disabled we ' re not interested in the polarity .
*/
2023-03-10 08:47:28 -06:00
if ( of_property_read_bool ( np , " disable-over-current " ) ) {
2013-08-14 12:44:16 +03:00
data - > disable_oc = 1 ;
2023-03-10 08:47:28 -06:00
} else if ( of_property_read_bool ( np , " over-current-active-high " ) ) {
2018-12-04 09:31:29 +01:00
data - > oc_pol_active_low = 0 ;
data - > oc_pol_configured = 1 ;
2023-03-10 08:47:28 -06:00
} else if ( of_property_read_bool ( np , " over-current-active-low " ) ) {
2018-12-04 09:31:29 +01:00
data - > oc_pol_active_low = 1 ;
data - > oc_pol_configured = 1 ;
2018-12-04 09:31:30 +01:00
} else {
dev_warn ( dev , " No over current polarity defined \n " ) ;
2018-12-04 09:31:29 +01:00
}
2016-07-20 16:02:42 +08:00
2019-02-27 06:51:34 +00:00
data - > pwr_pol = of_property_read_bool ( np , " power-active-high " ) ;
data - > evdo = of_property_read_bool ( np , " external-vbus-divider " ) ;
2013-03-30 12:54:01 +02:00
2016-09-26 13:14:19 +02:00
if ( of_usb_get_phy_mode ( np ) = = USBPHY_INTERFACE_MODE_ULPI )
data - > ulpi = 1 ;
2023-06-27 19:21:24 +08:00
if ( of_property_read_u32 ( np , " samsung,picophy-pre-emp-curr-control " ,
& data - > emp_curr_control ) )
data - > emp_curr_control = - 1 ;
if ( of_property_read_u32 ( np , " samsung,picophy-dc-vol-level-adjust " ,
& data - > dc_vol_level_adjust ) )
data - > dc_vol_level_adjust = - 1 ;
2023-06-27 19:21:26 +08:00
if ( of_property_read_u32 ( np , " fsl,picophy-rise-fall-time-adjust " ,
& data - > rise_fall_time_adjust ) )
data - > rise_fall_time_adjust = - 1 ;
2019-09-30 13:56:26 +08:00
2013-08-14 12:44:16 +03:00
return data ;
2012-09-12 14:58:05 +03:00
}
/* End of common functions shared by usbmisc drivers*/
2015-09-16 09:40:51 +08:00
static int imx_get_clks ( struct device * dev )
{
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
int ret = 0 ;
data - > clk_ipg = devm_clk_get ( dev , " ipg " ) ;
if ( IS_ERR ( data - > clk_ipg ) ) {
2023-12-28 19:07:51 +08:00
/* If the platform only needs one primary clock */
2015-09-16 09:40:51 +08:00
data - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( data - > clk ) ) {
ret = PTR_ERR ( data - > clk ) ;
dev_err ( dev ,
" Failed to get clks, err=%ld,%ld \n " ,
PTR_ERR ( data - > clk ) , PTR_ERR ( data - > clk_ipg ) ) ;
return ret ;
}
2023-12-28 19:07:51 +08:00
/* Get wakeup clock. Not all of the platforms need to
* handle this clock . So make it optional .
*/
2024-03-21 16:14:39 +08:00
data - > clk_wakeup = devm_clk_get_optional ( dev , " usb_wakeup " ) ;
2023-12-28 19:07:51 +08:00
if ( IS_ERR ( data - > clk_wakeup ) )
ret = dev_err_probe ( dev , PTR_ERR ( data - > clk_wakeup ) ,
" Failed to get wakeup clk \n " ) ;
2015-09-16 09:40:51 +08:00
return ret ;
}
data - > clk_ahb = devm_clk_get ( dev , " ahb " ) ;
if ( IS_ERR ( data - > clk_ahb ) ) {
ret = PTR_ERR ( data - > clk_ahb ) ;
dev_err ( dev ,
" Failed to get ahb clock, err=%d \n " , ret ) ;
return ret ;
}
data - > clk_per = devm_clk_get ( dev , " per " ) ;
if ( IS_ERR ( data - > clk_per ) ) {
ret = PTR_ERR ( data - > clk_per ) ;
dev_err ( dev ,
" Failed to get per clock, err=%d \n " , ret ) ;
return ret ;
}
data - > need_three_clks = true ;
return ret ;
}
static int imx_prepare_enable_clks ( struct device * dev )
{
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
int ret = 0 ;
if ( data - > need_three_clks ) {
ret = clk_prepare_enable ( data - > clk_ipg ) ;
if ( ret ) {
dev_err ( dev ,
" Failed to prepare/enable ipg clk, err=%d \n " ,
ret ) ;
return ret ;
}
ret = clk_prepare_enable ( data - > clk_ahb ) ;
if ( ret ) {
dev_err ( dev ,
" Failed to prepare/enable ahb clk, err=%d \n " ,
ret ) ;
clk_disable_unprepare ( data - > clk_ipg ) ;
return ret ;
}
ret = clk_prepare_enable ( data - > clk_per ) ;
if ( ret ) {
dev_err ( dev ,
" Failed to prepare/enable per clk, err=%d \n " ,
ret ) ;
clk_disable_unprepare ( data - > clk_ahb ) ;
clk_disable_unprepare ( data - > clk_ipg ) ;
return ret ;
}
} else {
ret = clk_prepare_enable ( data - > clk ) ;
if ( ret ) {
dev_err ( dev ,
" Failed to prepare/enable clk, err=%d \n " ,
ret ) ;
return ret ;
}
}
return ret ;
}
static void imx_disable_unprepare_clks ( struct device * dev )
{
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
if ( data - > need_three_clks ) {
clk_disable_unprepare ( data - > clk_per ) ;
clk_disable_unprepare ( data - > clk_ahb ) ;
clk_disable_unprepare ( data - > clk_ipg ) ;
} else {
clk_disable_unprepare ( data - > clk ) ;
}
}
2012-09-12 14:58:05 +03:00
2018-10-16 09:17:02 +08:00
static int ci_hdrc_imx_notify_event ( struct ci_hdrc * ci , unsigned int event )
{
struct device * dev = ci - > dev - > parent ;
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
int ret = 0 ;
2020-01-23 14:35:58 +08:00
struct imx_usbmisc_data * mdata = data - > usbmisc_data ;
2018-10-16 09:17:02 +08:00
switch ( event ) {
case CI_HDRC_IMX_HSIC_ACTIVE_EVENT :
2019-10-10 09:20:26 +08:00
if ( data - > pinctrl ) {
ret = pinctrl_select_state ( data - > pinctrl ,
data - > pinctrl_hsic_active ) ;
if ( ret )
dev_err ( dev ,
" hsic_active select failed, err=%d \n " ,
ret ) ;
}
2018-10-16 09:17:02 +08:00
break ;
case CI_HDRC_IMX_HSIC_SUSPEND_EVENT :
2020-01-23 14:35:58 +08:00
ret = imx_usbmisc_hsic_set_connect ( mdata ) ;
2018-10-16 09:17:02 +08:00
if ( ret )
dev_err ( dev ,
" hsic_set_connect failed, err=%d \n " , ret ) ;
break ;
2020-01-23 14:35:58 +08:00
case CI_HDRC_CONTROLLER_VBUS_EVENT :
if ( ci - > vbus_active )
ret = imx_usbmisc_charger_detection ( mdata , true ) ;
else
ret = imx_usbmisc_charger_detection ( mdata , false ) ;
if ( ci - > usb_phy )
schedule_work ( & ci - > usb_phy - > chg_work ) ;
break ;
2018-10-16 09:17:02 +08:00
default :
break ;
}
return ret ;
}
2013-06-24 14:46:36 +03:00
static int ci_hdrc_imx_probe ( struct platform_device * pdev )
2012-07-07 22:56:48 +08:00
{
2013-06-24 14:46:36 +03:00
struct ci_hdrc_imx_data * data ;
struct ci_hdrc_platform_data pdata = {
2014-03-11 13:47:38 +08:00
. name = dev_name ( & pdev - > dev ) ,
2013-06-13 17:59:58 +03:00
. capoffset = DEF_CAPOFFSET ,
2018-10-16 09:17:02 +08:00
. notify_event = ci_hdrc_imx_notify_event ,
2013-06-13 17:59:58 +03:00
} ;
2012-07-07 22:56:48 +08:00
int ret ;
2015-11-12 08:43:34 +01:00
const struct ci_hdrc_imx_platform_flag * imx_platform_flag ;
2018-03-29 17:52:49 +08:00
struct device_node * np = pdev - > dev . of_node ;
2018-10-16 09:17:02 +08:00
struct device * dev = & pdev - > dev ;
2015-11-12 08:43:34 +01:00
2020-11-24 13:39:12 -03:00
imx_platform_flag = of_device_get_match_data ( & pdev - > dev ) ;
2012-07-07 22:56:48 +08:00
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
2014-11-26 13:44:24 +08:00
if ( ! data )
2012-07-07 22:56:48 +08:00
return - ENOMEM ;
2019-04-28 10:35:31 +08:00
data - > plat_data = imx_platform_flag ;
pdata . flags | = imx_platform_flag - > flags ;
2015-09-16 09:40:51 +08:00
platform_set_drvdata ( pdev , data ) ;
2018-10-16 09:17:02 +08:00
data - > usbmisc_data = usbmisc_get_init_data ( dev ) ;
2013-08-14 12:44:16 +03:00
if ( IS_ERR ( data - > usbmisc_data ) )
return PTR_ERR ( data - > usbmisc_data ) ;
2019-01-17 09:24:20 +00:00
if ( ( of_usb_get_phy_mode ( dev - > of_node ) = = USBPHY_INTERFACE_MODE_HSIC )
& & data - > usbmisc_data ) {
2018-10-16 09:17:02 +08:00
pdata . flags | = CI_HDRC_IMX_IS_HSIC ;
data - > usbmisc_data - > hsic = 1 ;
data - > pinctrl = devm_pinctrl_get ( dev ) ;
2019-10-10 08:59:14 +08:00
if ( PTR_ERR ( data - > pinctrl ) = = - ENODEV )
data - > pinctrl = NULL ;
2022-06-14 14:05:22 +02:00
else if ( IS_ERR ( data - > pinctrl ) )
return dev_err_probe ( dev , PTR_ERR ( data - > pinctrl ) ,
" pinctrl get failed \n " ) ;
2018-10-16 09:17:02 +08:00
2019-10-10 09:20:26 +08:00
data - > hsic_pad_regulator =
devm_regulator_get_optional ( dev , " hsic " ) ;
if ( PTR_ERR ( data - > hsic_pad_regulator ) = = - ENODEV ) {
2022-11-04 10:58:38 +01:00
/* no pad regulator is needed */
2019-10-10 09:20:26 +08:00
data - > hsic_pad_regulator = NULL ;
2022-06-14 14:05:22 +02:00
} else if ( IS_ERR ( data - > hsic_pad_regulator ) )
return dev_err_probe ( dev , PTR_ERR ( data - > hsic_pad_regulator ) ,
" Get HSIC pad regulator error \n " ) ;
2019-10-10 09:20:26 +08:00
if ( data - > hsic_pad_regulator ) {
ret = regulator_enable ( data - > hsic_pad_regulator ) ;
if ( ret ) {
dev_err ( dev ,
" Failed to enable HSIC pad regulator \n " ) ;
return ret ;
}
}
}
/* HSIC pinctrl handling */
if ( data - > pinctrl ) {
struct pinctrl_state * pinctrl_hsic_idle ;
2018-10-16 09:17:02 +08:00
pinctrl_hsic_idle = pinctrl_lookup_state ( data - > pinctrl , " idle " ) ;
if ( IS_ERR ( pinctrl_hsic_idle ) ) {
dev_err ( dev ,
" pinctrl_hsic_idle lookup failed, err=%ld \n " ,
PTR_ERR ( pinctrl_hsic_idle ) ) ;
return PTR_ERR ( pinctrl_hsic_idle ) ;
}
ret = pinctrl_select_state ( data - > pinctrl , pinctrl_hsic_idle ) ;
if ( ret ) {
dev_err ( dev , " hsic_idle select failed, err=%d \n " , ret ) ;
return ret ;
}
data - > pinctrl_hsic_active = pinctrl_lookup_state ( data - > pinctrl ,
" active " ) ;
if ( IS_ERR ( data - > pinctrl_hsic_active ) ) {
dev_err ( dev ,
" pinctrl_hsic_active lookup failed, err=%ld \n " ,
PTR_ERR ( data - > pinctrl_hsic_active ) ) ;
return PTR_ERR ( data - > pinctrl_hsic_active ) ;
}
}
2019-04-28 10:35:31 +08:00
if ( pdata . flags & CI_HDRC_PMQOS )
2020-02-12 00:28:44 +01:00
cpu_latency_qos_add_request ( & data - > pm_qos_req , 0 ) ;
2019-04-28 10:35:31 +08:00
2018-10-16 09:17:02 +08:00
ret = imx_get_clks ( dev ) ;
2015-09-16 09:40:51 +08:00
if ( ret )
2018-10-16 09:17:02 +08:00
goto disable_hsic_regulator ;
2012-07-07 22:56:48 +08:00
2018-10-16 09:17:02 +08:00
ret = imx_prepare_enable_clks ( dev ) ;
2015-09-16 09:40:51 +08:00
if ( ret )
2018-10-16 09:17:02 +08:00
goto disable_hsic_regulator ;
2012-07-07 22:56:48 +08:00
2023-12-28 19:07:51 +08:00
ret = clk_prepare_enable ( data - > clk_wakeup ) ;
if ( ret )
goto err_wakeup_clk ;
2018-10-16 09:17:02 +08:00
data - > phy = devm_usb_get_phy_by_phandle ( dev , " fsl,usbphy " , 0 ) ;
2013-09-24 12:47:54 +08:00
if ( IS_ERR ( data - > phy ) ) {
ret = PTR_ERR ( data - > phy ) ;
2023-01-30 10:41:51 +01:00
if ( ret ! = - ENODEV ) {
dev_err_probe ( dev , ret , " Failed to parse fsl,usbphy \n " ) ;
2021-11-17 10:49:23 +03:00
goto err_clk ;
2023-01-30 10:41:51 +01:00
}
2021-11-17 10:49:23 +03:00
data - > phy = devm_usb_get_phy_by_phandle ( dev , " phys " , 0 ) ;
if ( IS_ERR ( data - > phy ) ) {
ret = PTR_ERR ( data - > phy ) ;
2023-01-30 10:41:51 +01:00
if ( ret = = - ENODEV ) {
2021-11-17 10:49:23 +03:00
data - > phy = NULL ;
2023-01-30 10:41:51 +01:00
} else {
dev_err_probe ( dev , ret , " Failed to parse phys \n " ) ;
2021-11-17 10:49:23 +03:00
goto err_clk ;
2023-01-30 10:41:51 +01:00
}
2021-09-21 08:37:54 -03:00
}
2012-07-07 22:56:48 +08:00
}
2014-10-30 18:41:16 +01:00
pdata . usb_phy = data - > phy ;
2020-01-23 14:35:58 +08:00
if ( data - > usbmisc_data )
data - > usbmisc_data - > usb_phy = data - > phy ;
2018-03-29 17:52:49 +08:00
2018-05-30 10:34:14 -07:00
if ( ( of_device_is_compatible ( np , " fsl,imx53-usb " ) | |
of_device_is_compatible ( np , " fsl,imx51-usb " ) ) & & pdata . usb_phy & &
2018-03-29 17:52:49 +08:00
of_usb_get_phy_mode ( np ) = = USBPHY_INTERFACE_MODE_ULPI ) {
pdata . flags | = CI_HDRC_OVERRIDE_PHY_CONTROL ;
data - > override_phy_control = true ;
usb_phy_init ( pdata . usb_phy ) ;
}
2015-02-11 12:44:47 +08:00
if ( pdata . flags & CI_HDRC_SUPPORTS_RUNTIME_PM )
data - > supports_runtime_pm = true ;
2014-01-10 13:51:28 +08:00
2015-02-11 12:44:44 +08:00
ret = imx_usbmisc_init ( data - > usbmisc_data ) ;
if ( ret ) {
2018-10-16 09:17:02 +08:00
dev_err ( dev , " usbmisc init failed, ret=%d \n " , ret ) ;
2015-02-11 12:44:44 +08:00
goto err_clk ;
2012-09-12 14:58:05 +03:00
}
2018-10-16 09:17:02 +08:00
data - > ci_pdev = ci_hdrc_add_device ( dev ,
2012-07-07 22:56:48 +08:00
pdev - > resource , pdev - > num_resources ,
2013-06-13 17:59:58 +03:00
& pdata ) ;
2013-06-13 17:59:48 +03:00
if ( IS_ERR ( data - > ci_pdev ) ) {
ret = PTR_ERR ( data - > ci_pdev ) ;
2022-06-14 14:05:22 +02:00
dev_err_probe ( dev , ret , " ci_hdrc_add_device failed \n " ) ;
2013-09-24 12:47:54 +08:00
goto err_clk ;
2012-07-07 22:56:48 +08:00
}
2019-10-09 10:52:28 +08:00
if ( data - > usbmisc_data ) {
if ( ! IS_ERR ( pdata . id_extcon . edev ) | |
of_property_read_bool ( np , " usb-role-switch " ) )
data - > usbmisc_data - > ext_id = 1 ;
if ( ! IS_ERR ( pdata . vbus_extcon . edev ) | |
of_property_read_bool ( np , " usb-role-switch " ) )
data - > usbmisc_data - > ext_vbus = 1 ;
2020-07-28 14:12:11 +08:00
/* usbmisc needs to know dr mode to choose wakeup setting */
data - > usbmisc_data - > available_role =
ci_hdrc_query_available_role ( data - > ci_pdev ) ;
2019-10-09 10:52:28 +08:00
}
2019-09-09 14:41:41 +08:00
2015-02-11 12:44:44 +08:00
ret = imx_usbmisc_init_post ( data - > usbmisc_data ) ;
if ( ret ) {
2018-10-16 09:17:02 +08:00
dev_err ( dev , " usbmisc post failed, ret=%d \n " , ret ) ;
2015-02-11 12:44:44 +08:00
goto disable_device ;
2013-03-30 12:54:01 +02:00
}
2015-02-11 12:44:47 +08:00
if ( data - > supports_runtime_pm ) {
2018-10-16 09:17:02 +08:00
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
2015-02-11 12:44:47 +08:00
}
2012-07-07 22:56:48 +08:00
2018-10-16 09:17:02 +08:00
device_set_wakeup_capable ( dev , true ) ;
2015-02-11 12:44:49 +08:00
2012-07-07 22:56:48 +08:00
return 0 ;
2013-06-13 17:59:48 +03:00
disable_device :
2013-06-24 14:46:36 +03:00
ci_hdrc_remove_device ( data - > ci_pdev ) ;
2013-06-13 18:00:00 +03:00
err_clk :
2023-12-28 19:07:51 +08:00
clk_disable_unprepare ( data - > clk_wakeup ) ;
err_wakeup_clk :
2018-10-16 09:17:02 +08:00
imx_disable_unprepare_clks ( dev ) ;
disable_hsic_regulator :
if ( data - > hsic_pad_regulator )
2019-08-10 16:07:58 +01:00
/* don't overwrite original ret (cf. EPROBE_DEFER) */
regulator_disable ( data - > hsic_pad_regulator ) ;
2019-04-28 10:35:31 +08:00
if ( pdata . flags & CI_HDRC_PMQOS )
2020-02-12 00:28:44 +01:00
cpu_latency_qos_remove_request ( & data - > pm_qos_req ) ;
2019-08-10 16:07:58 +01:00
data - > ci_pdev = NULL ;
2012-07-07 22:56:48 +08:00
return ret ;
}
2023-05-18 01:01:07 +02:00
static void ci_hdrc_imx_remove ( struct platform_device * pdev )
2012-07-07 22:56:48 +08:00
{
2013-06-24 14:46:36 +03:00
struct ci_hdrc_imx_data * data = platform_get_drvdata ( pdev ) ;
2012-07-07 22:56:48 +08:00
2015-02-11 12:44:47 +08:00
if ( data - > supports_runtime_pm ) {
pm_runtime_get_sync ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_put_noidle ( & pdev - > dev ) ;
}
2019-08-10 16:07:58 +01:00
if ( data - > ci_pdev )
ci_hdrc_remove_device ( data - > ci_pdev ) ;
2018-03-29 17:52:49 +08:00
if ( data - > override_phy_control )
usb_phy_shutdown ( data - > phy ) ;
2019-08-10 16:07:58 +01:00
if ( data - > ci_pdev ) {
imx_disable_unprepare_clks ( & pdev - > dev ) ;
2023-12-28 19:07:51 +08:00
clk_disable_unprepare ( data - > clk_wakeup ) ;
2019-08-10 16:07:58 +01:00
if ( data - > plat_data - > flags & CI_HDRC_PMQOS )
2020-02-12 00:28:44 +01:00
cpu_latency_qos_remove_request ( & data - > pm_qos_req ) ;
2019-08-10 16:07:58 +01:00
if ( data - > hsic_pad_regulator )
regulator_disable ( data - > hsic_pad_regulator ) ;
}
2012-07-07 22:56:48 +08:00
}
2015-11-12 14:14:40 +01:00
static void ci_hdrc_imx_shutdown ( struct platform_device * pdev )
{
ci_hdrc_imx_remove ( pdev ) ;
}
2022-10-13 23:14:39 +08:00
static int __maybe_unused imx_controller_suspend ( struct device * dev ,
pm_message_t msg )
2014-11-26 13:44:30 +08:00
{
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
2018-10-16 09:17:02 +08:00
int ret = 0 ;
2014-11-26 13:44:30 +08:00
dev_dbg ( dev , " at %s \n " , __func__ ) ;
2022-10-13 23:14:39 +08:00
ret = imx_usbmisc_suspend ( data - > usbmisc_data ,
PMSG_IS_AUTO ( msg ) | | device_may_wakeup ( dev ) ) ;
2018-10-16 09:17:02 +08:00
if ( ret ) {
2022-10-13 23:14:39 +08:00
dev_err ( dev ,
" usbmisc suspend failed, ret=%d \n " , ret ) ;
2018-10-16 09:17:02 +08:00
return ret ;
}
2015-09-16 09:40:51 +08:00
imx_disable_unprepare_clks ( dev ) ;
2019-04-28 10:35:31 +08:00
if ( data - > plat_data - > flags & CI_HDRC_PMQOS )
2020-02-12 00:28:44 +01:00
cpu_latency_qos_remove_request ( & data - > pm_qos_req ) ;
2019-04-28 10:35:31 +08:00
2015-02-11 12:44:47 +08:00
data - > in_lpm = true ;
2014-11-26 13:44:30 +08:00
return 0 ;
}
2022-10-13 23:14:39 +08:00
static int __maybe_unused imx_controller_resume ( struct device * dev ,
pm_message_t msg )
2014-11-26 13:44:30 +08:00
{
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
2015-02-11 12:44:47 +08:00
int ret = 0 ;
2014-11-26 13:44:30 +08:00
dev_dbg ( dev , " at %s \n " , __func__ ) ;
2015-02-11 12:44:47 +08:00
if ( ! data - > in_lpm ) {
WARN_ON ( 1 ) ;
return 0 ;
}
2019-04-28 10:35:31 +08:00
if ( data - > plat_data - > flags & CI_HDRC_PMQOS )
2020-02-12 00:28:44 +01:00
cpu_latency_qos_add_request ( & data - > pm_qos_req , 0 ) ;
2019-04-28 10:35:31 +08:00
2015-09-16 09:40:51 +08:00
ret = imx_prepare_enable_clks ( dev ) ;
2015-02-11 12:44:47 +08:00
if ( ret )
return ret ;
data - > in_lpm = false ;
2022-10-13 23:14:39 +08:00
ret = imx_usbmisc_resume ( data - > usbmisc_data ,
PMSG_IS_AUTO ( msg ) | | device_may_wakeup ( dev ) ) ;
2015-02-11 12:44:47 +08:00
if ( ret ) {
2022-10-13 23:14:39 +08:00
dev_err ( dev , " usbmisc resume failed, ret=%d \n " , ret ) ;
2015-02-11 12:44:47 +08:00
goto clk_disable ;
}
return 0 ;
clk_disable :
2015-09-16 09:40:51 +08:00
imx_disable_unprepare_clks ( dev ) ;
2015-02-11 12:44:47 +08:00
return ret ;
2014-11-26 13:44:30 +08:00
}
2018-09-02 19:37:04 +02:00
static int __maybe_unused ci_hdrc_imx_suspend ( struct device * dev )
2014-11-26 13:44:30 +08:00
{
2015-02-11 12:44:49 +08:00
int ret ;
2015-02-11 12:44:47 +08:00
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
if ( data - > in_lpm )
/* The core's suspend doesn't run */
return 0 ;
2022-10-13 23:14:39 +08:00
ret = imx_controller_suspend ( dev , PMSG_SUSPEND ) ;
2018-11-01 14:47:20 +08:00
if ( ret )
return ret ;
pinctrl_pm_select_sleep_state ( dev ) ;
return ret ;
2014-11-26 13:44:30 +08:00
}
2018-09-02 19:37:04 +02:00
static int __maybe_unused ci_hdrc_imx_resume ( struct device * dev )
2014-11-26 13:44:30 +08:00
{
2015-02-11 12:44:47 +08:00
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
int ret ;
2018-11-01 14:47:20 +08:00
pinctrl_pm_select_default_state ( dev ) ;
2022-10-13 23:14:39 +08:00
ret = imx_controller_resume ( dev , PMSG_RESUME ) ;
2015-02-11 12:44:47 +08:00
if ( ! ret & & data - > supports_runtime_pm ) {
pm_runtime_disable ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
}
return ret ;
2014-11-26 13:44:30 +08:00
}
2018-09-02 19:37:04 +02:00
static int __maybe_unused ci_hdrc_imx_runtime_suspend ( struct device * dev )
2015-02-11 12:44:47 +08:00
{
struct ci_hdrc_imx_data * data = dev_get_drvdata ( dev ) ;
if ( data - > in_lpm ) {
WARN_ON ( 1 ) ;
return 0 ;
}
2022-10-13 23:14:39 +08:00
return imx_controller_suspend ( dev , PMSG_AUTO_SUSPEND ) ;
2015-02-11 12:44:47 +08:00
}
2018-09-02 19:37:04 +02:00
static int __maybe_unused ci_hdrc_imx_runtime_resume ( struct device * dev )
2015-02-11 12:44:47 +08:00
{
2022-10-13 23:14:39 +08:00
return imx_controller_resume ( dev , PMSG_AUTO_RESUME ) ;
2015-02-11 12:44:47 +08:00
}
2014-11-26 13:44:30 +08:00
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( ci_hdrc_imx_suspend , ci_hdrc_imx_resume )
2015-02-11 12:44:47 +08:00
SET_RUNTIME_PM_OPS ( ci_hdrc_imx_runtime_suspend ,
ci_hdrc_imx_runtime_resume , NULL )
2014-11-26 13:44:30 +08:00
} ;
2013-06-24 14:46:36 +03:00
static struct platform_driver ci_hdrc_imx_driver = {
. probe = ci_hdrc_imx_probe ,
2023-05-18 01:01:07 +02:00
. remove_new = ci_hdrc_imx_remove ,
2015-11-12 14:14:40 +01:00
. shutdown = ci_hdrc_imx_shutdown ,
2012-07-07 22:56:48 +08:00
. driver = {
. name = " imx_usb " ,
2013-06-24 14:46:36 +03:00
. of_match_table = ci_hdrc_imx_dt_ids ,
2014-11-26 13:44:30 +08:00
. pm = & ci_hdrc_imx_pm_ops ,
2012-07-07 22:56:48 +08:00
} ,
} ;
2013-06-24 14:46:36 +03:00
module_platform_driver ( ci_hdrc_imx_driver ) ;
2012-07-07 22:56:48 +08:00
MODULE_ALIAS ( " platform:imx-usb " ) ;
2018-09-02 19:36:50 +02:00
MODULE_LICENSE ( " GPL " ) ;
2013-06-24 14:46:36 +03:00
MODULE_DESCRIPTION ( " CI HDRC i.MX USB binding " ) ;
2012-07-07 22:56:48 +08:00
MODULE_AUTHOR ( " Marek Vasut <marex@denx.de> " ) ;
MODULE_AUTHOR ( " Richard Zhao <richard.zhao@freescale.com> " ) ;