2019-03-29 12:14:03 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
* Phy provider for USB 3.0 controller on HiSilicon 3660 platform
*
* Copyright ( C ) 2017 - 2018 Hilisicon Electronics Co . , Ltd .
* http : //www.huawei.com
*
* Authors : Yu Chen < chenyu56 @ huawei . com >
*/
# include <linux/kernel.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# define PERI_CRG_CLK_EN4 0x40
# define PERI_CRG_CLK_DIS4 0x44
# define GT_CLK_USB3OTG_REF BIT(0)
# define GT_ACLK_USB3OTG BIT(1)
# define PERI_CRG_RSTEN4 0x90
# define PERI_CRG_RSTDIS4 0x94
# define IP_RST_USB3OTGPHY_POR BIT(3)
# define IP_RST_USB3OTG BIT(5)
# define PERI_CRG_ISODIS 0x148
# define USB_REFCLK_ISO_EN BIT(25)
# define PCTRL_PERI_CTRL3 0x10
# define PCTRL_PERI_CTRL3_MSK_START 16
# define USB_TCXO_EN BIT(1)
# define PCTRL_PERI_CTRL24 0x64
# define SC_CLK_USB3PHY_3MUX1_SEL BIT(25)
# define USBOTG3_CTRL0 0x00
# define SC_USB3PHY_ABB_GT_EN BIT(15)
# define USBOTG3_CTRL2 0x08
# define USBOTG3CTRL2_POWERDOWN_HSP BIT(0)
# define USBOTG3CTRL2_POWERDOWN_SSP BIT(1)
# define USBOTG3_CTRL3 0x0C
# define USBOTG3_CTRL3_VBUSVLDEXT BIT(6)
# define USBOTG3_CTRL3_VBUSVLDEXTSEL BIT(5)
# define USBOTG3_CTRL4 0x10
# define USBOTG3_CTRL7 0x1c
# define REF_SSP_EN BIT(16)
/* This value config the default txtune parameter of the usb 2.0 phy */
# define HI3660_USB_DEFAULT_PHY_PARAM 0x1c466e3
struct hi3660_priv {
struct device * dev ;
struct regmap * peri_crg ;
struct regmap * pctrl ;
struct regmap * otg_bc ;
u32 eye_diagram_param ;
} ;
static int hi3660_phy_init ( struct phy * phy )
{
struct hi3660_priv * priv = phy_get_drvdata ( phy ) ;
u32 val , mask ;
int ret ;
/* usb refclk iso disable */
ret = regmap_write ( priv - > peri_crg , PERI_CRG_ISODIS , USB_REFCLK_ISO_EN ) ;
if ( ret )
goto out ;
/* enable usb_tcxo_en */
val = USB_TCXO_EN | ( USB_TCXO_EN < < PCTRL_PERI_CTRL3_MSK_START ) ;
ret = regmap_write ( priv - > pctrl , PCTRL_PERI_CTRL3 , val ) ;
if ( ret )
goto out ;
/* assert phy */
val = IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG ;
ret = regmap_write ( priv - > peri_crg , PERI_CRG_RSTEN4 , val ) ;
if ( ret )
goto out ;
/* enable phy ref clk */
val = SC_USB3PHY_ABB_GT_EN ;
mask = val ;
ret = regmap_update_bits ( priv - > otg_bc , USBOTG3_CTRL0 , mask , val ) ;
if ( ret )
goto out ;
val = REF_SSP_EN ;
mask = val ;
ret = regmap_update_bits ( priv - > otg_bc , USBOTG3_CTRL7 , mask , val ) ;
if ( ret )
goto out ;
/* exit from IDDQ mode */
mask = USBOTG3CTRL2_POWERDOWN_HSP | USBOTG3CTRL2_POWERDOWN_SSP ;
ret = regmap_update_bits ( priv - > otg_bc , USBOTG3_CTRL2 , mask , 0 ) ;
if ( ret )
goto out ;
/* delay for exit from IDDQ mode */
usleep_range ( 100 , 120 ) ;
/* deassert phy */
val = IP_RST_USB3OTGPHY_POR | IP_RST_USB3OTG ;
ret = regmap_write ( priv - > peri_crg , PERI_CRG_RSTDIS4 , val ) ;
if ( ret )
goto out ;
/* delay for phy deasserted */
usleep_range ( 10000 , 15000 ) ;
/* fake vbus valid signal */
val = USBOTG3_CTRL3_VBUSVLDEXT | USBOTG3_CTRL3_VBUSVLDEXTSEL ;
mask = val ;
ret = regmap_update_bits ( priv - > otg_bc , USBOTG3_CTRL3 , mask , val ) ;
if ( ret )
goto out ;
/* delay for vbus valid */
usleep_range ( 100 , 120 ) ;
ret = regmap_write ( priv - > otg_bc , USBOTG3_CTRL4 ,
priv - > eye_diagram_param ) ;
if ( ret )
goto out ;
return 0 ;
out :
dev_err ( priv - > dev , " failed to init phy ret: %d \n " , ret ) ;
return ret ;
}
static int hi3660_phy_exit ( struct phy * phy )
{
struct hi3660_priv * priv = phy_get_drvdata ( phy ) ;
u32 val ;
int ret ;
/* assert phy */
val = IP_RST_USB3OTGPHY_POR ;
ret = regmap_write ( priv - > peri_crg , PERI_CRG_RSTEN4 , val ) ;
if ( ret )
goto out ;
/* disable usb_tcxo_en */
val = USB_TCXO_EN < < PCTRL_PERI_CTRL3_MSK_START ;
ret = regmap_write ( priv - > pctrl , PCTRL_PERI_CTRL3 , val ) ;
if ( ret )
goto out ;
return 0 ;
out :
dev_err ( priv - > dev , " failed to exit phy ret: %d \n " , ret ) ;
return ret ;
}
2020-08-24 00:00:20 +02:00
static const struct phy_ops hi3660_phy_ops = {
2019-03-29 12:14:03 +08:00
. init = hi3660_phy_init ,
. exit = hi3660_phy_exit ,
. owner = THIS_MODULE ,
} ;
static int hi3660_phy_probe ( struct platform_device * pdev )
{
struct phy_provider * phy_provider ;
struct device * dev = & pdev - > dev ;
struct phy * phy ;
struct hi3660_priv * priv ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > dev = dev ;
priv - > peri_crg = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" hisilicon,pericrg-syscon " ) ;
if ( IS_ERR ( priv - > peri_crg ) ) {
dev_err ( dev , " no hisilicon,pericrg-syscon \n " ) ;
return PTR_ERR ( priv - > peri_crg ) ;
}
priv - > pctrl = syscon_regmap_lookup_by_phandle ( dev - > of_node ,
" hisilicon,pctrl-syscon " ) ;
if ( IS_ERR ( priv - > pctrl ) ) {
dev_err ( dev , " no hisilicon,pctrl-syscon \n " ) ;
return PTR_ERR ( priv - > pctrl ) ;
}
/* node of hi3660 phy is a sub-node of usb3_otg_bc */
priv - > otg_bc = syscon_node_to_regmap ( dev - > parent - > of_node ) ;
if ( IS_ERR ( priv - > otg_bc ) ) {
dev_err ( dev , " no hisilicon,usb3-otg-bc-syscon \n " ) ;
return PTR_ERR ( priv - > otg_bc ) ;
}
if ( of_property_read_u32 ( dev - > of_node , " hisilicon,eye-diagram-param " ,
& ( priv - > eye_diagram_param ) ) )
priv - > eye_diagram_param = HI3660_USB_DEFAULT_PHY_PARAM ;
phy = devm_phy_create ( dev , NULL , & hi3660_phy_ops ) ;
if ( IS_ERR ( phy ) )
return PTR_ERR ( phy ) ;
phy_set_drvdata ( phy , priv ) ;
phy_provider = devm_of_phy_provider_register ( dev , of_phy_simple_xlate ) ;
return PTR_ERR_OR_ZERO ( phy_provider ) ;
}
static const struct of_device_id hi3660_phy_of_match [ ] = {
{ . compatible = " hisilicon,hi3660-usb-phy " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , hi3660_phy_of_match ) ;
static struct platform_driver hi3660_phy_driver = {
. probe = hi3660_phy_probe ,
. driver = {
. name = " hi3660-usb-phy " ,
. of_match_table = hi3660_phy_of_match ,
}
} ;
module_platform_driver ( hi3660_phy_driver ) ;
MODULE_AUTHOR ( " Yu Chen <chenyu56@huawei.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Hilisicon Hi3660 USB3 PHY Driver " ) ;