// 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 * Manivannan Sadhasivam * * Based on: * https://lore.kernel.org/lkml/4c9d6581478aa966698758c0420933f5defab4dd.1612335031.git.mchehab+huawei@kernel.org/ */ #include #include #include #include #include #include #include #include #include #include #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"); if (IS_ERR(phy->pmctrl)) return PTR_ERR(phy->pmctrl); /* 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 "); MODULE_AUTHOR("Manivannan Sadhasivam "); MODULE_LICENSE("GPL v2");