2021-09-28 09:34:11 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* PCIe phy driver for Kirin 970
*
* Copyright ( C ) 2017 HiSilicon Electronics Co . , Ltd .
* https : //www.huawei.com
* Copyright ( C ) 2021 Huawei Technologies Co . , Ltd .
* https : //www.huawei.com
*
* Authors :
* Mauro Carvalho Chehab < mchehab + huawei @ kernel . org >
* Manivannan Sadhasivam < mani @ kernel . org >
*
* Based on :
* https : //lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/
*/
# include <linux/bitfield.h>
# include <linux/clk.h>
# include <linux/gpio.h>
# include <linux/kernel.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/of_gpio.h>
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# define AXI_CLK_FREQ 207500000
# define REF_CLK_FREQ 100000000
/* PCIe CTRL registers */
# define SOC_PCIECTRL_CTRL7_ADDR 0x01c
# define SOC_PCIECTRL_CTRL12_ADDR 0x030
# define SOC_PCIECTRL_CTRL20_ADDR 0x050
# define SOC_PCIECTRL_CTRL21_ADDR 0x054
# define PCIE_OUTPUT_PULL_BITS GENMASK(3, 0)
# define SOC_PCIECTRL_CTRL20_2P_MEM_CTRL 0x02605550
# define SOC_PCIECTRL_CTRL21_DEFAULT 0x20000070
# define PCIE_PULL_UP_SYS_AUX_PWR_DET BIT(10)
# define PCIE_OUTPUT_PULL_DOWN BIT(1)
/* PCIe PHY registers */
# define SOC_PCIEPHY_CTRL0_ADDR 0x000
# define SOC_PCIEPHY_CTRL1_ADDR 0x004
# define SOC_PCIEPHY_CTRL38_ADDR 0x0098
# define SOC_PCIEPHY_STATE0_ADDR 0x400
# define RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 0xc004
# define SUP_DIG_LVL_OVRD_IN 0x003c
# define LANEN_DIG_ASIC_TX_OVRD_IN_1 0x4008
# define LANEN_DIG_ASIC_TX_OVRD_IN_2 0x400c
# define PCIEPHY_RESET_BIT BIT(17)
# define PCIEPHY_PIPE_LINE0_RESET_BIT BIT(19)
# define PCIE_TXDETECT_RX_FAIL BIT(2)
# define PCIE_CLK_SOURCE BIT(8)
# define PCIE_IS_CLOCK_STABLE BIT(19)
# define PCIE_PULL_DOWN_PHY_TEST_POWERDOWN BIT(22)
# define PCIE_DEASSERT_CONTROLLER_PERST BIT(2)
# define EYEPARAM_NOCFG 0xffffffff
# define EYE_PARM0_MASK GENMASK(8, 6)
# define EYE_PARM1_MASK GENMASK(11, 8)
# define EYE_PARM2_MASK GENMASK(5, 0)
# define EYE_PARM3_MASK GENMASK(12, 7)
# define EYE_PARM4_MASK GENMASK(14, 9)
# define EYE_PARM0_EN BIT(9)
# define EYE_PARM1_EN BIT(12)
# define EYE_PARM2_EN BIT(6)
# define EYE_PARM3_EN BIT(13)
# define EYE_PARM4_EN BIT(15)
/* hi3670 pciephy register */
# define APB_PHY_START_ADDR 0x40000
# define SOC_PCIEPHY_MMC1PLL_CTRL1 0xc04
# define SOC_PCIEPHY_MMC1PLL_CTRL16 0xC40
# define SOC_PCIEPHY_MMC1PLL_CTRL17 0xC44
# define SOC_PCIEPHY_MMC1PLL_CTRL20 0xC50
# define SOC_PCIEPHY_MMC1PLL_CTRL21 0xC54
# define SOC_PCIEPHY_MMC1PLL_STAT0 0xE00
# define CRGPERIPH_PEREN12 0x470
# define CRGPERIPH_PERDIS12 0x474
# define CRGPERIPH_PCIECTRL0 0x800
# define PCIE_FNPLL_FBDIV_MASK GENMASK(27, 16)
# define PCIE_FNPLL_FRACDIV_MASK GENMASK(23, 0)
# define PCIE_FNPLL_POSTDIV1_MASK GENMASK(10, 8)
# define PCIE_FNPLL_POSTDIV2_MASK GENMASK(14, 12)
# define PCIE_FNPLL_PLL_MODE_MASK BIT(25)
# define PCIE_FNPLL_DLL_EN BIT(27)
# define PCIE_FNPLL_FBDIV 0xd0
# define PCIE_FNPLL_FRACDIV 0x555555
# define PCIE_FNPLL_POSTDIV1 0x5
# define PCIE_FNPLL_POSTDIV2 0x4
# define PCIE_FNPLL_PLL_MODE 0x0
# define PCIE_PHY_MMC1PLL 0x20
# define PCIE_PHY_CHOOSE_FNPLL BIT(27)
# define PCIE_PHY_MMC1PLL_DISABLE BIT(0)
# define PCIE_PHY_PCIEPL_BP BIT(16)
/* define ie,oe cfg */
# define IO_OE_HARD_GT_MODE BIT(1)
# define IO_IE_EN_HARD_BYPASS BIT(27)
# define IO_OE_EN_HARD_BYPASS BIT(11)
# define IO_HARD_CTRL_DEBOUNCE_BYPASS BIT(10)
# define IO_OE_GT_MODE BIT(8)
# define DEBOUNCE_WAITCFG_IN GENMASK(23, 20)
# define DEBOUNCE_WAITCFG_OUT GENMASK(16, 13)
# define IO_HP_DEBOUNCE_GT (BIT(12) | BIT(15))
# define IO_PHYREF_SOFT_GT_MODE BIT(14)
# define IO_REF_SOFT_GT_MODE BIT(13)
# define IO_REF_HARD_GT_MODE BIT(0)
/* noc power domain */
# define NOC_POWER_IDLEREQ_1 0x38c
# define NOC_POWER_IDLE_1 0x394
# define NOC_PW_MASK 0x10000
# define NOC_PW_SET_BIT 0x1
# define NUM_EYEPARAM 5
/* info located in sysctrl */
# define SCTRL_PCIE_CMOS_OFFSET 0x60
# define SCTRL_PCIE_CMOS_BIT 0x10
# define SCTRL_PCIE_ISO_OFFSET 0x44
# define SCTRL_PCIE_ISO_BIT 0x30
# define SCTRL_PCIE_HPCLK_OFFSET 0x190
# define SCTRL_PCIE_HPCLK_BIT 0x184000
# define SCTRL_PCIE_OE_OFFSET 0x14a
# define PCIE_DEBOUNCE_PARAM 0xf0f400
# define PCIE_OE_BYPASS GENMASK(29, 28)
/* peri_crg ctrl */
# define CRGCTRL_PCIE_ASSERT_OFFSET 0x88
# define CRGCTRL_PCIE_ASSERT_BIT 0x8c000000
# define FNPLL_HAS_LOCKED BIT(4)
/* Time for delay */
# define TIME_CMOS_MIN 100
# define TIME_CMOS_MAX 105
# define PIPE_CLK_STABLE_TIME 100
# define PLL_CTRL_WAIT_TIME 200
# define NOC_POWER_TIME 100
struct hi3670_pcie_phy {
struct device * dev ;
void __iomem * base ;
struct regmap * apb ;
struct regmap * crgctrl ;
struct regmap * sysctrl ;
struct regmap * pmctrl ;
struct clk * apb_sys_clk ;
struct clk * apb_phy_clk ;
struct clk * phy_ref_clk ;
struct clk * aclk ;
struct clk * aux_clk ;
u32 eye_param [ NUM_EYEPARAM ] ;
} ;
/* Registers in PCIePHY */
static inline void hi3670_apb_phy_writel ( struct hi3670_pcie_phy * phy , u32 val ,
u32 reg )
{
writel ( val , phy - > base + APB_PHY_START_ADDR + reg ) ;
}
static inline u32 hi3670_apb_phy_readl ( struct hi3670_pcie_phy * phy , u32 reg )
{
return readl ( phy - > base + APB_PHY_START_ADDR + reg ) ;
}
static inline void hi3670_apb_phy_updatel ( struct hi3670_pcie_phy * phy ,
u32 val , u32 mask , u32 reg )
{
u32 regval ;
regval = hi3670_apb_phy_readl ( phy , reg ) ;
regval & = ~ mask ;
regval | = val ;
hi3670_apb_phy_writel ( phy , regval , reg ) ;
}
static inline void kirin_apb_natural_phy_writel ( struct hi3670_pcie_phy * phy ,
u32 val , u32 reg )
{
writel ( val , phy - > base + reg ) ;
}
static inline u32 kirin_apb_natural_phy_readl ( struct hi3670_pcie_phy * phy ,
u32 reg )
{
return readl ( phy - > base + reg ) ;
}
static void hi3670_pcie_phy_oe_enable ( struct hi3670_pcie_phy * phy , bool enable )
{
u32 val ;
regmap_read ( phy - > sysctrl , SCTRL_PCIE_OE_OFFSET , & val ) ;
val | = PCIE_DEBOUNCE_PARAM ;
if ( enable )
val & = ~ PCIE_OE_BYPASS ;
else
val | = PCIE_OE_BYPASS ;
regmap_write ( phy - > sysctrl , SCTRL_PCIE_OE_OFFSET , val ) ;
}
static void hi3670_pcie_get_eyeparam ( struct hi3670_pcie_phy * phy )
{
struct device * dev = phy - > dev ;
struct device_node * np ;
int ret , i ;
np = dev - > of_node ;
ret = of_property_read_u32_array ( np , " hisilicon,eye-diagram-param " ,
phy - > eye_param , NUM_EYEPARAM ) ;
if ( ! ret )
return ;
/* There's no optional eye_param property. Set array to default */
for ( i = 0 ; i < NUM_EYEPARAM ; i + + )
phy - > eye_param [ i ] = EYEPARAM_NOCFG ;
}
static void hi3670_pcie_set_eyeparam ( struct hi3670_pcie_phy * phy )
{
u32 val ;
val = kirin_apb_natural_phy_readl ( phy , RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 ) ;
if ( phy - > eye_param [ 1 ] ! = EYEPARAM_NOCFG ) {
val & = ~ EYE_PARM1_MASK ;
val | = FIELD_PREP ( EYE_PARM1_MASK , phy - > eye_param [ 1 ] ) ;
val | = EYE_PARM1_EN ;
}
kirin_apb_natural_phy_writel ( phy , val ,
RAWLANEN_DIG_PCS_XF_TX_OVRD_IN_1 ) ;
val = kirin_apb_natural_phy_readl ( phy , LANEN_DIG_ASIC_TX_OVRD_IN_2 ) ;
val & = ~ ( EYE_PARM2_MASK | EYE_PARM3_MASK ) ;
if ( phy - > eye_param [ 2 ] ! = EYEPARAM_NOCFG ) {
val | = FIELD_PREP ( EYE_PARM2_MASK , phy - > eye_param [ 2 ] ) ;
val | = EYE_PARM2_EN ;
}
if ( phy - > eye_param [ 3 ] ! = EYEPARAM_NOCFG ) {
val | = FIELD_PREP ( EYE_PARM3_MASK , phy - > eye_param [ 3 ] ) ;
val | = EYE_PARM3_EN ;
}
kirin_apb_natural_phy_writel ( phy , val , LANEN_DIG_ASIC_TX_OVRD_IN_2 ) ;
val = kirin_apb_natural_phy_readl ( phy , SUP_DIG_LVL_OVRD_IN ) ;
if ( phy - > eye_param [ 0 ] ! = EYEPARAM_NOCFG ) {
val & = ~ EYE_PARM0_MASK ;
val | = FIELD_PREP ( EYE_PARM0_MASK , phy - > eye_param [ 0 ] ) ;
val | = EYE_PARM0_EN ;
}
kirin_apb_natural_phy_writel ( phy , val , SUP_DIG_LVL_OVRD_IN ) ;
val = kirin_apb_natural_phy_readl ( phy , LANEN_DIG_ASIC_TX_OVRD_IN_1 ) ;
if ( phy - > eye_param [ 4 ] ! = EYEPARAM_NOCFG ) {
val & = ~ EYE_PARM4_MASK ;
val | = FIELD_PREP ( EYE_PARM4_MASK , phy - > eye_param [ 4 ] ) ;
val | = EYE_PARM4_EN ;
}
kirin_apb_natural_phy_writel ( phy , val , LANEN_DIG_ASIC_TX_OVRD_IN_1 ) ;
}
static void hi3670_pcie_natural_cfg ( struct hi3670_pcie_phy * phy )
{
u32 val ;
/* change 2p mem_ctrl */
regmap_write ( phy - > apb , SOC_PCIECTRL_CTRL20_ADDR ,
SOC_PCIECTRL_CTRL20_2P_MEM_CTRL ) ;
regmap_read ( phy - > apb , SOC_PCIECTRL_CTRL7_ADDR , & val ) ;
val | = PCIE_PULL_UP_SYS_AUX_PWR_DET ;
regmap_write ( phy - > apb , SOC_PCIECTRL_CTRL7_ADDR , val ) ;
/* output, pull down */
regmap_read ( phy - > apb , SOC_PCIECTRL_CTRL12_ADDR , & val ) ;
val & = ~ PCIE_OUTPUT_PULL_BITS ;
val | = PCIE_OUTPUT_PULL_DOWN ;
regmap_write ( phy - > apb , SOC_PCIECTRL_CTRL12_ADDR , val ) ;
/* Handle phy_reset and lane0_reset to HW */
hi3670_apb_phy_updatel ( phy , PCIEPHY_RESET_BIT ,
PCIEPHY_PIPE_LINE0_RESET_BIT | PCIEPHY_RESET_BIT ,
SOC_PCIEPHY_CTRL1_ADDR ) ;
/* fix chip bug: TxDetectRx fail */
hi3670_apb_phy_updatel ( phy , PCIE_TXDETECT_RX_FAIL , PCIE_TXDETECT_RX_FAIL ,
SOC_PCIEPHY_CTRL38_ADDR ) ;
}
static void hi3670_pcie_pll_init ( struct hi3670_pcie_phy * phy )
{
hi3670_apb_phy_updatel ( phy , PCIE_PHY_CHOOSE_FNPLL , PCIE_PHY_CHOOSE_FNPLL ,
SOC_PCIEPHY_MMC1PLL_CTRL1 ) ;
hi3670_apb_phy_updatel ( phy ,
FIELD_PREP ( PCIE_FNPLL_FBDIV_MASK , PCIE_FNPLL_FBDIV ) ,
PCIE_FNPLL_FBDIV_MASK ,
SOC_PCIEPHY_MMC1PLL_CTRL16 ) ;
hi3670_apb_phy_updatel ( phy ,
FIELD_PREP ( PCIE_FNPLL_FRACDIV_MASK , PCIE_FNPLL_FRACDIV ) ,
PCIE_FNPLL_FRACDIV_MASK , SOC_PCIEPHY_MMC1PLL_CTRL17 ) ;
hi3670_apb_phy_updatel ( phy ,
PCIE_FNPLL_DLL_EN |
FIELD_PREP ( PCIE_FNPLL_POSTDIV1_MASK , PCIE_FNPLL_POSTDIV1 ) |
FIELD_PREP ( PCIE_FNPLL_POSTDIV2_MASK , PCIE_FNPLL_POSTDIV2 ) |
FIELD_PREP ( PCIE_FNPLL_PLL_MODE_MASK , PCIE_FNPLL_PLL_MODE ) ,
PCIE_FNPLL_POSTDIV1_MASK |
PCIE_FNPLL_POSTDIV2_MASK |
PCIE_FNPLL_PLL_MODE_MASK | PCIE_FNPLL_DLL_EN ,
SOC_PCIEPHY_MMC1PLL_CTRL20 ) ;
hi3670_apb_phy_writel ( phy , PCIE_PHY_MMC1PLL ,
SOC_PCIEPHY_MMC1PLL_CTRL21 ) ;
}
static int hi3670_pcie_pll_ctrl ( struct hi3670_pcie_phy * phy , bool enable )
{
struct device * dev = phy - > dev ;
u32 val ;
int time = PLL_CTRL_WAIT_TIME ;
if ( enable ) {
/* pd = 0 */
hi3670_apb_phy_updatel ( phy , 0 , PCIE_PHY_MMC1PLL_DISABLE ,
SOC_PCIEPHY_MMC1PLL_CTRL16 ) ;
/* choose FNPLL */
val = hi3670_apb_phy_readl ( phy , SOC_PCIEPHY_MMC1PLL_STAT0 ) ;
while ( ! ( val & FNPLL_HAS_LOCKED ) ) {
if ( ! time ) {
dev_err ( dev , " wait for pll_lock timeout \n " ) ;
return - EINVAL ;
}
time - - ;
udelay ( 1 ) ;
val = hi3670_apb_phy_readl ( phy , SOC_PCIEPHY_MMC1PLL_STAT0 ) ;
}
hi3670_apb_phy_updatel ( phy , 0 , PCIE_PHY_PCIEPL_BP ,
SOC_PCIEPHY_MMC1PLL_CTRL20 ) ;
} else {
hi3670_apb_phy_updatel ( phy ,
PCIE_PHY_MMC1PLL_DISABLE ,
PCIE_PHY_MMC1PLL_DISABLE ,
SOC_PCIEPHY_MMC1PLL_CTRL16 ) ;
hi3670_apb_phy_updatel ( phy , PCIE_PHY_PCIEPL_BP ,
PCIE_PHY_PCIEPL_BP ,
SOC_PCIEPHY_MMC1PLL_CTRL20 ) ;
}
return 0 ;
}
static void hi3670_pcie_hp_debounce_gt ( struct hi3670_pcie_phy * phy , bool open )
{
if ( open )
/* gt_clk_pcie_hp/gt_clk_pcie_debounce open */
regmap_write ( phy - > crgctrl , CRGPERIPH_PEREN12 ,
IO_HP_DEBOUNCE_GT ) ;
else
/* gt_clk_pcie_hp/gt_clk_pcie_debounce close */
regmap_write ( phy - > crgctrl , CRGPERIPH_PERDIS12 ,
IO_HP_DEBOUNCE_GT ) ;
}
static void hi3670_pcie_phyref_gt ( struct hi3670_pcie_phy * phy , bool open )
{
unsigned int val ;
regmap_read ( phy - > crgctrl , CRGPERIPH_PCIECTRL0 , & val ) ;
if ( open )
val & = ~ IO_OE_HARD_GT_MODE ; /* enable hard gt mode */
else
val | = IO_OE_HARD_GT_MODE ; /* disable hard gt mode */
regmap_write ( phy - > crgctrl , CRGPERIPH_PCIECTRL0 , val ) ;
/* disable soft gt mode */
regmap_write ( phy - > crgctrl , CRGPERIPH_PERDIS12 , IO_PHYREF_SOFT_GT_MODE ) ;
}
static void hi3670_pcie_oe_ctrl ( struct hi3670_pcie_phy * phy , bool en_flag )
{
unsigned int val ;
regmap_read ( phy - > crgctrl , CRGPERIPH_PCIECTRL0 , & val ) ;
/* set ie cfg */
val | = IO_IE_EN_HARD_BYPASS ;
/* set oe cfg */
val & = ~ IO_HARD_CTRL_DEBOUNCE_BYPASS ;
/* set phy_debounce in&out time */
val | = ( DEBOUNCE_WAITCFG_IN | DEBOUNCE_WAITCFG_OUT ) ;
/* select oe_gt_mode */
val | = IO_OE_GT_MODE ;
if ( en_flag )
val & = ~ IO_OE_EN_HARD_BYPASS ;
else
val | = IO_OE_EN_HARD_BYPASS ;
regmap_write ( phy - > crgctrl , CRGPERIPH_PCIECTRL0 , val ) ;
}
static void hi3670_pcie_ioref_gt ( struct hi3670_pcie_phy * phy , bool open )
{
unsigned int val ;
if ( open ) {
regmap_write ( phy - > apb , SOC_PCIECTRL_CTRL21_ADDR ,
SOC_PCIECTRL_CTRL21_DEFAULT ) ;
hi3670_pcie_oe_ctrl ( phy , true ) ;
/* en hard gt mode */
regmap_read ( phy - > crgctrl , CRGPERIPH_PCIECTRL0 , & val ) ;
val & = ~ IO_REF_HARD_GT_MODE ;
regmap_write ( phy - > crgctrl , CRGPERIPH_PCIECTRL0 , val ) ;
/* disable soft gt mode */
regmap_write ( phy - > crgctrl , CRGPERIPH_PERDIS12 ,
IO_REF_SOFT_GT_MODE ) ;
} else {
/* disable hard gt mode */
regmap_read ( phy - > crgctrl , CRGPERIPH_PCIECTRL0 , & val ) ;
val | = IO_REF_HARD_GT_MODE ;
regmap_write ( phy - > crgctrl , CRGPERIPH_PCIECTRL0 , val ) ;
/* disable soft gt mode */
regmap_write ( phy - > crgctrl , CRGPERIPH_PERDIS12 ,
IO_REF_SOFT_GT_MODE ) ;
hi3670_pcie_oe_ctrl ( phy , false ) ;
}
}
static int hi3670_pcie_allclk_ctrl ( struct hi3670_pcie_phy * phy , bool clk_on )
{
struct device * dev = phy - > dev ;
int ret = 0 ;
if ( ! clk_on )
goto close_clocks ;
/* choose 100MHz clk src: Bit[8]==1 pad, Bit[8]==0 pll */
hi3670_apb_phy_updatel ( phy , 0 , PCIE_CLK_SOURCE ,
SOC_PCIEPHY_CTRL1_ADDR ) ;
hi3670_pcie_pll_init ( phy ) ;
ret = hi3670_pcie_pll_ctrl ( phy , true ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable pll \n " ) ;
return - EINVAL ;
}
hi3670_pcie_hp_debounce_gt ( phy , true ) ;
hi3670_pcie_phyref_gt ( phy , true ) ;
hi3670_pcie_ioref_gt ( phy , true ) ;
ret = clk_set_rate ( phy - > aclk , AXI_CLK_FREQ ) ;
if ( ret ) {
dev_err ( dev , " Failed to set rate \n " ) ;
goto close_clocks ;
}
return 0 ;
close_clocks :
hi3670_pcie_ioref_gt ( phy , false ) ;
hi3670_pcie_phyref_gt ( phy , false ) ;
hi3670_pcie_hp_debounce_gt ( phy , false ) ;
hi3670_pcie_pll_ctrl ( phy , false ) ;
return ret ;
}
static bool is_pipe_clk_stable ( struct hi3670_pcie_phy * phy )
{
struct device * dev = phy - > dev ;
u32 val ;
u32 time = PIPE_CLK_STABLE_TIME ;
u32 pipe_clk_stable = PCIE_IS_CLOCK_STABLE ;
val = hi3670_apb_phy_readl ( phy , SOC_PCIEPHY_STATE0_ADDR ) ;
while ( val & pipe_clk_stable ) {
mdelay ( 1 ) ;
if ( ! time ) {
dev_err ( dev , " PIPE clk is not stable \n " ) ;
return false ;
}
time - - ;
val = hi3670_apb_phy_readl ( phy , SOC_PCIEPHY_STATE0_ADDR ) ;
}
return true ;
}
static int hi3670_pcie_noc_power ( struct hi3670_pcie_phy * phy , bool enable )
{
struct device * dev = phy - > dev ;
u32 time = NOC_POWER_TIME ;
unsigned int val = NOC_PW_MASK ;
int rst ;
if ( enable )
val = NOC_PW_MASK | NOC_PW_SET_BIT ;
else
val = NOC_PW_MASK ;
rst = enable ? 1 : 0 ;
regmap_write ( phy - > pmctrl , NOC_POWER_IDLEREQ_1 , val ) ;
time = NOC_POWER_TIME ;
regmap_read ( phy - > pmctrl , NOC_POWER_IDLE_1 , & val ) ;
while ( ( val & NOC_PW_SET_BIT ) ! = rst ) {
udelay ( 10 ) ;
if ( ! time ) {
dev_err ( dev , " Failed to reverse noc power-status \n " ) ;
return - EINVAL ;
}
time - - ;
regmap_read ( phy - > pmctrl , NOC_POWER_IDLE_1 , & val ) ;
}
return 0 ;
}
static int hi3670_pcie_get_resources_from_pcie ( struct hi3670_pcie_phy * phy )
{
struct device_node * pcie_port ;
struct device * dev = phy - > dev ;
struct device * pcie_dev ;
pcie_port = of_get_child_by_name ( dev - > parent - > of_node , " pcie " ) ;
if ( ! pcie_port ) {
dev_err ( dev , " no pcie node found in %s \n " ,
dev - > parent - > of_node - > full_name ) ;
return - ENODEV ;
}
pcie_dev = bus_find_device_by_of_node ( & platform_bus_type , pcie_port ) ;
if ( ! pcie_dev ) {
dev_err ( dev , " Didn't find pcie device \n " ) ;
return - ENODEV ;
}
/*
* We might just use NULL instead of the APB name , as the
* pcie - kirin currently registers directly just one regmap ( although
* the DWC driver register other regmaps ) .
*
* Yet , it sounds safer to warrant that it will be accessing the
* right regmap . So , let ' s use the named version .
*/
phy - > apb = dev_get_regmap ( pcie_dev , " kirin_pcie_apb " ) ;
if ( ! phy - > apb ) {
dev_err ( dev , " Failed to get APB regmap \n " ) ;
return - ENODEV ;
}
return 0 ;
}
static int kirin_pcie_clk_ctrl ( struct hi3670_pcie_phy * phy , bool enable )
{
int ret = 0 ;
if ( ! enable )
goto close_clk ;
ret = clk_set_rate ( phy - > phy_ref_clk , REF_CLK_FREQ ) ;
if ( ret )
return ret ;
ret = clk_prepare_enable ( phy - > phy_ref_clk ) ;
if ( ret )
return ret ;
ret = clk_prepare_enable ( phy - > apb_sys_clk ) ;
if ( ret )
goto apb_sys_fail ;
ret = clk_prepare_enable ( phy - > apb_phy_clk ) ;
if ( ret )
goto apb_phy_fail ;
ret = clk_prepare_enable ( phy - > aclk ) ;
if ( ret )
goto aclk_fail ;
ret = clk_prepare_enable ( phy - > aux_clk ) ;
if ( ret )
goto aux_clk_fail ;
return 0 ;
close_clk :
clk_disable_unprepare ( phy - > aux_clk ) ;
aux_clk_fail :
clk_disable_unprepare ( phy - > aclk ) ;
aclk_fail :
clk_disable_unprepare ( phy - > apb_phy_clk ) ;
apb_phy_fail :
clk_disable_unprepare ( phy - > apb_sys_clk ) ;
apb_sys_fail :
clk_disable_unprepare ( phy - > phy_ref_clk ) ;
return ret ;
}
static int hi3670_pcie_phy_init ( struct phy * generic_phy )
{
struct hi3670_pcie_phy * phy = phy_get_drvdata ( generic_phy ) ;
int ret ;
/*
* The code under hi3670_pcie_get_resources_from_pcie ( ) need to
* access the reset - gpios and the APB registers , both from the
* pcie - kirin driver .
*
* The APB is obtained via the pcie driver ' s regmap
* Such kind of resource can only be obtained during the PCIe
* power_on sequence , as the code inside pcie - kirin needs to
* be already probed , as it needs to register the APB regmap .
*/
ret = hi3670_pcie_get_resources_from_pcie ( phy ) ;
if ( ret )
return ret ;
return 0 ;
}
static int hi3670_pcie_phy_power_on ( struct phy * generic_phy )
{
struct hi3670_pcie_phy * phy = phy_get_drvdata ( generic_phy ) ;
int val , ret ;
/* Power supply for Host */
regmap_write ( phy - > sysctrl , SCTRL_PCIE_CMOS_OFFSET , SCTRL_PCIE_CMOS_BIT ) ;
usleep_range ( TIME_CMOS_MIN , TIME_CMOS_MAX ) ;
hi3670_pcie_phy_oe_enable ( phy , true ) ;
ret = kirin_pcie_clk_ctrl ( phy , true ) ;
if ( ret )
return ret ;
/* ISO disable, PCIeCtrl, PHY assert and clk gate clear */
regmap_write ( phy - > sysctrl , SCTRL_PCIE_ISO_OFFSET , SCTRL_PCIE_ISO_BIT ) ;
regmap_write ( phy - > crgctrl , CRGCTRL_PCIE_ASSERT_OFFSET ,
CRGCTRL_PCIE_ASSERT_BIT ) ;
regmap_write ( phy - > sysctrl , SCTRL_PCIE_HPCLK_OFFSET ,
SCTRL_PCIE_HPCLK_BIT ) ;
hi3670_pcie_natural_cfg ( phy ) ;
ret = hi3670_pcie_allclk_ctrl ( phy , true ) ;
if ( ret )
goto disable_clks ;
/* pull down phy_test_powerdown signal */
hi3670_apb_phy_updatel ( phy , 0 , PCIE_PULL_DOWN_PHY_TEST_POWERDOWN ,
SOC_PCIEPHY_CTRL0_ADDR ) ;
/* deassert controller perst_n */
regmap_read ( phy - > apb , SOC_PCIECTRL_CTRL12_ADDR , & val ) ;
val | = PCIE_DEASSERT_CONTROLLER_PERST ;
regmap_write ( phy - > apb , SOC_PCIECTRL_CTRL12_ADDR , val ) ;
udelay ( 10 ) ;
ret = is_pipe_clk_stable ( phy ) ;
if ( ! ret )
goto disable_clks ;
hi3670_pcie_set_eyeparam ( phy ) ;
ret = hi3670_pcie_noc_power ( phy , false ) ;
if ( ret )
goto disable_clks ;
return 0 ;
disable_clks :
kirin_pcie_clk_ctrl ( phy , false ) ;
return ret ;
}
static int hi3670_pcie_phy_power_off ( struct phy * generic_phy )
{
struct hi3670_pcie_phy * phy = phy_get_drvdata ( generic_phy ) ;
hi3670_pcie_phy_oe_enable ( phy , false ) ;
hi3670_pcie_allclk_ctrl ( phy , false ) ;
/* Drop power supply for Host */
regmap_write ( phy - > sysctrl , SCTRL_PCIE_CMOS_OFFSET , 0 ) ;
/*
* FIXME : The enabled clocks should be disabled here by calling
* kirin_pcie_clk_ctrl ( phy , false ) ;
* However , some clocks used at Kirin 970 should be marked as
* CLK_IS_CRITICAL at clk - hi3670 driver , as powering such clocks off
* cause an Asynchronous SError interrupt , which produces panic ( ) .
* While clk - hi3670 is not fixed , we cannot risk disabling clocks here .
*/
return 0 ;
}
static const struct phy_ops hi3670_phy_ops = {
. init = hi3670_pcie_phy_init ,
. power_on = hi3670_pcie_phy_power_on ,
. power_off = hi3670_pcie_phy_power_off ,
. owner = THIS_MODULE ,
} ;
static int hi3670_pcie_phy_get_resources ( struct hi3670_pcie_phy * phy ,
struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
/* syscon */
phy - > crgctrl = syscon_regmap_lookup_by_compatible ( " hisilicon,hi3670-crgctrl " ) ;
if ( IS_ERR ( phy - > crgctrl ) )
return PTR_ERR ( phy - > crgctrl ) ;
phy - > sysctrl = syscon_regmap_lookup_by_compatible ( " hisilicon,hi3670-sctrl " ) ;
if ( IS_ERR ( phy - > sysctrl ) )
return PTR_ERR ( phy - > sysctrl ) ;
phy - > pmctrl = syscon_regmap_lookup_by_compatible ( " hisilicon,hi3670-pmctrl " ) ;
2021-11-17 10:48:43 +03:00
if ( IS_ERR ( phy - > pmctrl ) )
return PTR_ERR ( phy - > pmctrl ) ;
2021-09-28 09:34:11 +02:00
/* clocks */
phy - > phy_ref_clk = devm_clk_get ( dev , " phy_ref " ) ;
if ( IS_ERR ( phy - > phy_ref_clk ) )
return PTR_ERR ( phy - > phy_ref_clk ) ;
phy - > aux_clk = devm_clk_get ( dev , " aux " ) ;
if ( IS_ERR ( phy - > aux_clk ) )
return PTR_ERR ( phy - > aux_clk ) ;
phy - > apb_phy_clk = devm_clk_get ( dev , " apb_phy " ) ;
if ( IS_ERR ( phy - > apb_phy_clk ) )
return PTR_ERR ( phy - > apb_phy_clk ) ;
phy - > apb_sys_clk = devm_clk_get ( dev , " apb_sys " ) ;
if ( IS_ERR ( phy - > apb_sys_clk ) )
return PTR_ERR ( phy - > apb_sys_clk ) ;
phy - > aclk = devm_clk_get ( dev , " aclk " ) ;
if ( IS_ERR ( phy - > aclk ) )
return PTR_ERR ( phy - > aclk ) ;
/* registers */
phy - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( phy - > base ) )
return PTR_ERR ( phy - > base ) ;
hi3670_pcie_get_eyeparam ( phy ) ;
return 0 ;
}
static int hi3670_pcie_phy_probe ( struct platform_device * pdev )
{
struct phy_provider * phy_provider ;
struct device * dev = & pdev - > dev ;
struct hi3670_pcie_phy * phy ;
struct phy * generic_phy ;
int ret ;
phy = devm_kzalloc ( dev , sizeof ( * phy ) , GFP_KERNEL ) ;
if ( ! phy )
return - ENOMEM ;
phy - > dev = dev ;
ret = hi3670_pcie_phy_get_resources ( phy , pdev ) ;
if ( ret )
return ret ;
generic_phy = devm_phy_create ( dev , dev - > of_node , & hi3670_phy_ops ) ;
if ( IS_ERR ( generic_phy ) ) {
dev_err ( dev , " failed to create PHY \n " ) ;
return PTR_ERR ( generic_phy ) ;
}
phy_set_drvdata ( generic_phy , phy ) ;
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 hi3670_pcie_phy_match [ ] = {
{
. compatible = " hisilicon,hi970-pcie-phy " ,
} ,
{ } ,
} ;
static struct platform_driver hi3670_pcie_phy_driver = {
. probe = hi3670_pcie_phy_probe ,
. driver = {
. of_match_table = hi3670_pcie_phy_match ,
. name = " hi3670_pcie_phy " ,
. suppress_bind_attrs = true ,
}
} ;
builtin_platform_driver ( hi3670_pcie_phy_driver ) ;
MODULE_DEVICE_TABLE ( of , hi3670_pcie_phy_match ) ;
MODULE_DESCRIPTION ( " PCIe phy driver for Kirin 970 " ) ;
MODULE_AUTHOR ( " Mauro Carvalho Chehab <mchehab@kernel.org> " ) ;
MODULE_AUTHOR ( " Manivannan Sadhasivam <mani@kernel.org> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;